diff --git a/Algorithm.CSharp/AddFutureContractWithContinuousRegressionAlgorithm.cs b/Algorithm.CSharp/AddFutureContractWithContinuousRegressionAlgorithm.cs index ed794a054c72..7d0296464e23 100644 --- a/Algorithm.CSharp/AddFutureContractWithContinuousRegressionAlgorithm.cs +++ b/Algorithm.CSharp/AddFutureContractWithContinuousRegressionAlgorithm.cs @@ -115,7 +115,7 @@ public override void OnSecuritiesChanged(SecurityChanges changes) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 73; + public long DataPoints => 76; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/AddFutureOptionContractDataStreamingRegressionAlgorithm.cs b/Algorithm.CSharp/AddFutureOptionContractDataStreamingRegressionAlgorithm.cs index 7ca259332550..f2f506569166 100644 --- a/Algorithm.CSharp/AddFutureOptionContractDataStreamingRegressionAlgorithm.cs +++ b/Algorithm.CSharp/AddFutureOptionContractDataStreamingRegressionAlgorithm.cs @@ -164,7 +164,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 311879; + public long DataPoints => 311881; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/AddFutureOptionContractFromFutureChainRegressionAlgorithm.cs b/Algorithm.CSharp/AddFutureOptionContractFromFutureChainRegressionAlgorithm.cs index cc31259c1d97..fa91f4b20606 100644 --- a/Algorithm.CSharp/AddFutureOptionContractFromFutureChainRegressionAlgorithm.cs +++ b/Algorithm.CSharp/AddFutureOptionContractFromFutureChainRegressionAlgorithm.cs @@ -93,7 +93,7 @@ public override void OnData(Slice slice) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 12169; + public long DataPoints => 12172; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm.cs b/Algorithm.CSharp/AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm.cs index 6f4c24e89106..481956791110 100644 --- a/Algorithm.CSharp/AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm.cs +++ b/Algorithm.CSharp/AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm.cs @@ -220,7 +220,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 608377; + public long DataPoints => 608380; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/BasicTemplateContinuousFutureAlgorithm.cs b/Algorithm.CSharp/BasicTemplateContinuousFutureAlgorithm.cs index 79c8a952e88a..270acee85cad 100644 --- a/Algorithm.CSharp/BasicTemplateContinuousFutureAlgorithm.cs +++ b/Algorithm.CSharp/BasicTemplateContinuousFutureAlgorithm.cs @@ -118,7 +118,7 @@ public override void OnSecuritiesChanged(SecurityChanges changes) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 713369; + public long DataPoints => 713371; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/BasicTemplateContinuousFutureWithExtendedMarketAlgorithm.cs b/Algorithm.CSharp/BasicTemplateContinuousFutureWithExtendedMarketAlgorithm.cs index 7370b942bb02..faff9f82d4c3 100644 --- a/Algorithm.CSharp/BasicTemplateContinuousFutureWithExtendedMarketAlgorithm.cs +++ b/Algorithm.CSharp/BasicTemplateContinuousFutureWithExtendedMarketAlgorithm.cs @@ -123,7 +123,7 @@ public override void OnSecuritiesChanged(SecurityChanges changes) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 2217299; + public long DataPoints => 2217326; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/BasicTemplateFutureRolloverAlgorithm.cs b/Algorithm.CSharp/BasicTemplateFutureRolloverAlgorithm.cs index d303b9aa8c91..68eaa5737f63 100644 --- a/Algorithm.CSharp/BasicTemplateFutureRolloverAlgorithm.cs +++ b/Algorithm.CSharp/BasicTemplateFutureRolloverAlgorithm.cs @@ -176,7 +176,7 @@ public void Dispose() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 1190; + public long DataPoints => 1199; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/BasicTemplateFuturesDailyAlgorithm.cs b/Algorithm.CSharp/BasicTemplateFuturesDailyAlgorithm.cs index 7845df1c8d86..de73bcceb0dd 100644 --- a/Algorithm.CSharp/BasicTemplateFuturesDailyAlgorithm.cs +++ b/Algorithm.CSharp/BasicTemplateFuturesDailyAlgorithm.cs @@ -117,7 +117,7 @@ select futuresContract /// /// Data Points count of all timeslices of algorithm /// - public virtual long DataPoints => 12452; + public virtual long DataPoints => 12453; /// /// Data Points count of the algorithm history @@ -134,33 +134,33 @@ select futuresContract /// public virtual Dictionary ExpectedStatistics => new Dictionary { - {"Total Orders", "32"}, + {"Total Orders", "34"}, {"Average Win", "0.33%"}, {"Average Loss", "-0.04%"}, - {"Compounding Annual Return", "0.110%"}, + {"Compounding Annual Return", "0.106%"}, {"Drawdown", "0.300%"}, - {"Expectancy", "0.184"}, + {"Expectancy", "0.178"}, {"Start Equity", "1000000"}, - {"End Equity", "1001108"}, - {"Net Profit", "0.111%"}, - {"Sharpe Ratio", "-1.688"}, - {"Sortino Ratio", "-0.772"}, - {"Probabilistic Sharpe Ratio", "14.944%"}, + {"End Equity", "1001066.2"}, + {"Net Profit", "0.107%"}, + {"Sharpe Ratio", "-1.695"}, + {"Sortino Ratio", "-0.804"}, + {"Probabilistic Sharpe Ratio", "14.797%"}, {"Loss Rate", "88%"}, {"Win Rate", "12%"}, - {"Profit-Loss Ratio", "8.47"}, + {"Profit-Loss Ratio", "9.01"}, {"Alpha", "-0.007"}, {"Beta", "0.002"}, {"Annual Standard Deviation", "0.004"}, {"Annual Variance", "0"}, {"Information Ratio", "-1.353"}, {"Tracking Error", "0.089"}, - {"Treynor Ratio", "-4.099"}, - {"Total Fees", "$72.00"}, + {"Treynor Ratio", "-4.112"}, + {"Total Fees", "$76.30"}, {"Estimated Strategy Capacity", "$0"}, {"Lowest Capacity Asset", "ES VRJST036ZY0X"}, - {"Portfolio Turnover", "0.87%"}, - {"OrderListHash", "168731c8f3a19f230cc1410818b3b573"} + {"Portfolio Turnover", "0.92%"}, + {"OrderListHash", "7afa589d648c3f24253cd59156a2014e"} }; } } diff --git a/Algorithm.CSharp/BasicTemplateFuturesFrameworkWithExtendedMarketAlgorithm.cs b/Algorithm.CSharp/BasicTemplateFuturesFrameworkWithExtendedMarketAlgorithm.cs index 80db9524d603..560934813d2f 100644 --- a/Algorithm.CSharp/BasicTemplateFuturesFrameworkWithExtendedMarketAlgorithm.cs +++ b/Algorithm.CSharp/BasicTemplateFuturesFrameworkWithExtendedMarketAlgorithm.cs @@ -41,7 +41,7 @@ public class BasicTemplateFuturesFrameworkWithExtendedMarketAlgorithm : BasicTem /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 163415; + public override long DataPoints => 163416; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm diff --git a/Algorithm.CSharp/BasicTemplateFuturesHourlyAlgorithm.cs b/Algorithm.CSharp/BasicTemplateFuturesHourlyAlgorithm.cs index dcc0d00e2065..349d51956488 100644 --- a/Algorithm.CSharp/BasicTemplateFuturesHourlyAlgorithm.cs +++ b/Algorithm.CSharp/BasicTemplateFuturesHourlyAlgorithm.cs @@ -41,7 +41,7 @@ public class BasicTemplateFuturesHourlyAlgorithm : BasicTemplateFuturesDailyAlgo /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 87289; + public override long DataPoints => 87290; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm diff --git a/Algorithm.CSharp/BasicTemplateFuturesWithExtendedMarketDailyAlgorithm.cs b/Algorithm.CSharp/BasicTemplateFuturesWithExtendedMarketDailyAlgorithm.cs index b42c25a2d9b0..345582e45693 100644 --- a/Algorithm.CSharp/BasicTemplateFuturesWithExtendedMarketDailyAlgorithm.cs +++ b/Algorithm.CSharp/BasicTemplateFuturesWithExtendedMarketDailyAlgorithm.cs @@ -43,7 +43,7 @@ public class BasicTemplateFuturesWithExtendedMarketDailyAlgorithm : BasicTemplat /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 14790; + public override long DataPoints => 14895; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm diff --git a/Algorithm.CSharp/BasicTemplateFuturesWithExtendedMarketHourlyAlgorithm.cs b/Algorithm.CSharp/BasicTemplateFuturesWithExtendedMarketHourlyAlgorithm.cs index caee6edc792c..41a5a5957605 100644 --- a/Algorithm.CSharp/BasicTemplateFuturesWithExtendedMarketHourlyAlgorithm.cs +++ b/Algorithm.CSharp/BasicTemplateFuturesWithExtendedMarketHourlyAlgorithm.cs @@ -41,7 +41,7 @@ public class BasicTemplateFuturesWithExtendedMarketHourlyAlgorithm : BasicTempla /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 228834; + public override long DataPoints => 228934; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm diff --git a/Algorithm.CSharp/ConsolidateRegressionAlgorithm.cs b/Algorithm.CSharp/ConsolidateRegressionAlgorithm.cs index d75896d64d16..7346898866a5 100644 --- a/Algorithm.CSharp/ConsolidateRegressionAlgorithm.cs +++ b/Algorithm.CSharp/ConsolidateRegressionAlgorithm.cs @@ -189,7 +189,7 @@ public override void OnData(Slice slice) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 14227; + public long DataPoints => 14228; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/ContinuousBackMonthRawFutureRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousBackMonthRawFutureRegressionAlgorithm.cs index 43be4c45e944..92a547a7563f 100644 --- a/Algorithm.CSharp/ContinuousBackMonthRawFutureRegressionAlgorithm.cs +++ b/Algorithm.CSharp/ContinuousBackMonthRawFutureRegressionAlgorithm.cs @@ -139,7 +139,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 713369; + public long DataPoints => 713371; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/ContinuousFutureBackMonthRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureBackMonthRegressionAlgorithm.cs index be0f4585da57..77f6d858b5d1 100644 --- a/Algorithm.CSharp/ContinuousFutureBackMonthRegressionAlgorithm.cs +++ b/Algorithm.CSharp/ContinuousFutureBackMonthRegressionAlgorithm.cs @@ -155,7 +155,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 723496; + public long DataPoints => 723498; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/ContinuousFutureImmediateUniverseSelectionRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureImmediateUniverseSelectionRegressionAlgorithm.cs index c8ae875e3d8c..ebf044b4fff6 100644 --- a/Algorithm.CSharp/ContinuousFutureImmediateUniverseSelectionRegressionAlgorithm.cs +++ b/Algorithm.CSharp/ContinuousFutureImmediateUniverseSelectionRegressionAlgorithm.cs @@ -184,7 +184,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 596356; + public long DataPoints => 596358; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/ContinuousFutureLimitIfTouchedOrderRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureLimitIfTouchedOrderRegressionAlgorithm.cs index 913cc49a5738..f883aa001273 100644 --- a/Algorithm.CSharp/ContinuousFutureLimitIfTouchedOrderRegressionAlgorithm.cs +++ b/Algorithm.CSharp/ContinuousFutureLimitIfTouchedOrderRegressionAlgorithm.cs @@ -78,7 +78,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 19886; + public long DataPoints => 19888; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/ContinuousFutureRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRegressionAlgorithm.cs index fa6392fb97db..22afb222af9a 100644 --- a/Algorithm.CSharp/ContinuousFutureRegressionAlgorithm.cs +++ b/Algorithm.CSharp/ContinuousFutureRegressionAlgorithm.cs @@ -169,7 +169,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 713369; + public long DataPoints => 713371; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/ContinuousFutureRolloverBaseRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverBaseRegressionAlgorithm.cs new file mode 100644 index 000000000000..8975109da32c --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverBaseRegressionAlgorithm.cs @@ -0,0 +1,262 @@ +/* + * 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 QuantConnect.Data; +using QuantConnect.Securities; +using QuantConnect.Securities.Future; +using System; +using QuantConnect.Util; +using System.Linq; +using NodaTime; +using QuantConnect.Interfaces; +using System.Collections.Generic; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data, regardless of the + /// offset between the exchange time zone and the data time zone. + /// + public abstract class ContinuousFutureRolloverBaseRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition + { + const string Ticker = Futures.Indices.SP500EMini; + + private Future _continuousContract; + + private DateTime _rolloverTime; + + private MarketHoursDatabase.Entry _originalMhdbEntry; + + protected abstract Resolution Resolution { get; } + + protected abstract Offset ExchangeToDataTimeZoneOffset { get; } + + private DateTimeZone DataTimeZone => TimeZones.Utc; + + private DateTimeZone ExchangeTimeZone => DateTimeZone.ForOffset(ExchangeToDataTimeZoneOffset); + + private bool RolloverHappened => _rolloverTime != DateTime.MinValue; + + public override void Initialize() + { + SetStartDate(2013, 10, 8); + SetEndDate(2013, 12, 20); + + _originalMhdbEntry = MarketHoursDatabase.GetEntry(Market.CME, Ticker, SecurityType.Future); + var exchangeHours = new SecurityExchangeHours(ExchangeTimeZone, + _originalMhdbEntry.ExchangeHours.Holidays, + _originalMhdbEntry.ExchangeHours.MarketHours.ToDictionary(), + _originalMhdbEntry.ExchangeHours.EarlyCloses, + _originalMhdbEntry.ExchangeHours.LateOpens); + MarketHoursDatabase.SetEntry(Market.CME, Ticker, SecurityType.Future, exchangeHours, DataTimeZone); + + SetTimeZone(ExchangeTimeZone); + + _continuousContract = AddFuture(Ticker, + Resolution, + extendedMarketHours: true, + dataNormalizationMode: DataNormalizationMode.Raw, + dataMappingMode: DataMappingMode.OpenInterest, + contractDepthOffset: 0 + ); + + SetBenchmark(x => 0); + } + + public override void OnData(Slice slice) + { + try + { + var receivedRollover = false; + foreach (var (symbol, symbolChangedEvent) in slice.SymbolChangedEvents) + { + if (RolloverHappened) + { + throw new RegressionTestException($"[{Time}] -- Unexpected symbol changed event for {symbol}. Expected only one mapping."); + } + + receivedRollover = true; + _rolloverTime = symbolChangedEvent.EndTime; + + var oldSymbol = symbolChangedEvent.OldSymbol; + var newSymbol = symbolChangedEvent.NewSymbol; + Debug($"[{Time}] -- Rollover: {oldSymbol} -> {newSymbol}"); + + if (symbol != _continuousContract.Symbol) + { + throw new RegressionTestException($"[{Time}] -- Unexpected symbol changed event for {symbol}"); + } + + var expectedMappingDate = new DateTime(2013, 12, 18); + if (_rolloverTime != expectedMappingDate) + { + throw new RegressionTestException($"[{Time}] -- Unexpected date {_rolloverTime}. Expected {expectedMappingDate}"); + } + + var expectedMappingOldSymbol = "ES VMKLFZIH2MTD"; + var expectedMappingNewSymbol = "ES VP274HSU1AF5"; + if (symbolChangedEvent.OldSymbol != expectedMappingOldSymbol || symbolChangedEvent.NewSymbol != expectedMappingNewSymbol) + { + throw new RegressionTestException($"[{Time}] -- Unexpected mapping. " + + $"Expected {expectedMappingOldSymbol} -> {expectedMappingNewSymbol} " + + $"but was {symbolChangedEvent.OldSymbol} -> {symbolChangedEvent.NewSymbol}"); + } + } + + var mappedFuture = Securities[_continuousContract.Mapped]; + var mappedFuturePrice = mappedFuture.Price; + + var otherFuture = Securities.Values.SingleOrDefault(x => !x.Symbol.IsCanonical() && x.Symbol != _continuousContract.Mapped); + var otherFuturePrice = otherFuture?.Price; + + var continuousContractPrice = _continuousContract.Price; + + Debug($"[{Time}] Contracts prices:\n" + + $" -- Mapped future: {mappedFuture.Symbol} :: {mappedFuture.Price} :: {mappedFuture.GetLastData()}\n" + + $" -- Other future: {otherFuture?.Symbol} :: {otherFuture?.Price} :: {otherFuture?.GetLastData()}\n" + + $" -- Mapped future from continuous contract: {_continuousContract.Symbol} :: {_continuousContract.Mapped} :: " + + $"{_continuousContract.Price} :: {_continuousContract.GetLastData()}\n"); + + if (receivedRollover) + { + if (continuousContractPrice != otherFuturePrice) + { + var continuousContractLastData = _continuousContract.GetLastData(); + throw new RegressionTestException($"[{Time}] -- Prices do not match. " + + $"At the time of the rollover, expected continuous future price to be the same as " + + $"the previously mapped contract since no data for the new mapped contract has been received:\n" + + $" Continuous contract ({_continuousContract.Symbol}) price: " + + $"{continuousContractPrice} :: {continuousContractLastData.Symbol.Underlying} :: " + + $"{continuousContractLastData.Time} - {continuousContractLastData.EndTime} :: {continuousContractLastData}. \n" + + $" Mapped contract ({mappedFuture.Symbol}) price: {mappedFuturePrice} :: {mappedFuture.GetLastData()}. \n" + + $" Other contract ({otherFuture?.Symbol}) price: {otherFuturePrice} :: {otherFuture?.GetLastData()}\n"); + } + } + else if (mappedFuturePrice != 0 || !RolloverHappened) + { + if (continuousContractPrice != mappedFuturePrice) + { + var continuousContractLastData = _continuousContract.GetLastData(); + throw new RegressionTestException($"[{Time}] -- Prices do not match. " + + $"Expected continuous future price to be the same as the mapped contract:\n" + + $" Continuous contract ({_continuousContract.Symbol}) price: {continuousContractPrice} :: " + + $"{continuousContractLastData.Symbol.Underlying} :: {continuousContractLastData}. \n" + + $" Mapped contract ({mappedFuture.Symbol}) price: {mappedFuturePrice} :: {mappedFuture.GetLastData()}. \n" + + $" Other contract ({otherFuture?.Symbol}) price: {otherFuturePrice} :: {otherFuture?.GetLastData()}\n"); + } + } + // No data for the mapped future yet after rollover + else + { + if (otherFuture == null) + { + throw new RegressionTestException($"[{Time}] --" + + $" Mapped future price is 0 (no data has arrived) so the previous mapped contract is expected to be there"); + } + + var continuousContractLastData = _continuousContract.GetLastData(); + + if (continuousContractLastData.EndTime > _rolloverTime) + { + throw new RegressionTestException($"[{Time}] -- Expected continuous future contract last data to be from the previously " + + $"mapped contract until the new mapped contract gets data:\n" + + $" Rollover time: {_rolloverTime}\n" + + $" Continuous contract ({_continuousContract.Symbol}) last data: " + + $"{continuousContractLastData.Symbol.Underlying} :: " + + $"{continuousContractLastData.Time} - {continuousContractLastData.EndTime} :: {continuousContractLastData}."); + } + } + } + catch (Exception ex) + { + ResetMarketHoursDatabase(); + throw; + } + } + + public override void OnEndOfAlgorithm() + { + ResetMarketHoursDatabase(); + + if (!RolloverHappened) + { + throw new RegressionTestException($"[{Time}] -- Rollover did not happen."); + } + } + + private void ResetMarketHoursDatabase() + { + MarketHoursDatabase.SetEntry(Market.CME, Ticker, SecurityType.Future, _originalMhdbEntry.ExchangeHours, _originalMhdbEntry.DataTimeZone); + } + + /// + /// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm. + /// + public bool CanRunLocally { get; } = true; + + /// + /// This is used by the regression test system to indicate which languages this algorithm is written in. + /// + public List Languages { get; } = new() { Language.CSharp }; + + /// + /// Data Points count of all timeslices of algorithm + /// + public virtual long DataPoints => 0; + + /// + /// Data Points count of the algorithm history + /// + public int AlgorithmHistoryDataPoints => 0; + + /// + /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm + /// + public Dictionary ExpectedStatistics => new Dictionary + { + {"Total Orders", "0"}, + {"Average Win", "0%"}, + {"Average Loss", "0%"}, + {"Compounding Annual Return", "0%"}, + {"Drawdown", "0%"}, + {"Expectancy", "0"}, + {"Start Equity", "100000"}, + {"End Equity", "100000"}, + {"Net Profit", "0%"}, + {"Sharpe Ratio", "0"}, + {"Sortino Ratio", "0"}, + {"Probabilistic Sharpe Ratio", "0%"}, + {"Loss Rate", "0%"}, + {"Win Rate", "0%"}, + {"Profit-Loss Ratio", "0"}, + {"Alpha", "0"}, + {"Beta", "0"}, + {"Annual Standard Deviation", "0"}, + {"Annual Variance", "0"}, + {"Information Ratio", "0"}, + {"Tracking Error", "0"}, + {"Treynor Ratio", "0"}, + {"Total Fees", "$0.00"}, + {"Estimated Strategy Capacity", "$0"}, + {"Lowest Capacity Asset", ""}, + {"Portfolio Turnover", "0%"}, + {"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"} + }; + + public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..a7441c6581a7 --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the exchange time zone is ahead of the data time zone. + /// + public class ContinuousFutureRolloverDailyExchangeTimeZoneAheadOfDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Daily; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(2); + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 1022; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..40825b180c7f --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the exchange time zone is behind of the data time zone. + /// + public class ContinuousFutureRolloverDailyExchangeTimeZoneBehindOfDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Daily; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(-2); + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 1017; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneSameAsDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneSameAsDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..2f3e039f781a --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverDailyExchangeTimeZoneSameAsDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the data time zone is the same as the exchange time zone. + /// + public class ContinuousFutureRolloverDailyExchangeTimeZoneSameAsDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Daily; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.Zero; + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 1015; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..ae991fda65d9 --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the exchange time zone is ahead of the data time zone. + /// + public class ContinuousFutureRolloverHourExchangeTimeZoneAheadOfDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Hour; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(2); + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 18850; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..4ca71fcd85d0 --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the exchange time zone is behind of the data time zone. + /// + public class ContinuousFutureRolloverHourExchangeTimeZoneBehindOfDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Hour; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(-2); + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 18844; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneSameAsDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneSameAsDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..57d85882b0f4 --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverHourExchangeTimeZoneSameAsDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the data time zone is the same as the exchange time zone. + /// + public class ContinuousFutureRolloverHourExchangeTimeZoneSameAsDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Hour; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.Zero; + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 18846; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..3e77be5e229d --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneAheadOfDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the exchange time zone is ahead of the data time zone. + /// + public class ContinuousFutureRolloverMinuteExchangeTimeZoneAheadOfDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Minute; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(2); + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 1127376; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..93b3130d8734 --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneBehindOfDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the exchange time zone is behind of the data time zone. + /// + public class ContinuousFutureRolloverMinuteExchangeTimeZoneBehindOfDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Minute; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(-2); + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 1127374; + } +} diff --git a/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneSameAsDataRegressionAlgorithm.cs b/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneSameAsDataRegressionAlgorithm.cs new file mode 100644 index 000000000000..be11903cfd31 --- /dev/null +++ b/Algorithm.CSharp/ContinuousFutureRolloverMinuteExchangeTimeZoneSameAsDataRegressionAlgorithm.cs @@ -0,0 +1,38 @@ +/* + * 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 NodaTime; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Base class for regression algorithms testing that when a continuous future rollover happens, + /// the continuous contract is updated correctly with the new contract data. + /// The algorithms asserts the behavior for the case when the data time zone is the same as the exchange time zone. + /// + public class ContinuousFutureRolloverMinuteExchangeTimeZoneSameAsDataRegressionAlgorithm + : ContinuousFutureRolloverBaseRegressionAlgorithm + { + protected override Resolution Resolution => Resolution.Minute; + + protected override Offset ExchangeToDataTimeZoneOffset => Offset.Zero; + + /// + /// Data Points count of all timeslices of algorithm + /// + public override long DataPoints => 1127488; + } +} diff --git a/Algorithm.CSharp/FutureMarketOpenAndCloseWithExtendedMarketRegressionAlgorithm.cs b/Algorithm.CSharp/FutureMarketOpenAndCloseWithExtendedMarketRegressionAlgorithm.cs index 907cd6948ee4..8109fcf408bc 100644 --- a/Algorithm.CSharp/FutureMarketOpenAndCloseWithExtendedMarketRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureMarketOpenAndCloseWithExtendedMarketRegressionAlgorithm.cs @@ -50,7 +50,7 @@ public class FutureMarketOpenAndCloseWithExtendedMarketRegressionAlgorithm : Fut /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 41466; + public override long DataPoints => 41467; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm diff --git a/Algorithm.CSharp/FutureMarketOpenConsolidatorRegressionAlgorithm.cs b/Algorithm.CSharp/FutureMarketOpenConsolidatorRegressionAlgorithm.cs index 41db052bd7ee..098084841790 100644 --- a/Algorithm.CSharp/FutureMarketOpenConsolidatorRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureMarketOpenConsolidatorRegressionAlgorithm.cs @@ -93,7 +93,7 @@ public void Assert(BaseData bar) /// /// Data Points count of all timeslices of algorithm /// - public virtual long DataPoints => 32071; + public virtual long DataPoints => 32073; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/FutureMarketOpenConsolidatorWithExtendedMarketRegressionAlgorithm.cs b/Algorithm.CSharp/FutureMarketOpenConsolidatorWithExtendedMarketRegressionAlgorithm.cs index ad9434455ce8..51febcc14451 100644 --- a/Algorithm.CSharp/FutureMarketOpenConsolidatorWithExtendedMarketRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureMarketOpenConsolidatorWithExtendedMarketRegressionAlgorithm.cs @@ -52,7 +52,7 @@ public class FutureMarketOpenConsolidatorWithExtendedMarketRegressionAlgorithm : /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 103816; + public override long DataPoints => 103818; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm diff --git a/Algorithm.CSharp/FutureOptionBuySellCallIntradayRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionBuySellCallIntradayRegressionAlgorithm.cs index 0b2df56cc07b..9c3724d89748 100644 --- a/Algorithm.CSharp/FutureOptionBuySellCallIntradayRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionBuySellCallIntradayRegressionAlgorithm.cs @@ -112,7 +112,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 309280; + public long DataPoints => 309282; /// /// Data Points count of the algorithm history @@ -150,7 +150,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.005"}, {"Information Ratio", "-0.134"}, {"Tracking Error", "0.385"}, - {"Treynor Ratio", "3.784"}, + {"Treynor Ratio", "3.785"}, {"Total Fees", "$2.84"}, {"Estimated Strategy Capacity", "$120000000.00"}, {"Lowest Capacity Asset", "ES XFH59UPBIJ7O|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionCallITMExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionCallITMExpiryRegressionAlgorithm.cs index 25f88b83b090..e173b2eb12a1 100644 --- a/Algorithm.CSharp/FutureOptionCallITMExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionCallITMExpiryRegressionAlgorithm.cs @@ -200,7 +200,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -238,7 +238,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.004"}, {"Information Ratio", "-0.226"}, {"Tracking Error", "0.378"}, - {"Treynor Ratio", "-21.838"}, + {"Treynor Ratio", "-21.841"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$120000000.00"}, {"Lowest Capacity Asset", "ES XFH59UPBIJ7O|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionCallITMGreeksExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionCallITMGreeksExpiryRegressionAlgorithm.cs index 68bce654dfe6..d2d4b08323dc 100644 --- a/Algorithm.CSharp/FutureOptionCallITMGreeksExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionCallITMGreeksExpiryRegressionAlgorithm.cs @@ -165,7 +165,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -203,7 +203,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.098"}, {"Information Ratio", "-0.649"}, {"Tracking Error", "0.483"}, - {"Treynor Ratio", "-18.589"}, + {"Treynor Ratio", "-18.59"}, {"Total Fees", "$7.10"}, {"Estimated Strategy Capacity", "$24000000.00"}, {"Lowest Capacity Asset", "ES XFH59UPBIJ7O|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionCallOTMExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionCallOTMExpiryRegressionAlgorithm.cs index 19c4bfb64db7..945e63fefb2d 100644 --- a/Algorithm.CSharp/FutureOptionCallOTMExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionCallOTMExpiryRegressionAlgorithm.cs @@ -176,7 +176,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -214,7 +214,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.003"}, {"Information Ratio", "-0.198"}, {"Tracking Error", "0.377"}, - {"Treynor Ratio", "-23.06"}, + {"Treynor Ratio", "-23.065"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$180000000.00"}, {"Lowest Capacity Asset", "ES XFH59UPHGV9G|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionMultipleContractsInDifferentContractMonthsWithSameUnderlyingFutureRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionMultipleContractsInDifferentContractMonthsWithSameUnderlyingFutureRegressionAlgorithm.cs index d1efdfe28313..fec8ca9eb321 100644 --- a/Algorithm.CSharp/FutureOptionMultipleContractsInDifferentContractMonthsWithSameUnderlyingFutureRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionMultipleContractsInDifferentContractMonthsWithSameUnderlyingFutureRegressionAlgorithm.cs @@ -109,7 +109,7 @@ private static Symbol CreateOption(DateTime expiry, OptionRight optionRight, dec /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 24378; + public long DataPoints => 24379; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/FutureOptionPutITMExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionPutITMExpiryRegressionAlgorithm.cs index c0a01fa99020..177f9372d5bc 100644 --- a/Algorithm.CSharp/FutureOptionPutITMExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionPutITMExpiryRegressionAlgorithm.cs @@ -201,7 +201,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -239,7 +239,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.003"}, {"Information Ratio", "-0.248"}, {"Tracking Error", "0.377"}, - {"Treynor Ratio", "-36.295"}, + {"Treynor Ratio", "-36.3"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$79000000.00"}, {"Lowest Capacity Asset", "ES 31EL5FAOOQON8|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionPutOTMExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionPutOTMExpiryRegressionAlgorithm.cs index 50d24d7ca9df..c60149c64e81 100644 --- a/Algorithm.CSharp/FutureOptionPutOTMExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionPutOTMExpiryRegressionAlgorithm.cs @@ -174,7 +174,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -212,7 +212,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.003"}, {"Information Ratio", "-0.249"}, {"Tracking Error", "0.377"}, - {"Treynor Ratio", "-32.551"}, + {"Treynor Ratio", "-32.556"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$290000000.00"}, {"Lowest Capacity Asset", "ES 31EL5FBZBMXES|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionShortCallITMExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionShortCallITMExpiryRegressionAlgorithm.cs index d97531471a15..ed67a742d933 100644 --- a/Algorithm.CSharp/FutureOptionShortCallITMExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionShortCallITMExpiryRegressionAlgorithm.cs @@ -185,7 +185,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -223,7 +223,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.002"}, {"Information Ratio", "0.064"}, {"Tracking Error", "0.378"}, - {"Treynor Ratio", "-13.132"}, + {"Treynor Ratio", "-13.127"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$13000000.00"}, {"Lowest Capacity Asset", "ES XFH59UP5K75W|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionShortCallOTMExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionShortCallOTMExpiryRegressionAlgorithm.cs index 3387daef3906..45b8f5f2ba24 100644 --- a/Algorithm.CSharp/FutureOptionShortCallOTMExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionShortCallOTMExpiryRegressionAlgorithm.cs @@ -168,7 +168,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -194,7 +194,7 @@ public override void OnEndOfAlgorithm() {"Start Equity", "100000"}, {"End Equity", "101736.08"}, {"Net Profit", "1.736%"}, - {"Sharpe Ratio", "0.597"}, + {"Sharpe Ratio", "0.596"}, {"Sortino Ratio", "0"}, {"Probabilistic Sharpe Ratio", "49.736%"}, {"Loss Rate", "0%"}, @@ -206,7 +206,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.001"}, {"Information Ratio", "0.009"}, {"Tracking Error", "0.375"}, - {"Treynor Ratio", "-11.057"}, + {"Treynor Ratio", "-11.048"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$100000000.00"}, {"Lowest Capacity Asset", "ES XFH59UPNF7B8|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionShortPutITMExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionShortPutITMExpiryRegressionAlgorithm.cs index deedc5a91894..10b05989581b 100644 --- a/Algorithm.CSharp/FutureOptionShortPutITMExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionShortPutITMExpiryRegressionAlgorithm.cs @@ -182,7 +182,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -220,7 +220,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.001"}, {"Information Ratio", "0.058"}, {"Tracking Error", "0.375"}, - {"Treynor Ratio", "-41.846"}, + {"Treynor Ratio", "-41.831"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$12000000.00"}, {"Lowest Capacity Asset", "ES 31EL5FAOUP0P0|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureOptionShortPutOTMExpiryRegressionAlgorithm.cs b/Algorithm.CSharp/FutureOptionShortPutOTMExpiryRegressionAlgorithm.cs index cb9e6c11a9b9..4b9132b16c8d 100644 --- a/Algorithm.CSharp/FutureOptionShortPutOTMExpiryRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureOptionShortPutOTMExpiryRegressionAlgorithm.cs @@ -167,7 +167,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -193,7 +193,7 @@ public override void OnEndOfAlgorithm() {"Start Equity", "100000"}, {"End Equity", "103423.58"}, {"Net Profit", "3.424%"}, - {"Sharpe Ratio", "1.089"}, + {"Sharpe Ratio", "1.088"}, {"Sortino Ratio", "0"}, {"Probabilistic Sharpe Ratio", "73.287%"}, {"Loss Rate", "0%"}, @@ -205,7 +205,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.001"}, {"Information Ratio", "0.075"}, {"Tracking Error", "0.377"}, - {"Treynor Ratio", "-24.083"}, + {"Treynor Ratio", "-24.076"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$110000000.00"}, {"Lowest Capacity Asset", "ES 31EL5FAJQ6SBO|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FutureStopMarketOrderOnExtendedHoursRegressionAlgorithm.cs b/Algorithm.CSharp/FutureStopMarketOrderOnExtendedHoursRegressionAlgorithm.cs index 02c3d8cfb4ac..24affa9ea1d8 100644 --- a/Algorithm.CSharp/FutureStopMarketOrderOnExtendedHoursRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FutureStopMarketOrderOnExtendedHoursRegressionAlgorithm.cs @@ -113,7 +113,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all time slices of algorithm /// - public long DataPoints => 75960; + public long DataPoints => 75961; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/FuturesAndFuturesOptionsExpiryTimeAndLiquidationRegressionAlgorithm.cs b/Algorithm.CSharp/FuturesAndFuturesOptionsExpiryTimeAndLiquidationRegressionAlgorithm.cs index f0e012b69826..24730ef97112 100644 --- a/Algorithm.CSharp/FuturesAndFuturesOptionsExpiryTimeAndLiquidationRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FuturesAndFuturesOptionsExpiryTimeAndLiquidationRegressionAlgorithm.cs @@ -178,7 +178,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212941; + public long DataPoints => 212942; /// /// Data Points count of the algorithm history @@ -204,7 +204,7 @@ public override void OnEndOfAlgorithm() {"Start Equity", "100000"}, {"End Equity", "98233.93"}, {"Net Profit", "-1.766%"}, - {"Sharpe Ratio", "-1.14"}, + {"Sharpe Ratio", "-1.141"}, {"Sortino Ratio", "0"}, {"Probabilistic Sharpe Ratio", "0.020%"}, {"Loss Rate", "50%"}, @@ -216,7 +216,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0"}, {"Information Ratio", "-0.602"}, {"Tracking Error", "0.291"}, - {"Treynor Ratio", "-16.64"}, + {"Treynor Ratio", "-16.65"}, {"Total Fees", "$3.57"}, {"Estimated Strategy Capacity", "$16000000.00"}, {"Lowest Capacity Asset", "ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/FuturesExpiredContractRegression.cs b/Algorithm.CSharp/FuturesExpiredContractRegression.cs index 7812a48aaacb..0c8ec37ef63e 100644 --- a/Algorithm.CSharp/FuturesExpiredContractRegression.cs +++ b/Algorithm.CSharp/FuturesExpiredContractRegression.cs @@ -89,7 +89,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 268273; + public long DataPoints => 268275; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/FuturesExtendedMarketHoursRegressionAlgorithm.cs b/Algorithm.CSharp/FuturesExtendedMarketHoursRegressionAlgorithm.cs index 5dd438fe857d..69a687b6a8aa 100644 --- a/Algorithm.CSharp/FuturesExtendedMarketHoursRegressionAlgorithm.cs +++ b/Algorithm.CSharp/FuturesExtendedMarketHoursRegressionAlgorithm.cs @@ -113,7 +113,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 3630; + public long DataPoints => 3631; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/LimitOrdersAreFilledAfterHoursForFuturesRegressionAlgorithm.cs b/Algorithm.CSharp/LimitOrdersAreFilledAfterHoursForFuturesRegressionAlgorithm.cs index 436b5ec8fd81..7af2780d0a10 100644 --- a/Algorithm.CSharp/LimitOrdersAreFilledAfterHoursForFuturesRegressionAlgorithm.cs +++ b/Algorithm.CSharp/LimitOrdersAreFilledAfterHoursForFuturesRegressionAlgorithm.cs @@ -113,7 +113,7 @@ public override void OnOrderEvent(OrderEvent orderEvent) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 82371; + public long DataPoints => 82372; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/MarketOrdersAreSupportedOnExtendedHoursForFuturesRegressionAlgorithm.cs b/Algorithm.CSharp/MarketOrdersAreSupportedOnExtendedHoursForFuturesRegressionAlgorithm.cs index 30aa43e92d44..9bdeeada06c0 100644 --- a/Algorithm.CSharp/MarketOrdersAreSupportedOnExtendedHoursForFuturesRegressionAlgorithm.cs +++ b/Algorithm.CSharp/MarketOrdersAreSupportedOnExtendedHoursForFuturesRegressionAlgorithm.cs @@ -90,7 +90,7 @@ public override void OnOrderEvent(OrderEvent orderEvent) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 82371; + public long DataPoints => 82372; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/OptionOTMExpiryOrderHasZeroPriceRegressionAlgorithm.cs b/Algorithm.CSharp/OptionOTMExpiryOrderHasZeroPriceRegressionAlgorithm.cs index 4e496f3e5ddf..e695e5583065 100644 --- a/Algorithm.CSharp/OptionOTMExpiryOrderHasZeroPriceRegressionAlgorithm.cs +++ b/Algorithm.CSharp/OptionOTMExpiryOrderHasZeroPriceRegressionAlgorithm.cs @@ -153,7 +153,7 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 212195; + public long DataPoints => 212196; /// /// Data Points count of the algorithm history @@ -191,7 +191,7 @@ public override void OnEndOfAlgorithm() {"Annual Variance", "0.003"}, {"Information Ratio", "-0.198"}, {"Tracking Error", "0.377"}, - {"Treynor Ratio", "-23.06"}, + {"Treynor Ratio", "-23.065"}, {"Total Fees", "$1.42"}, {"Estimated Strategy Capacity", "$180000000.00"}, {"Lowest Capacity Asset", "ES XFH59UPHGV9G|ES XFH59UK0MYO1"}, diff --git a/Algorithm.CSharp/OptionPriceModelForSupportedEuropeanOptionRegressionAlgorithm.cs b/Algorithm.CSharp/OptionPriceModelForSupportedEuropeanOptionRegressionAlgorithm.cs index 84f792815220..b6077696d50b 100644 --- a/Algorithm.CSharp/OptionPriceModelForSupportedEuropeanOptionRegressionAlgorithm.cs +++ b/Algorithm.CSharp/OptionPriceModelForSupportedEuropeanOptionRegressionAlgorithm.cs @@ -41,7 +41,7 @@ public override void Initialize() /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 175; + public override long DataPoints => 177; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/OptionPriceModelForUnsupportedEuropeanOptionRegressionAlgorithm.cs b/Algorithm.CSharp/OptionPriceModelForUnsupportedEuropeanOptionRegressionAlgorithm.cs index f68eb24d6660..65f892960aed 100644 --- a/Algorithm.CSharp/OptionPriceModelForUnsupportedEuropeanOptionRegressionAlgorithm.cs +++ b/Algorithm.CSharp/OptionPriceModelForUnsupportedEuropeanOptionRegressionAlgorithm.cs @@ -42,7 +42,7 @@ public override void Initialize() /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 175; + public override long DataPoints => 177; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/RegisterIndicatorRegressionAlgorithm.cs b/Algorithm.CSharp/RegisterIndicatorRegressionAlgorithm.cs index fe1e1a022cf8..c39c08b7a929 100644 --- a/Algorithm.CSharp/RegisterIndicatorRegressionAlgorithm.cs +++ b/Algorithm.CSharp/RegisterIndicatorRegressionAlgorithm.cs @@ -160,7 +160,7 @@ protected override decimal ComputeNextValue(QuoteBar input) /// /// Data Points count of all timeslices of algorithm /// - public long DataPoints => 6802; + public long DataPoints => 6803; /// /// Data Points count of the algorithm history diff --git a/Algorithm.CSharp/WarmupFutureTimeSpanWarmupRegressionAlgorithm.cs b/Algorithm.CSharp/WarmupFutureTimeSpanWarmupRegressionAlgorithm.cs index 02cd88ce6a61..8bc3c4d26cd3 100644 --- a/Algorithm.CSharp/WarmupFutureTimeSpanWarmupRegressionAlgorithm.cs +++ b/Algorithm.CSharp/WarmupFutureTimeSpanWarmupRegressionAlgorithm.cs @@ -36,6 +36,6 @@ public override void OnEndOfAlgorithm() /// /// Data Points count of all timeslices of algorithm /// - public override long DataPoints => 28889; + public override long DataPoints => 28892; } } diff --git a/Common/Data/UniverseSelection/ContinuousContractUniverse.cs b/Common/Data/UniverseSelection/ContinuousContractUniverse.cs index bd7a8ee44670..78829b8809a1 100644 --- a/Common/Data/UniverseSelection/ContinuousContractUniverse.cs +++ b/Common/Data/UniverseSelection/ContinuousContractUniverse.cs @@ -120,7 +120,7 @@ public IEnumerable GetTriggerTimes(DateTime startTimeUtc, DateTime end var startTimeLocal = startTimeUtc.ConvertFromUtc(_security.Exchange.TimeZone); var endTimeLocal = endTimeUtc.ConvertFromUtc(_security.Exchange.TimeZone); - return Time.EachTradeableDay(_security, startTimeLocal, endTimeLocal, extendedMarketHours: false) + return Time.EachTradeableDay(_security, startTimeLocal, endTimeLocal, Configuration.ExtendedMarketHours) // in live trading selection happens on start see 'DataQueueFuturesChainUniverseDataCollectionEnumerator' .Where(tradeableDay => _liveMode || tradeableDay >= startTimeLocal) // in live trading we delay selection so that we make sure auxiliary data is ready diff --git a/Common/Data/UniverseSelection/SubscriptionRequest.cs b/Common/Data/UniverseSelection/SubscriptionRequest.cs index 07cfb3f3b07c..1a4bf4cc0f44 100644 --- a/Common/Data/UniverseSelection/SubscriptionRequest.cs +++ b/Common/Data/UniverseSelection/SubscriptionRequest.cs @@ -47,7 +47,7 @@ public class SubscriptionRequest : BaseDataRequest /// /// Gets the tradable days specified by this request, in the security's data time zone /// - public override IEnumerable TradableDaysInDataTimeZone => Time.EachTradeableDayInTimeZone(Security.Exchange.Hours, + public override IEnumerable TradableDaysInDataTimeZone => Time.EachTradeableDayInTimeZone(ExchangeHours, StartTimeLocal, EndTimeLocal, Configuration.DataTimeZone, diff --git a/Common/TimeKeeper.cs b/Common/TimeKeeper.cs index 775a8a386f72..720f29db237c 100644 --- a/Common/TimeKeeper.cs +++ b/Common/TimeKeeper.cs @@ -67,7 +67,7 @@ public TimeKeeper(DateTime utcDateTime, IEnumerable timeZones) /// Sets the current UTC time for this time keeper and the attached child instances. /// /// The current time in UTC - public void SetUtcDateTime(DateTime utcDateTime) + public virtual void SetUtcDateTime(DateTime utcDateTime) { _utcDateTime = utcDateTime; foreach (var timeZone in _localTimeKeepers) diff --git a/Data/future/comex/map_files/gc.csv b/Data/future/comex/map_files/gc.csv index d22c6052fd82..c395b0b03b2c 100644 --- a/Data/future/comex/map_files/gc.csv +++ b/Data/future/comex/map_files/gc.csv @@ -39,4 +39,31 @@ 20131226,gc vmrhkn2nlwv1,COMEX,0 20131226,gc vmrhkn2nlwv1,COMEX,2 20131227,gc vnnzbq584t5p,COMEX,2 -20131231,gc vnnzbq584t5p,COMEX,1 \ No newline at end of file +20131231,gc vnnzbq584t5p,COMEX,1 +20190523,gc x5nnsa8f63z1,COMEX,3 +20190528,gc x4w39nzp0zh9,COMEX,0 +20190531,gc x5nnsa8f63z1,COMEX,1 +20190605,gc x5nnsa8f63z1,COMEX,2 +20190606,gc x6k5jdazp09p,COMEX,2 +20190625,gc x5nnsa8f63z1,COMEX,0 +20190630,gc x6k5jdazp09p,COMEX,1 +20190724,gc x7doy6a1zn31,COMEX,3 +20190728,gc x6k5jdazp09p,COMEX,0 +20190731,gc x7doy6a1zn31,COMEX,1 +20190802,gc x7doy6a1zn31,COMEX,2 +20190804,gc x868wvvy7iql,COMEX,2 +20190827,gc x7doy6a1zn31,COMEX,0 +20190831,gc x868wvvy7iql,COMEX,1 +20190925,gc x868wvvy7iql,COMEX,0 +20190930,gc x92qnyyiqf19,COMEX,1 +20191006,gc x92qnyyiqf19,COMEX,2 +20191007,gc x9ub6l78vjj1,COMEX,2 +20191028,gc x92qnyyiqf19,COMEX,0 +20191031,gc x9ub6l78vjj1,COMEX,1 +20191121,gc xaou1hjh8xi5,COMEX,3 +20191125,gc x9ub6l78vjj1,COMEX,0 +20191130,gc xaou1hjh8xi5,COMEX,1 +20191203,gc xaou1hjh8xi5,COMEX,2 +20191204,gc xblbskm1rtst,COMEX,2 +20191226,gc xaou1hjh8xi5,COMEX,0 +20191231,gc xblbskm1rtst,COMEX,1 \ No newline at end of file diff --git a/Engine/DataFeeds/DateChangeTimeKeeper.cs b/Engine/DataFeeds/DateChangeTimeKeeper.cs new file mode 100644 index 000000000000..7092f71db9f7 --- /dev/null +++ b/Engine/DataFeeds/DateChangeTimeKeeper.cs @@ -0,0 +1,332 @@ +/* + * 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; +using QuantConnect.Util; +using QuantConnect.Data; +using System.Collections.Generic; +using QuantConnect.Securities; +using System.Runtime.CompilerServices; + +namespace QuantConnect.Lean.Engine.DataFeeds +{ + /// + /// Time keeper specialization to keep time for a subscription both in data and exchange time zones. + /// It also emits events when the exchange date changes, which is useful to emit date change events + /// required for some daily actions like mapping symbols, delistings, splits, etc. + /// + internal class DateChangeTimeKeeper : TimeKeeper, IDisposable + { + private IEnumerator _tradableDatesInDataTimeZone; + private SubscriptionDataConfig _config; + private SecurityExchangeHours _exchangeHours; + private DateTime _delistingDate; + + private DateTime _previousNewExchangeDate; + + private bool _needsMoveNext; + private bool _initialized; + + private DateTime _exchangeTime; + private DateTime _dataTime; + private bool _exchangeTimeNeedsUpdate; + private bool _dataTimeNeedsUpdate; + + /// + /// The current time in the data time zone + /// + public DateTime DataTime + { + get + { + if (_dataTimeNeedsUpdate) + { + _dataTime = GetTimeIn(_config.DataTimeZone); + _dataTimeNeedsUpdate = false; + } + return _dataTime; + } + } + + /// + /// The current time in the exchange time zone + /// + public DateTime ExchangeTime + { + get + { + if (_exchangeTimeNeedsUpdate) + { + _exchangeTime = GetTimeIn(_config.ExchangeTimeZone); + _exchangeTimeNeedsUpdate = false; + } + return _exchangeTime; + } + } + + /// + /// Event that fires every time the exchange date changes + /// + public event EventHandler NewExchangeDate; + + /// + /// Initializes a new instance of the class + /// + /// The tradable dates in data time zone + /// The subscription data configuration this instance will keep track of time for + /// The exchange hours + /// The symbol's delisting date + public DateChangeTimeKeeper(IEnumerable tradableDatesInDataTimeZone, SubscriptionDataConfig config, + SecurityExchangeHours exchangeHours, DateTime delistingDate) + : base(Time.BeginningOfTime, new[] { config.DataTimeZone, config.ExchangeTimeZone }) + { + _tradableDatesInDataTimeZone = tradableDatesInDataTimeZone.GetEnumerator(); + _config = config; + _exchangeHours = exchangeHours; + _delistingDate = delistingDate; + _exchangeTimeNeedsUpdate = true; + _dataTimeNeedsUpdate = true; + _needsMoveNext = true; + } + + /// + /// Disposes the resources + /// + public void Dispose() + { + _tradableDatesInDataTimeZone.DisposeSafely(); + } + + /// + /// Sets the current UTC time for this time keeper + /// + /// The current time in UTC + public override void SetUtcDateTime(DateTime utcDateTime) + { + base.SetUtcDateTime(utcDateTime); + _exchangeTimeNeedsUpdate = true; + _dataTimeNeedsUpdate = true; + } + + /// + /// Advances the time keeper towards the target exchange time. + /// If an exchange date is found before the target time, it is emitted and the time keeper is set to that date. + /// The caller must check whether the target time was reached or if the time keeper was set to a new exchange date before the target time. + /// + public void AdvanceTowardsExchangeTime(DateTime targetExchangeTime) + { + if (!_initialized) + { + throw new InvalidOperationException($"The time keeper has not been initialized. " + + $"{nameof(TryAdvanceUntilNextDataDate)} needs to be called at least once to flush the first date before advancing."); + } + + var currentExchangeTime = ExchangeTime; + // Advancing within the same exchange date, just update the time, no new exchange date will be emitted + if (targetExchangeTime.Date == currentExchangeTime.Date) + { + SetExchangeTime(targetExchangeTime); + return; + } + + while (currentExchangeTime < targetExchangeTime) + { + var newExchangeTime = currentExchangeTime + Time.OneDay; + if (newExchangeTime > targetExchangeTime) + { + newExchangeTime = targetExchangeTime; + } + + var newExchangeDate = newExchangeTime.Date; + + // We found a new exchange date before the target time, emit it first + if (newExchangeDate != currentExchangeTime.Date && + _exchangeHours.IsDateOpen(newExchangeDate, _config.ExtendedMarketHours)) + { + // Stop here, set the new exchange date + SetExchangeTime(newExchangeDate); + EmitNewExchangeDate(newExchangeDate); + return; + } + + currentExchangeTime = newExchangeTime; + } + + // We reached the target time, set it + SetExchangeTime(targetExchangeTime); + } + + /// + /// Advances the time keeper until the next data date, emitting the new exchange date if this happens before the new data date + /// + public bool TryAdvanceUntilNextDataDate() + { + if (!_initialized) + { + return EmitFirstExchangeDate(); + } + + // Before moving forward, check whether we need to emit a new exchange date + if (TryEmitPassedExchangeDate()) + { + return true; + } + + if (!_needsMoveNext || _tradableDatesInDataTimeZone.MoveNext()) + { + var nextDataDate = _tradableDatesInDataTimeZone.Current; + var nextExchangeTime = nextDataDate.ConvertTo(_config.DataTimeZone, _config.ExchangeTimeZone); + var nextExchangeDate = nextExchangeTime.Date; + + if (nextExchangeDate > _delistingDate) + { + // We are done, but an exchange date might still need to be emitted + TryEmitPassedExchangeDate(); + _needsMoveNext = false; + return false; + } + + // If the exchange is not behind the data, the data might have not been enough to emit the exchange date, + // which already passed if we are moving on to the next data date. So we need to check if we need to emit it here. + // e.g. moving data date from tuesday to wednesday, but the exchange date is already past the end of tuesday + // (by N hours, depending on the time zones offset). If data didn't trigger the exchange date change, we need to do it here. + if (!IsExchangeBehindData(nextExchangeTime, nextDataDate) && nextExchangeDate > _previousNewExchangeDate) + { + EmitNewExchangeDate(nextExchangeDate); + SetExchangeTime(nextExchangeDate); + // nextExchangeDate == DataTime means time zones are synchronized, need to move next only when exchange is actually ahead + _needsMoveNext = nextExchangeDate == DataTime; + return true; + } + + _needsMoveNext = true; + SetDataTime(nextDataDate); + return true; + } + + _needsMoveNext = false; + return false; + } + + /// + /// Emits the first exchange date for the algorithm so that the first daily events are triggered (mappings, delistings, etc.) + /// + /// True if the new exchange date is emitted. False if already done or the tradable dates enumerable is empty + private bool EmitFirstExchangeDate() + { + if (_initialized) + { + return false; + } + + if (!_tradableDatesInDataTimeZone.MoveNext()) + { + _initialized = true; + return false; + } + + var firstDataDate = _tradableDatesInDataTimeZone.Current; + var firstExchangeTime = firstDataDate.ConvertTo(_config.DataTimeZone, _config.ExchangeTimeZone); + var firstExchangeDate = firstExchangeTime.Date; + + DateTime exchangeDateToEmit; + // The exchange is ahead of the data, so we need to emit the current exchange date, which already passed + if (firstExchangeTime < firstDataDate && _exchangeHours.IsDateOpen(firstExchangeDate, _config.ExtendedMarketHours)) + { + exchangeDateToEmit = firstExchangeDate; + SetExchangeTime(exchangeDateToEmit); + // Don't move, the current data date still needs to be consumed + _needsMoveNext = false; + } + // The exchange is behind of (or in sync with) data: exchange has not passed to this new date, but with emit it here + // so that first daily things are done (mappings, delistings, etc.) + else + { + exchangeDateToEmit = firstDataDate; + SetDataTime(firstDataDate); + _needsMoveNext = true; + } + + EmitNewExchangeDate(exchangeDateToEmit); + _initialized = true; + return true; + } + + /// + /// Determines whether the exchange time zone is behind the data time zone + /// + /// + public bool IsExchangeBehindData() + { + return IsExchangeBehindData(ExchangeTime, DataTime); + } + + /// + /// Determines whether the exchange time zone is behind the data time zone + /// + private static bool IsExchangeBehindData(DateTime exchangeTime, DateTime dataTime) + { + return dataTime > exchangeTime; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetExchangeTime(DateTime exchangeTime) + { + SetUtcDateTime(exchangeTime.ConvertToUtc(_config.ExchangeTimeZone)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetDataTime(DateTime dataTime) + { + SetUtcDateTime(dataTime.ConvertToUtc(_config.DataTimeZone)); + } + + /// + /// Checks that before moving to the next data date, if the exchange date has already passed and has been emitted, else it emits it. + /// This can happen when the exchange is behind of the data. e.g We advance data date from Monday to Tuesday, then the data itself + /// will drive the exchange data change (N hours later, depending on the time zones offset). + /// But if there is no enough data or the file is not found, the new exchange date will not be emitted, so we need to do it here. + /// + private bool TryEmitPassedExchangeDate() + { + if (_needsMoveNext && _tradableDatesInDataTimeZone.Current != default) + { + // This data date passed, and it should have emitted as an exchange tradable date when detected + // as a date change in the data itself, if not, emit it now before moving to the next data date + var currentDataDate = _tradableDatesInDataTimeZone.Current; + if (_previousNewExchangeDate < currentDataDate && + _exchangeHours.IsDateOpen(currentDataDate, _config.ExtendedMarketHours)) + { + var nextExchangeDate = currentDataDate; + SetExchangeTime(nextExchangeDate); + EmitNewExchangeDate(nextExchangeDate); + return true; + } + } + + return false; + } + + /// + /// Emits a new exchange date event + /// + private void EmitNewExchangeDate(DateTime newExchangeDate) + { + NewExchangeDate?.Invoke(this, newExchangeDate); + _previousNewExchangeDate = newExchangeDate; + } + } +} diff --git a/Engine/DataFeeds/SubscriptionDataReader.cs b/Engine/DataFeeds/SubscriptionDataReader.cs index 96cb6ce7e93f..e05ad98553dc 100644 --- a/Engine/DataFeeds/SubscriptionDataReader.cs +++ b/Engine/DataFeeds/SubscriptionDataReader.cs @@ -27,6 +27,7 @@ using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Custom.Tiingo; using QuantConnect.Lean.Engine.DataFeeds.Enumerators; +using QuantConnect.Securities; namespace QuantConnect.Lean.Engine.DataFeeds { @@ -71,12 +72,16 @@ public class SubscriptionDataReader : IEnumerator, ITradableDatesNotif private BaseData _previous; private decimal? _lastRawPrice; - private readonly IEnumerator _tradeableDates; + private DateChangeTimeKeeper _timeKeeper; + private readonly IEnumerable _tradableDatesInDataTimeZone; + private readonly SecurityExchangeHours _exchangeHours; // used when emitting aux data from within while loop private readonly IDataCacheProvider _dataCacheProvider; private DateTime _delistingDate; + private bool _updatingDataEnumerator; + /// /// Event fired when an invalid configuration has been detected /// @@ -151,10 +156,11 @@ public SubscriptionDataReader(SubscriptionDataConfig config, _factorFileProvider = factorFileProvider; _dataCacheProvider = dataCacheProvider; - //Save access to securities - _tradeableDates = dataRequest.TradableDaysInDataTimeZone.GetEnumerator(); _dataProvider = dataProvider; _objectStore = objectStore; + + _tradableDatesInDataTimeZone = dataRequest.TradableDaysInDataTimeZone; + _exchangeHours = dataRequest.ExchangeHours; } /// @@ -248,6 +254,10 @@ public void Initialize() // adding a day so we stop at EOD _delistingDate = _delistingDate.AddDays(1); + + _timeKeeper = new DateChangeTimeKeeper(_tradableDatesInDataTimeZone, _config, _exchangeHours, _delistingDate); + _timeKeeper.NewExchangeDate += HandleNewTradableDate; + UpdateDataEnumerator(true); _initialized = true; @@ -292,6 +302,7 @@ public bool MoveNext() { break; } + // keep enumerating until we find something that is within our time frame while (_subscriptionFactoryEnumerator.MoveNext()) { @@ -302,6 +313,50 @@ public bool MoveNext() continue; } + // We rely on symbol change to detect a mapping or symbol change, instead of using SubscriptionDataConfig.NewSymbol + // because only one of the configs with the same symbol will trigger a symbol change event. + var previousMappedSymbol = _config.MappedSymbol; + + // Advance the time keeper either until the current instance time (to synchronize) or until the source changes. + // Note: use time instead of end time to avoid skipping instances that all have the same timestamps in the same file (e.g. universe data) + var currentSource = _source; + var nextExchangeDate = _config.Resolution == Resolution.Daily && _timeKeeper.IsExchangeBehindData() + // If daily and exchange is behind data, data for date X will have a start time within date X-1, + // so we use the actual date from end time. e.g. a daily bar for Jan15 can have a start time of Jan14 8PM + // (exchange tz 4 hours behind data tz) and end time would be Jan15 8PM. + ? instance.EndTime + : instance.Time; + while (_timeKeeper.ExchangeTime < nextExchangeDate && currentSource == _source) + { + _timeKeeper.AdvanceTowardsExchangeTime(nextExchangeDate); + } + + // Source change, check if we should emit the current instance + if (currentSource != _source + && ( + // After a mapping for every resolution except daily: + // For other resolutions, the instance that triggered the exchange date change should be skipped, + // it's end time will be either midnight or for a future date. The new source might have a data point with this times. + (_config.MappedSymbol != previousMappedSymbol && _config.Resolution != Resolution.Daily) + // Skip if the exchange time zone is behind of the data time zone: + // The new source might have data for these same times, we want data for the new symbol + || (_config.Resolution == Resolution.Daily && _timeKeeper.IsExchangeBehindData()) + // skip if the instance if it's beyond what the previous source should have. + // e.g. A file mistakenly has data for the next day + // (see SubscriptionDataReaderTests.DoesNotEmitDataBeyondTradableDate unit test) + // or the instance that triggered the exchange date change is for a future date (no data found in between) + || instance.EndTime.ConvertTo(_config.ExchangeTimeZone, _config.DataTimeZone).Date >= _timeKeeper.DataTime.Date + )) + { + continue; + } + + // This can happen after a mapping, we already have data but we need to skip some points that belong to a previous date. + if (Current != null && instance.EndTime < _timeKeeper.ExchangeTime) + { + continue; + } + // prevent emitting past data, this can happen when switching symbols on daily data if (_previous != null && _config.Resolution != Resolution.Tick) { @@ -325,43 +380,6 @@ public bool MoveNext() continue; } - // if we move past our current 'date' then we need to do daily things, such - // as updating factors and symbol mapping - var shouldSkip = false; - - while (instance.Time.ConvertTo(_config.ExchangeTimeZone, _config.DataTimeZone).Date > _tradeableDates.Current) - { - var currentTradeableDate = _tradeableDates.Current; - if (UpdateDataEnumerator(false)) - { - shouldSkip = true; - if (_subscriptionFactoryEnumerator == null) - { - // if null enumerator we have not been mapped into something new, we just ended, - // let's double check this data point should be skipped or not based on current tradeable date - shouldSkip = instance.Time.ConvertTo(_config.ExchangeTimeZone, _config.DataTimeZone).Date > _tradeableDates.Current; - if (shouldSkip) - { - // the end, no new enumerator and current instance is beyond current date - _endOfStream = true; - return false; - } - } - break; - } - - if (currentTradeableDate == _tradeableDates.Current) - { - // if tradeable dates did not advanced let's not check again - break; - } - } - if(shouldSkip) - { - // Skip current 'instance' if its start time is beyond the current date, fixes GH issue 3912 - continue; - } - // We have to perform this check after refreshing the enumerator, if appropriate // 'instance' could be a data point far in the future due to remapping (GH issue 5232) in which case it will be dropped if (instance.Time > _periodFinish) @@ -389,6 +407,15 @@ public bool MoveNext() return false; } + /// + /// Emits a new tradable date event and tries to update the data enumerator if necessary + /// + private void HandleNewTradableDate(object sender, DateTime date) + { + OnNewTradableDate(new NewTradableDateEventArgs(date, _previous, _config.Symbol, _lastRawPrice)); + UpdateDataEnumerator(false); + } + /// /// Resolves the next enumerator to be used in and updates /// @@ -396,58 +423,74 @@ public bool MoveNext() /// True, if the enumerator has been updated (even if updated to null) private bool UpdateDataEnumerator(bool endOfEnumerator) { - do + // Guard for infinite recursion: during an enumerator update, we might ask for a new date, + // which might end up with a new exchange date being detected and another update being requested. + // Just skip that update and let's do it ourselves after the date is resolved + if (_updatingDataEnumerator) { - // always advance the date enumerator, this function is intended to be - // called on date changes, never return null for live mode, we'll always - // just keep trying to refresh the subscription - DateTime date; - if (!TryGetNextDate(out date)) - { - _subscriptionFactoryEnumerator = null; - // if we run out of dates then we're finished with this subscription - return true; - } + return false; + } - // fetch the new source, using the data time zone for the date - var newSource = _dataFactory.GetSource(_config, date, false); - if (newSource == null) + _updatingDataEnumerator = true; + try + { + do { - // move to the next day - continue; - } + var date = _timeKeeper.DataTime.Date; - // check if we should create a new subscription factory - var sourceChanged = _source != newSource && !string.IsNullOrEmpty(newSource.Source); - if (sourceChanged) - { - // dispose of the current enumerator before creating a new one - Dispose(); + // Update current date only if the enumerator has ended, else we might just need to change files + // (e.g. same date, but symbol was mapped) + if (endOfEnumerator && !TryGetNextDate(out date)) + { + _subscriptionFactoryEnumerator = null; + // if we run out of dates then we're finished with this subscription + return true; + } - // save off for comparison next time - _source = newSource; - var subscriptionFactory = CreateSubscriptionFactory(newSource, _dataFactory, _dataProvider); - _subscriptionFactoryEnumerator = SortEnumerator.TryWrapSortEnumerator(newSource.Sort, subscriptionFactory.Read(newSource)); - return true; - } + // fetch the new source, using the data time zone for the date + var newSource = _dataFactory.GetSource(_config, date, false); + if (newSource == null) + { + // move to the next day + continue; + } - // if there's still more in the enumerator and we received the same source from the GetSource call - // above, then just keep using the same enumerator as we were before - if (!endOfEnumerator) // && !sourceChanged is always true here - { - return false; - } + // check if we should create a new subscription factory + var sourceChanged = _source != newSource && !string.IsNullOrEmpty(newSource.Source); + if (sourceChanged) + { + // dispose of the current enumerator before creating a new one + _subscriptionFactoryEnumerator.DisposeSafely(); + + // save off for comparison next time + _source = newSource; + var subscriptionFactory = CreateSubscriptionFactory(newSource, _dataFactory, _dataProvider); + _subscriptionFactoryEnumerator = SortEnumerator.TryWrapSortEnumerator(newSource.Sort, subscriptionFactory.Read(newSource)); + return true; + } + + // if there's still more in the enumerator and we received the same source from the GetSource call + // above, then just keep using the same enumerator as we were before + if (!endOfEnumerator) // && !sourceChanged is always true here + { + return false; + } - // keep churning until we find a new source or run out of tradeable dates - // in live mode tradeable dates won't advance beyond today's date, but - // TryGetNextDate will return false if it's already at today + // keep churning until we find a new source or run out of tradeable dates + // in live mode tradeable dates won't advance beyond today's date, but + // TryGetNextDate will return false if it's already at today + } + while (true); + } + finally + { + _updatingDataEnumerator = false; } - while (true); } private ISubscriptionDataSourceReader CreateSubscriptionFactory(SubscriptionDataSource source, BaseData baseDataInstance, IDataProvider dataProvider) { - var factory = SubscriptionDataSourceReader.ForSource(source, _dataCacheProvider, _config, _tradeableDates.Current, false, baseDataInstance, dataProvider, _objectStore); + var factory = SubscriptionDataSourceReader.ForSource(source, _dataCacheProvider, _config, _timeKeeper.DataTime.Date, false, baseDataInstance, dataProvider, _objectStore); AttachEventHandlers(factory, source); return factory; } @@ -514,11 +557,9 @@ private void AttachEventHandlers(ISubscriptionDataSourceReader dataSourceReader, /// True if we got a new date from the enumerator, false if it's exhausted, or in live mode if we're already at today private bool TryGetNextDate(out DateTime date) { - while (_tradeableDates.MoveNext()) + while (_timeKeeper.TryAdvanceUntilNextDataDate()) { - date = _tradeableDates.Current; - - OnNewTradableDate(new NewTradableDateEventArgs(date, _previous, _config.Symbol, _lastRawPrice)); + date = _timeKeeper.DataTime.Date; if (_pastDelistedDate || date > _delistingDate) { @@ -533,7 +574,7 @@ private bool TryGetNextDate(out DateTime date) } // don't do other checks if we haven't gotten data for this date yet - if (_previous != null && _previous.EndTime.ConvertTo(_config.ExchangeTimeZone, _config.DataTimeZone) > _tradeableDates.Current) + if (_previous != null && _previous.EndTime.ConvertTo(_config.ExchangeTimeZone, _config.DataTimeZone) > date) { continue; } @@ -562,7 +603,12 @@ public void Reset() public void Dispose() { _subscriptionFactoryEnumerator.DisposeSafely(); - _tradeableDates.DisposeSafely(); + + if (_initialized) + { + _timeKeeper.NewExchangeDate -= HandleNewTradableDate; + _timeKeeper.DisposeSafely(); + } } /// diff --git a/Engine/Properties/AssemblyInfo.cs b/Engine/Properties/AssemblyInfo.cs index b316a6a0c5f5..cfc555b0400b 100644 --- a/Engine/Properties/AssemblyInfo.cs +++ b/Engine/Properties/AssemblyInfo.cs @@ -1,17 +1,20 @@ -using System.Reflection; +using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following +// General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("QuantConnect.Lean.Engine")] [assembly: AssemblyProduct("QuantConnect.Lean.Engine")] [assembly: AssemblyCulture("")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("5c62be9c-5ecb-413e-bb6e-c6544e5b809c")] \ No newline at end of file +[assembly: Guid("5c62be9c-5ecb-413e-bb6e-c6544e5b809c")] + +[assembly: InternalsVisibleTo("QuantConnect.Tests")] diff --git a/Tests/Algorithm/AlgorithmChainsTests.cs b/Tests/Algorithm/AlgorithmChainsTests.cs index 91386cc01d88..3749d22177f1 100644 --- a/Tests/Algorithm/AlgorithmChainsTests.cs +++ b/Tests/Algorithm/AlgorithmChainsTests.cs @@ -41,7 +41,14 @@ public void OneTimeSetUp() var parameters = new HistoryProviderInitializeParameters(null, null, TestGlobals.DataProvider, TestGlobals.DataCacheProvider, TestGlobals.MapFileProvider, TestGlobals.FactorFileProvider, (_) => { }, true, new DataPermissionManager(), null, new AlgorithmSettings()); - historyProvider.Initialize(parameters); + try + { + historyProvider.Initialize(parameters); + } + catch (InvalidOperationException) + { + // Already initialized + } _algorithm = new QCAlgorithm(); _algorithm.SetHistoryProvider(historyProvider); diff --git a/Tests/Common/Securities/Options/OptionChainProviderTests.cs b/Tests/Common/Securities/Options/OptionChainProviderTests.cs index 720f24fce065..9dc85b92a7a6 100644 --- a/Tests/Common/Securities/Options/OptionChainProviderTests.cs +++ b/Tests/Common/Securities/Options/OptionChainProviderTests.cs @@ -35,7 +35,14 @@ public void OneTimeSetUp() { var historyProvider = Composer.Instance.GetExportedValueByTypeName("SubscriptionDataReaderHistoryProvider", true); var parameters = new HistoryProviderInitializeParameters(null, null, TestGlobals.DataProvider, TestGlobals.DataCacheProvider, TestGlobals.MapFileProvider, TestGlobals.FactorFileProvider, (_) => { }, true, new DataPermissionManager(), null, new AlgorithmSettings()); - historyProvider.Initialize(parameters); + try + { + historyProvider.Initialize(parameters); + } + catch (InvalidOperationException) + { + // Already initialized + } } [Test] diff --git a/Tests/Engine/DataFeeds/DateChangeTimeKeeperTests.cs b/Tests/Engine/DataFeeds/DateChangeTimeKeeperTests.cs new file mode 100644 index 000000000000..052a1576fdea --- /dev/null +++ b/Tests/Engine/DataFeeds/DateChangeTimeKeeperTests.cs @@ -0,0 +1,360 @@ +/* + * 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; +using System.Collections.Generic; +using System.Linq; +using NodaTime; +using NUnit.Framework; +using QuantConnect.Data; +using QuantConnect.Data.Market; +using QuantConnect.Lean.Engine.DataFeeds; +using QuantConnect.Securities; +using QuantConnect.Util; + +namespace QuantConnect.Tests.Engine.DataFeeds +{ + [TestFixture] + public class DateChangeTimeKeeperTests + { + private static DateTime _start = new DateTime(2024, 10, 01); + private static DateTime _end = new DateTime(2024, 10, 11); + + private static TestCaseData[] TimeZonesTestCases => new TestCaseData[] + { + new(TimeZones.Utc, DateTimeZone.ForOffset(Offset.FromHours(-1))), + new(TimeZones.Utc, DateTimeZone.ForOffset(Offset.FromHours(-2))), + new(TimeZones.Utc, DateTimeZone.ForOffset(Offset.FromHours(-3))), + new(TimeZones.Utc, DateTimeZone.ForOffset(Offset.FromHours(-4))), + new(TimeZones.Utc, DateTimeZone.ForOffset(Offset.FromHours(1))), + new(TimeZones.Utc, DateTimeZone.ForOffset(Offset.FromHours(2))), + new(TimeZones.Utc, DateTimeZone.ForOffset(Offset.FromHours(3))), + new(TimeZones.Utc, DateTimeZone.ForOffset(Offset.FromHours(4))), + new(TimeZones.Utc, TimeZones.Utc), + new(TimeZones.NewYork, TimeZones.NewYork), + new(TimeZones.HongKong, TimeZones.HongKong), + }; + + [TestCaseSource(nameof(TimeZonesTestCases))] + public void EmitsFirstExchangeDateEvent(DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone) + { + using var timeKeeper = GetTimeKeeper(dataTimeZone, exchangeTimeZone, true, null, out var tradableDates, out var config, out var exchangeHours); + + var emittedExchangeDates = new List(); + void HandleNewTradableDate(object sender, DateTime date) + { + emittedExchangeDates.Add(date); + } + + timeKeeper.NewExchangeDate += HandleNewTradableDate; + + var firstDataDate = tradableDates[0]; + var firstDataDateInExchangeTimeZone = firstDataDate.ConvertTo(dataTimeZone, exchangeTimeZone); + var firstExchangeDateIsBeforeFirstDataDate = firstDataDateInExchangeTimeZone < firstDataDate; + + try + { + Assert.IsTrue(timeKeeper.TryAdvanceUntilNextDataDate()); + Assert.AreEqual(1, emittedExchangeDates.Count); + + if (firstExchangeDateIsBeforeFirstDataDate) + { + // The first exchange date is the day before the first data date when the exchange is behind the data + var expectedFirstExchangeDate = firstDataDate.AddDays(-1); + Assert.AreEqual(expectedFirstExchangeDate, emittedExchangeDates[0]); + Assert.AreEqual(expectedFirstExchangeDate.ConvertTo(exchangeTimeZone, dataTimeZone), timeKeeper.DataTime); + } + else + { + // If exchange is ahead of the data, even though technically at the first data date the exchange date hasn't changed, + // we emit the first one so that first daily actions are performed (mappings, delistings, etc). + Assert.AreEqual(firstDataDate, emittedExchangeDates[0]); + Assert.AreEqual(firstDataDate, timeKeeper.DataTime); + } + } + finally + { + timeKeeper.NewExchangeDate -= HandleNewTradableDate; + } + } + + [TestCaseSource(nameof(TimeZonesTestCases))] + public void ExchangeDatesAreEmittedByAdvancingToNextDataDate(DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone) + { + using var timeKeeper = GetTimeKeeper(dataTimeZone, exchangeTimeZone, false, null, out var tradableDates, out var config, out var exchangeHours); + + var exchangeDateEmitted = false; + var emittedExchangeDates = new List(); + void HandleNewTradableDate(object sender, DateTime date) + { + emittedExchangeDates.Add(date); + exchangeDateEmitted = true; + } + + timeKeeper.NewExchangeDate += HandleNewTradableDate; + + var expectedExchangeDates = new List() + { + new(2024, 10, 1), + new(2024, 10, 2), + new(2024, 10, 3), + new(2024, 10, 4), + new(2024, 10, 7), + new(2024, 10, 8), + new(2024, 10, 9), + new(2024, 10, 10), + new(2024, 10, 11), + }; + + var firstDataDate = tradableDates[0]; + var firstDataDateInExchangeTimeZone = firstDataDate.ConvertTo(dataTimeZone, exchangeTimeZone); + var exchangeIsBehindData = firstDataDateInExchangeTimeZone < firstDataDate; + if (exchangeIsBehindData) + { + expectedExchangeDates.Insert(0, new(2024, 9, 30)); + expectedExchangeDates.RemoveAt(expectedExchangeDates.Count - 1); + } + + try + { + // Flush first date: + Assert.IsTrue(timeKeeper.TryAdvanceUntilNextDataDate()); + Assert.IsTrue(exchangeDateEmitted); + + for (var i = 1; i < tradableDates.Count; i++) + { + exchangeDateEmitted = false; + Assert.IsTrue(timeKeeper.TryAdvanceUntilNextDataDate()); + + if (timeKeeper.DataTime == timeKeeper.ExchangeTime) + { + Assert.AreEqual(tradableDates[i], timeKeeper.DataTime); + Assert.AreEqual(tradableDates[i], timeKeeper.ExchangeTime); + Assert.IsTrue(exchangeDateEmitted); + Assert.AreEqual(tradableDates[i], emittedExchangeDates[^1]); + } + else + { + if (exchangeIsBehindData) + { + Assert.IsFalse(exchangeDateEmitted); + Assert.AreEqual(tradableDates[i - 1], timeKeeper.DataTime); + } + else + { + Assert.IsTrue(exchangeDateEmitted); + Assert.AreEqual(emittedExchangeDates[^1], timeKeeper.ExchangeTime); + } + + // Move again to the next data date or next exchange date + exchangeDateEmitted = false; + Assert.IsTrue(timeKeeper.TryAdvanceUntilNextDataDate()); + + if (exchangeIsBehindData) + { + Assert.IsTrue(exchangeDateEmitted); + Assert.AreEqual(emittedExchangeDates[^1], timeKeeper.ExchangeTime); + } + else + { + Assert.IsFalse(exchangeDateEmitted); + Assert.AreEqual(tradableDates[i], timeKeeper.DataTime); + } + } + } + + CollectionAssert.AreEqual(expectedExchangeDates, emittedExchangeDates); + } + finally + { + timeKeeper.NewExchangeDate -= HandleNewTradableDate; + } + } + + [TestCaseSource(nameof(TimeZonesTestCases))] + public void ExchangeDatesAreEmittedByAdvancingToNextGivenExchangeTime(DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone) + { + using var timeKeeper = GetTimeKeeper(dataTimeZone, exchangeTimeZone, false, null, out var tradableDates, out var config, out var exchangeHours); + + var exchangeDateEmitted = false; + var emittedExchangeDates = new List(); + void HandleNewTradableDate(object sender, DateTime date) + { + emittedExchangeDates.Add(date); + exchangeDateEmitted = true; + } + + timeKeeper.NewExchangeDate += HandleNewTradableDate; + + var expectedExchangeDates = new List() + { + new(2024, 10, 1), + new(2024, 10, 2), + new(2024, 10, 3), + new(2024, 10, 4), + new(2024, 10, 7), + new(2024, 10, 8), + new(2024, 10, 9), + new(2024, 10, 10), + new(2024, 10, 11), + }; + + var firstDataDate = tradableDates[0]; + var firstDataDateInExchangeTimeZone = firstDataDate.ConvertTo(dataTimeZone, exchangeTimeZone); + var exchangeIsBehindData = firstDataDateInExchangeTimeZone < firstDataDate; + if (exchangeIsBehindData) + { + expectedExchangeDates.Insert(0, new(2024, 9, 30)); + expectedExchangeDates.RemoveAt(expectedExchangeDates.Count - 1); + } + + try + { + // Flush first date: + Assert.IsTrue(timeKeeper.TryAdvanceUntilNextDataDate()); + Assert.IsTrue(exchangeDateEmitted); + + // Using multiple step sizes to simulate data coming in with gaps + var steps = new Queue(); + steps.Enqueue(TimeSpan.FromMinutes(1)); + steps.Enqueue(TimeSpan.FromMinutes(5)); + steps.Enqueue(TimeSpan.FromMinutes(30)); + steps.Enqueue(TimeSpan.FromHours(1)); + + while (emittedExchangeDates.Count != expectedExchangeDates.Count) + { + exchangeDateEmitted = false; + var currentExchangeTime = timeKeeper.ExchangeTime; + var step = steps.Dequeue(); + steps.Enqueue(step); + var nextExchangeTime = currentExchangeTime + step; + timeKeeper.AdvanceTowardsExchangeTime(nextExchangeTime); + + if (nextExchangeTime.Date != currentExchangeTime.Date && + exchangeHours.IsDateOpen(nextExchangeTime.Date, config.ExtendedMarketHours)) + { + Assert.IsTrue(exchangeDateEmitted); + Assert.AreEqual(emittedExchangeDates[^1], nextExchangeTime.Date); + Assert.AreEqual(timeKeeper.ExchangeTime, nextExchangeTime.Date); + } + else + { + Assert.IsFalse(exchangeDateEmitted); + Assert.AreEqual(timeKeeper.ExchangeTime, nextExchangeTime); + } + } + + CollectionAssert.AreEqual(expectedExchangeDates, emittedExchangeDates); + } + finally + { + timeKeeper.NewExchangeDate -= HandleNewTradableDate; + } + } + + [TestCaseSource(nameof(TimeZonesTestCases))] + public void DoesNotAdvancePastDelistingDateWhenAdvancingToNextDataDate(DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone) + { + var delistingDate = _start.AddDays(7); + using var timeKeeper = GetTimeKeeper(dataTimeZone, exchangeTimeZone, false, delistingDate, out var _, out var _, out var _); + + while (timeKeeper.TryAdvanceUntilNextDataDate()) + { + Assert.LessOrEqual(timeKeeper.ExchangeTime.Date, delistingDate.AddDays(1)); + } + } + + [TestCaseSource(nameof(TimeZonesTestCases))] + public void ThrowsIfNotInitialized(DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone) + { + using var timeKeeper = GetTimeKeeper(dataTimeZone, exchangeTimeZone, false, null, out var _, out var _, out var _); + + Assert.Throws(() => timeKeeper.AdvanceTowardsExchangeTime(timeKeeper.ExchangeTime.AddHours(1))); + } + + [TestCaseSource(nameof(TimeZonesTestCases))] + public void FinishesRightAwayIfThereAreNoTradableDates(DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone) + { + using var timeKeeper = GetNoDatesTimeKeeper(dataTimeZone, exchangeTimeZone); + + Assert.IsFalse(timeKeeper.TryAdvanceUntilNextDataDate()); + } + + private static DateChangeTimeKeeper GetTimeKeeper(DateTimeZone dataTimeZone, + DateTimeZone exchangeTimeZone, + bool exchangeAlwaysOpen, + DateTime? delistingDate, + out List tradableDates, + out SubscriptionDataConfig config, + out SecurityExchangeHours exchangeHours) + { + var symbol = Symbols.SPY; + exchangeHours = GetMarketHours(symbol, exchangeTimeZone, exchangeAlwaysOpen); + config = new SubscriptionDataConfig(typeof(TradeBar), + symbol, + Resolution.Minute, + dataTimeZone, + exchangeTimeZone, + false, + true, + false); + + tradableDates = Time.EachTradeableDayInTimeZone(exchangeHours, + _start, + _end, + config.DataTimeZone, + config.ExtendedMarketHours).ToList(); + return new DateChangeTimeKeeper(tradableDates, config, exchangeHours, delistingDate ?? symbol.GetDelistingDate()); + } + + private static DateChangeTimeKeeper GetNoDatesTimeKeeper(DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone) + { + var symbol = Symbols.SPY; + var exchangeHours = GetMarketHours(symbol, exchangeTimeZone, true); + var config = new SubscriptionDataConfig(typeof(TradeBar), + symbol, + Resolution.Minute, + dataTimeZone, + exchangeTimeZone, + false, + true, + false); + + var tradableDates = Enumerable.Empty().ToList(); + return new DateChangeTimeKeeper(tradableDates, config, exchangeHours, symbol.GetDelistingDate()); + } + + private static SecurityExchangeHours GetMarketHours(Symbol symbol, DateTimeZone exchangeTimeZone, bool alwaysOpen) + { + SecurityExchangeHours exchangeHours; + if (alwaysOpen) + { + exchangeHours = SecurityExchangeHours.AlwaysOpen(exchangeTimeZone); + } + else + { + var databaseExchangeHours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType); + exchangeHours = new SecurityExchangeHours(exchangeTimeZone, + databaseExchangeHours.Holidays, + databaseExchangeHours.MarketHours.ToDictionary(), + databaseExchangeHours.EarlyCloses, + databaseExchangeHours.LateOpens); + } + + return exchangeHours; + } + } +} diff --git a/Tests/Engine/DataFeeds/FileSystemDataFeedTests.cs b/Tests/Engine/DataFeeds/FileSystemDataFeedTests.cs index 821b0b058b15..7516bf8dce9a 100644 --- a/Tests/Engine/DataFeeds/FileSystemDataFeedTests.cs +++ b/Tests/Engine/DataFeeds/FileSystemDataFeedTests.cs @@ -15,6 +15,7 @@ */ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; @@ -289,5 +290,56 @@ public void FutureChainEnumerator(bool fillForward) // 2 days worth of minute data Assert.AreEqual(24 * 2 * 60 + 1, count); } + + [Test] + public void ContinuousFutureUniverseSelectionIsPerformedOnExtendedMarketHoursDates([Values] bool extendedMarketHours) + { + var job = new BacktestNodePacket(); + var resultHandler = new BacktestingResultHandler(); + var feed = new FileSystemDataFeed(); + var algorithm = new AlgorithmStub(feed); + algorithm.Transactions.SetOrderProcessor(new FakeOrderProcessor()); + algorithm.SetStartDate(new DateTime(2019, 08, 01)); + algorithm.SetEndDate(new DateTime(2019, 08, 08)); + + var dataPermissionManager = new DataPermissionManager(); + using var synchronizer = new Synchronizer(); + synchronizer.Initialize(algorithm, algorithm.DataManager); + + feed.Initialize(algorithm, job, resultHandler, TestGlobals.MapFileProvider, TestGlobals.FactorFileProvider, TestGlobals.DataProvider, + algorithm.DataManager, synchronizer, dataPermissionManager.DataChannelProvider); + var future = algorithm.AddFuture("GC", Resolution.Daily, extendedMarketHours: extendedMarketHours); + algorithm.PostInitialize(); + + var addedSecurities = new HashSet(); + var mappingCounts = 0; + + using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + foreach (var timeSlice in synchronizer.StreamData(cancellationTokenSource.Token)) + { + if (timeSlice.IsTimePulse) continue; + + var addedSymbols = timeSlice.SecurityChanges.AddedSecurities.Select(x => x.Symbol).ToHashSet(); + + if (timeSlice.Slice.SymbolChangedEvents.TryGetValue(future.Symbol, out var symbolChangedEvent)) + { + mappingCounts++; + var oldSymbol = algorithm.Symbol(symbolChangedEvent.OldSymbol); + var newSymbol = algorithm.Symbol(symbolChangedEvent.NewSymbol); + + Assert.IsTrue(addedSecurities.Contains(oldSymbol)); + + Assert.IsTrue(addedSymbols.Contains(newSymbol)); + } + + addedSecurities.UnionWith(addedSymbols); + } + + feed.Exit(); + algorithm.DataManager.RemoveAllSubscriptions(); + + var expectedMappingCounts = extendedMarketHours ? 2 : 1; + Assert.AreEqual(expectedMappingCounts, mappingCounts); + } } }