diff --git a/Common/Data/UniverseSelection/CoarseFundamentalUniverse.cs b/Common/Data/UniverseSelection/CoarseFundamentalUniverse.cs index ed67a03a2834..42b032d32c22 100644 --- a/Common/Data/UniverseSelection/CoarseFundamentalUniverse.cs +++ b/Common/Data/UniverseSelection/CoarseFundamentalUniverse.cs @@ -17,6 +17,7 @@ using System.Linq; using Python.Runtime; using System.Collections.Generic; +using QuantConnect.Util; namespace QuantConnect.Data.UniverseSelection { @@ -87,7 +88,7 @@ public CoarseFundamentalUniverse(Symbol symbol, UniverseSettings universeSetting /// The data that passes the filter public override IEnumerable SelectSymbols(DateTime utcTime, BaseDataCollection data) { - return _selector(data.Data.OfType()); + return _selector(new CastingEnumerable(data.Data)); } /// diff --git a/Common/Data/UniverseSelection/FineFundamentalUniverse.cs b/Common/Data/UniverseSelection/FineFundamentalUniverse.cs index 8231a672f65e..59e67b49621e 100644 --- a/Common/Data/UniverseSelection/FineFundamentalUniverse.cs +++ b/Common/Data/UniverseSelection/FineFundamentalUniverse.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Collections.Generic; using QuantConnect.Data.Fundamental; +using QuantConnect.Util; namespace QuantConnect.Data.UniverseSelection { @@ -60,7 +61,7 @@ public FineFundamentalUniverse(Symbol symbol, UniverseSettings universeSettings, /// The data that passes the filter public override IEnumerable SelectSymbols(DateTime utcTime, BaseDataCollection data) { - return _selector(data.Data.OfType()); + return _selector(new CastingEnumerable(data.Data)); } /// diff --git a/Common/Data/UniverseSelection/FuncUniverse.cs b/Common/Data/UniverseSelection/FuncUniverse.cs index 572f2db4eabf..e4c7c04ac723 100644 --- a/Common/Data/UniverseSelection/FuncUniverse.cs +++ b/Common/Data/UniverseSelection/FuncUniverse.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Linq; using Python.Runtime; +using QuantConnect.Util; namespace QuantConnect.Data.UniverseSelection { @@ -62,7 +63,7 @@ public FuncUniverse(SubscriptionDataConfig configuration, UniverseSettings unive /// The data that passes the filter public override IEnumerable SelectSymbols(DateTime utcTime, BaseDataCollection data) { - return _universeSelector(data.Data.Cast()); + return _universeSelector(new CastingEnumerable(data.Data)); } } diff --git a/Common/Data/UniverseSelection/FundamentalUniverseFactory.cs b/Common/Data/UniverseSelection/FundamentalUniverseFactory.cs index 8d5288c7c2a7..2608cd377195 100644 --- a/Common/Data/UniverseSelection/FundamentalUniverseFactory.cs +++ b/Common/Data/UniverseSelection/FundamentalUniverseFactory.cs @@ -18,6 +18,7 @@ using Python.Runtime; using System.Collections.Generic; using QuantConnect.Data.Fundamental; +using QuantConnect.Util; namespace QuantConnect.Data.UniverseSelection { @@ -84,7 +85,7 @@ public FundamentalUniverseFactory(Symbol symbol, UniverseSettings universeSettin /// The data that passes the filter public override IEnumerable SelectSymbols(DateTime utcTime, BaseDataCollection data) { - return _selector(data.Data.OfType()); + return _selector(new CastingEnumerable(data.Data)); } /// diff --git a/Common/Data/UniverseSelection/FuturesChainUniverse.cs b/Common/Data/UniverseSelection/FuturesChainUniverse.cs index 36d222b80a42..81e292c97878 100644 --- a/Common/Data/UniverseSelection/FuturesChainUniverse.cs +++ b/Common/Data/UniverseSelection/FuturesChainUniverse.cs @@ -19,6 +19,7 @@ using System.Linq; using QuantConnect.Securities; using QuantConnect.Securities.Future; +using QuantConnect.Util; namespace QuantConnect.Data.UniverseSelection { @@ -84,7 +85,7 @@ public override UniverseSettings UniverseSettings public override IEnumerable SelectSymbols(DateTime utcTime, BaseDataCollection data) { var localEndTime = utcTime.ConvertFromUtc(Future.Exchange.TimeZone); - var availableContracts = data.Data.Cast().ToList(); + var availableContracts = new CastingEnumerable(data.Data); var results = Future.ContractFilter.Filter(new FutureFilterUniverse(availableContracts, localEndTime)); return results.Select(x => x.Symbol); diff --git a/Common/Data/UniverseSelection/OptionChainUniverse.cs b/Common/Data/UniverseSelection/OptionChainUniverse.cs index 64640f77dd65..de2083b72b7a 100644 --- a/Common/Data/UniverseSelection/OptionChainUniverse.cs +++ b/Common/Data/UniverseSelection/OptionChainUniverse.cs @@ -20,6 +20,7 @@ using QuantConnect.Securities; using System.Collections.Generic; using QuantConnect.Securities.Option; +using QuantConnect.Util; namespace QuantConnect.Data.UniverseSelection { @@ -92,7 +93,7 @@ public override IEnumerable SelectSymbols(DateTime utcTime, BaseDataColl { var localEndTime = utcTime.ConvertFromUtc(Option.Exchange.TimeZone); // we will only update unique strikes when there is an exchange date change - _optionFilterUniverse.Refresh(data.Data.Cast().ToList(), data.Underlying, localEndTime); + _optionFilterUniverse.Refresh(new CastingEnumerable(data.Data), data.Underlying, localEndTime); var results = Option.ContractFilter.Filter(_optionFilterUniverse); diff --git a/Common/Securities/ContractSecurityFilterUniverse.cs b/Common/Securities/ContractSecurityFilterUniverse.cs index 7611150f0d7f..252884d1729b 100644 --- a/Common/Securities/ContractSecurityFilterUniverse.cs +++ b/Common/Securities/ContractSecurityFilterUniverse.cs @@ -32,7 +32,7 @@ public abstract class ContractSecurityFilterUniverse : IDerivativeSecu { private bool _alreadyAppliedTypeFilters; - private IEnumerable _data; + private IReadOnlyList _data; /// /// Defines listed contract types with Flags attribute @@ -74,7 +74,7 @@ protected enum ContractExpirationType : int /// /// Setting it will also set AllSymbols /// - internal IEnumerable Data + internal IReadOnlyList Data { get { @@ -118,7 +118,7 @@ protected ContractSecurityFilterUniverse() /// /// Constructs ContractSecurityFilterUniverse /// - protected ContractSecurityFilterUniverse(IEnumerable allData, DateTime localTime) + protected ContractSecurityFilterUniverse(IReadOnlyList allData, DateTime localTime) { Data = allData; LocalTime = localTime; @@ -188,7 +188,7 @@ internal T ApplyTypesFilter() /// /// All data for contracts in the Universe /// The local exchange current time - public virtual void Refresh(IEnumerable allData, DateTime localTime) + public virtual void Refresh(IReadOnlyList allData, DateTime localTime) { Data = allData; LocalTime = localTime; diff --git a/Common/Securities/Future/FutureFilterUniverse.cs b/Common/Securities/Future/FutureFilterUniverse.cs index 3af6b50747da..8a30010d1a6d 100644 --- a/Common/Securities/Future/FutureFilterUniverse.cs +++ b/Common/Securities/Future/FutureFilterUniverse.cs @@ -31,7 +31,7 @@ public class FutureFilterUniverse : ContractSecurityFilterUniverse /// Constructs FutureFilterUniverse /// - public FutureFilterUniverse(IEnumerable allData, DateTime localTime) + public FutureFilterUniverse(IReadOnlyList allData, DateTime localTime) : base(allData, localTime) { } diff --git a/Common/Securities/Option/OptionFilterUniverse.cs b/Common/Securities/Option/OptionFilterUniverse.cs index 7f193be0342c..5cb946a97286 100644 --- a/Common/Securities/Option/OptionFilterUniverse.cs +++ b/Common/Securities/Option/OptionFilterUniverse.cs @@ -71,7 +71,7 @@ public OptionFilterUniverse(Option.Option option) /// Constructs OptionFilterUniverse /// /// Used for testing only - public OptionFilterUniverse(Option.Option option, IEnumerable allData, BaseData underlying, decimal underlyingScaleFactor = 1) + public OptionFilterUniverse(Option.Option option, IReadOnlyList allData, BaseData underlying, decimal underlyingScaleFactor = 1) : base(allData, underlying.EndTime) { _option = option; @@ -86,7 +86,7 @@ public OptionFilterUniverse(Option.Option option, IEnumerable al /// All data for the option contracts /// The current underlying last data point /// The current local time - public void Refresh(IEnumerable allContractsData, BaseData underlying, DateTime localTime) + public void Refresh(IReadOnlyList allContractsData, BaseData underlying, DateTime localTime) { base.Refresh(allContractsData, localTime); @@ -1037,7 +1037,7 @@ private IEnumerable GetContractsForExpiry(IEnumerable symbols, i /// private OptionFilterUniverse Empty() { - Data = Enumerable.Empty(); + Data = Enumerable.Empty().ToList(); return this; } diff --git a/Common/Util/CastingEnumerable.cs b/Common/Util/CastingEnumerable.cs new file mode 100644 index 000000000000..167ea776ba0b --- /dev/null +++ b/Common/Util/CastingEnumerable.cs @@ -0,0 +1,81 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace QuantConnect.Util +{ + /// + /// Defines a list that casts the elements of a source list to a derived type. + /// This is useful to avoid materializing another list after using, for example, the LINQ method. + /// + /// The base type of the elements in the source enumerable. + /// The type to cast the elements to. + public class CastingEnumerable : IReadOnlyList + where TDerived : class, TBase + { + private IReadOnlyList _data; + + /// + /// Gets the count of items in the enumerable. + /// + public int Count => _data.Count; + + /// + /// Gets the element at the specified index. + /// + /// The zero-based index of the element to get. + /// The element at the specified index. + public TDerived this[int index] => (TDerived)_data[index]; + + /// + /// Initializes a new instance of the class + /// + public CastingEnumerable(IReadOnlyList data) + { + _data = data; + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// An enumerator that can be used to iterate through the collection. + /// + /// 1 + public IEnumerator GetEnumerator() + { + foreach (var item in _data) + { + yield return (TDerived)item; + } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An enumerator object that can be used to iterate through the collection. + /// + /// 2 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/Research/QuantBook.cs b/Research/QuantBook.cs index dafdea31339e..d7577cad7e7e 100644 --- a/Research/QuantBook.cs +++ b/Research/QuantBook.cs @@ -864,7 +864,7 @@ public PyDict GetPortfolioStatistics(PyObject dataFrame) /// /// Get's the universe data for the specified date /// - private IEnumerable GetChainHistory(Symbol canonicalSymbol, DateTime date, out BaseData underlyingData) + private IReadOnlyList GetChainHistory(Symbol canonicalSymbol, DateTime date, out BaseData underlyingData) where T : BaseChainUniverseData { // Use this GetEntry extension method since it's data type dependent, so we get the correct entry for the option universe @@ -878,17 +878,17 @@ private IEnumerable GetChainHistory(Symbol canonicalSymbol, DateTime date, if (universeData is not null) { underlyingData = universeData.Underlying; - return universeData.Data.Cast(); + return new CastingEnumerable(universeData.Data); } underlyingData = null; - return Enumerable.Empty(); + return Enumerable.Empty().ToList(); } /// /// Helper method to get option/future chain historical data for a given date range /// - private IEnumerable<(DateTime Date, IEnumerable ChainData, BaseData UnderlyingData)> GetChainHistory( + private IEnumerable<(DateTime Date, IReadOnlyList ChainData, BaseData UnderlyingData)> GetChainHistory( Security security, DateTime start, DateTime end, bool extendedMarketHours) where T : BaseChainUniverseData { diff --git a/Tests/Common/Securities/FutureFilterTests.cs b/Tests/Common/Securities/FutureFilterTests.cs index 5ea286e62867..e95868e9d116 100644 --- a/Tests/Common/Securities/FutureFilterTests.cs +++ b/Tests/Common/Securities/FutureFilterTests.cs @@ -53,7 +53,7 @@ public void FiltersExpiryRange() }; var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); - var filtered = filter.Filter(new FutureFilterUniverse(data, time)).Select(x => x.Symbol).ToList(); + var filtered = filter.Filter(new FutureFilterUniverse(data.ToList(), time)).Select(x => x.Symbol).ToList(); Assert.AreEqual(5, filtered.Count); Assert.AreEqual(symbols[3], filtered[0]); Assert.AreEqual(symbols[4], filtered[1]); @@ -86,7 +86,7 @@ public void FutureContractFiltering(bool standardsOnly, int expectedCount) }; var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); - var filtered = filter.Filter(new FutureFilterUniverse(data, time)).Select(x => x.Symbol).ToList(); + var filtered = filter.Filter(new FutureFilterUniverse(data.ToList(), time)).Select(x => x.Symbol).ToList(); Assert.AreEqual(expectedCount, filtered.Count); @@ -126,7 +126,7 @@ public void WeeklysFilterDoesNotFilterStandardContractWithExpiryMonthPriorOrAfte }; var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); - var standardContracts = filter.Filter(new FutureFilterUniverse(data, new DateTime(2020, 1, 1))).Select(x => x.Symbol).ToList(); + var standardContracts = filter.Filter(new FutureFilterUniverse(data.ToList(), new DateTime(2020, 1, 1))).Select(x => x.Symbol).ToList(); Assert.AreEqual(6, standardContracts.Count); Assert.AreEqual(symbols[0], standardContracts[0]); Assert.AreEqual(symbols[1], standardContracts[1]); @@ -159,7 +159,7 @@ public void FilterAllowBothTypes() }; var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); - var filtered = filter.Filter(new FutureFilterUniverse(data, time)).Select(x => x.Symbol).ToList(); + var filtered = filter.Filter(new FutureFilterUniverse(data.ToList(), time)).Select(x => x.Symbol).ToList(); Assert.AreEqual(6, filtered.Count); Assert.AreEqual(symbols, filtered); } @@ -187,7 +187,7 @@ public void FilterOutStandards() }; var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); - var filtered = filter.Filter(new FutureFilterUniverse(data, time)).Select(x => x.Symbol).ToList(); + var filtered = filter.Filter(new FutureFilterUniverse(data.ToList(), time)).Select(x => x.Symbol).ToList(); Assert.AreEqual(4, filtered.Count); Assert.AreEqual(symbols[1], filtered[0]); Assert.AreEqual(symbols[2], filtered[1]); @@ -224,7 +224,7 @@ public void FiltersFrontMonth() }; var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); - var filtered = filter.Filter(new FutureFilterUniverse(data, new DateTime(2016, 02, 26))).ToList(); + var filtered = filter.Filter(new FutureFilterUniverse(data.ToList(), new DateTime(2016, 02, 26))).ToList(); Assert.AreEqual(4, filtered.Count); } @@ -257,7 +257,7 @@ public void FiltersBackMonth() }; var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); - var filtered = filter.Filter(new FutureFilterUniverse(data, new DateTime(2016, 02, 26))).ToList(); + var filtered = filter.Filter(new FutureFilterUniverse(data.ToList(), new DateTime(2016, 02, 26))).ToList(); Assert.AreEqual(3, filtered.Count); } @@ -290,7 +290,7 @@ public void FiltersExpirationCycles() }; var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); - var filtered = filter.Filter(new FutureFilterUniverse(data, new DateTime(2016, 02, 26))).ToList(); + var filtered = filter.Filter(new FutureFilterUniverse(data.ToList(), new DateTime(2016, 02, 26))).ToList(); Assert.AreEqual(5, filtered.Count); } @@ -314,7 +314,7 @@ public void FilterTypeDoesNotBreakOnMissingExpiryFunction() var data = symbols.Select(x => new FutureUniverse() { Symbol = x }); // Since this is a unidentifiable symbol for our expiry functions it will return true and be passed through - var filtered = filter.Filter(new FutureFilterUniverse(data, time)).Select(x => x.Symbol).ToList(); + var filtered = filter.Filter(new FutureFilterUniverse(data.ToList(), time)).Select(x => x.Symbol).ToList(); Assert.AreEqual(1, filtered.Count); Assert.AreEqual(symbols[0], filtered[0]); } diff --git a/Tests/Common/Securities/OptionFilterTests.cs b/Tests/Common/Securities/OptionFilterTests.cs index a8e2eff009bc..8e89f0b55fcd 100644 --- a/Tests/Common/Securities/OptionFilterTests.cs +++ b/Tests/Common/Securities/OptionFilterTests.cs @@ -52,7 +52,7 @@ public void FiltersStrikeRange(decimal underlyingPrice, Symbol[] symbols, int fi var canonical = symbols[0].Canonical; var option = CreateOptionSecurity(canonical); - var filterUniverse = new OptionFilterUniverse(option, data, underlying, underlyingScaleFactor); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying, underlyingScaleFactor); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(filteredNumber, filtered.Count); Assert.AreEqual(symbols[3], filtered[0].Symbol); @@ -100,7 +100,7 @@ public void FiltersStrikeRangeWithVaryingDistance(decimal underlyingPrice) var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(underlyingPrice == 8 ? 5 : 4, filtered.Count); Assert.AreEqual(symbols[1], filtered[0].Symbol); @@ -147,7 +147,7 @@ public void FiltersStrikeRangeWithNegativeMaxStrike(decimal underlyingPrice) var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(3, filtered.Count); Assert.AreEqual(symbols[5], filtered[0].Symbol); @@ -181,7 +181,7 @@ public void FiltersStrikeRangeWithNegativeMaxStrikeOutOfRange(decimal underlying var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(0, filtered.Count); } @@ -220,7 +220,7 @@ public void FiltersStrikeRangeWithPositiveMinStrike(decimal underlyingPrice) var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(3, filtered.Count); Assert.AreEqual(symbols[2], filtered[0].Symbol); @@ -254,7 +254,7 @@ public void FiltersStrikeRangeWithPositiveMinStrikeOutOfRange(decimal underlying var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(0, filtered.Count); } @@ -279,7 +279,7 @@ public void FiltersStrikeRangeWhenEmpty() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(0, filtered.Count); } @@ -316,7 +316,7 @@ public void FiltersExpiryRange() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(5, filtered.Count); Assert.AreEqual(symbols[3], filtered[0].Symbol); @@ -347,7 +347,7 @@ public void FiltersExpiryRangeAfterNonTradableDay() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); // Expiry range is 0 to 5 days, so 6 days times 3 strikes per day @@ -397,7 +397,7 @@ public void FiltersOutWeeklys() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filtered = filter.Filter(new OptionFilterUniverse(option, data, underlying)).ToList(); + var filtered = filter.Filter(new OptionFilterUniverse(option, data.ToList(), underlying)).ToList(); Assert.AreEqual(3, filtered.Count); Assert.AreEqual(symbols[5], filtered[0].Symbol); Assert.AreEqual(symbols[6], filtered[1].Symbol); @@ -442,7 +442,7 @@ public void FiltersOutWeeklysIfFridayHoliday() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filtered = filter.Filter(new OptionFilterUniverse(option, data, underlying)).ToList(); + var filtered = filter.Filter(new OptionFilterUniverse(option, data.ToList(), underlying)).ToList(); Assert.AreEqual(3, filtered.Count); Assert.AreEqual(symbols[5], filtered[0].Symbol); Assert.AreEqual(symbols[6], filtered[1].Symbol); @@ -483,7 +483,7 @@ public void FiltersOutStandardContracts() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filtered = filter.Filter(new OptionFilterUniverse(option, data, underlying)).ToList(); + var filtered = filter.Filter(new OptionFilterUniverse(option, data.ToList(), underlying)).ToList(); Assert.AreEqual(8, filtered.Count); } @@ -521,7 +521,7 @@ public void FiltersOutNothingAfterFilteringByType() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(10, filtered.Count); } @@ -560,7 +560,7 @@ public void FiltersFrontMonth() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filtered = filter.Filter(new OptionFilterUniverse(option, data, underlying)).ToList(); + var filtered = filter.Filter(new OptionFilterUniverse(option, data.ToList(), underlying)).ToList(); Assert.AreEqual(4, filtered.Count); } @@ -598,7 +598,7 @@ public void FiltersBackMonth() var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(3, filtered.Count); } @@ -653,7 +653,7 @@ def set_filter(universe: OptionFilterUniverse) -> OptionFilterUniverse: var option = CreateOptionSecurity(canonical); var data = symbols.Select(x => new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(option, data, underlying); + var filterUniverse = new OptionFilterUniverse(option, data.ToList(), underlying); var filtered = filter.Filter(filterUniverse).ToList(); Assert.AreEqual(5, filtered.Count); } diff --git a/Tests/Common/Securities/OptionStrategyFilterTests.cs b/Tests/Common/Securities/OptionStrategyFilterTests.cs index 98f82c6d2fce..446eec6d5164 100644 --- a/Tests/Common/Securities/OptionStrategyFilterTests.cs +++ b/Tests/Common/Securities/OptionStrategyFilterTests.cs @@ -962,7 +962,7 @@ private List BaseFiltering(decimal underlyingPrice, Func new OptionUniverse() { Symbol = x }); - var filterUniverse = new OptionFilterUniverse(null, data, underlying); + var filterUniverse = new OptionFilterUniverse(null, data.ToList(), underlying); filterUniverse.Refresh(filterUniverse.Data, underlying, underlying.EndTime); if (fails) diff --git a/Tests/Common/Util/CastingEnumerableTests.cs b/Tests/Common/Util/CastingEnumerableTests.cs new file mode 100644 index 000000000000..629f3b0c6b76 --- /dev/null +++ b/Tests/Common/Util/CastingEnumerableTests.cs @@ -0,0 +1,65 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +using System.Collections.Generic; +using NUnit.Framework; +using QuantConnect.Util; + +namespace QuantConnect.Tests.Common.Util +{ + [TestFixture] + public class CastingEnumerableTests + { + private class BaseClass { } + private class DerivedClass : BaseClass { } + + [Test] + public void CastsOnEnumeration() + { + var list = new List { new DerivedClass(), new DerivedClass(), new DerivedClass() }; + var casting = new CastingEnumerable(list); + + Assert.DoesNotThrow(() => + { + foreach (var item in casting) { } + }); + + CollectionAssert.AreEqual(list, casting); + Assert.AreEqual(list.Count, casting.Count); + } + + [Test] + public void CastsOnIndexing() + { + var list = new List { new DerivedClass(), new DerivedClass(), new DerivedClass() }; + var casting = new CastingEnumerable(list); + + DerivedClass casted0 = null; + DerivedClass casted1 = null; + DerivedClass casted2 = null; + + Assert.DoesNotThrow(() => + { + casted0 = casting[0]; + casted1 = casting[1]; + casted2 = casting[2]; + }); + + Assert.AreEqual(list[0], casted0); + Assert.AreEqual(list[1], casted1); + Assert.AreEqual(list[2], casted2); + } + } +} diff --git a/Tests/Common/Util/MemoizingEnumerableTests.cs b/Tests/Common/Util/MemoizingEnumerableTests.cs index 4ba3946b9e34..c8d568eb9f15 100644 --- a/Tests/Common/Util/MemoizingEnumerableTests.cs +++ b/Tests/Common/Util/MemoizingEnumerableTests.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework;