diff --git a/Algorithm/QCAlgorithm.Indicators.cs b/Algorithm/QCAlgorithm.Indicators.cs index 3c5c5ea672a7..a0a1982e0da2 100644 --- a/Algorithm/QCAlgorithm.Indicators.cs +++ b/Algorithm/QCAlgorithm.Indicators.cs @@ -3094,7 +3094,7 @@ public void WarmUpIndicator(Symbol symbol, IndicatorBase indicator, TimeSp private IEnumerable GetIndicatorWarmUpHistory(IEnumerable symbols, IIndicator indicator, TimeSpan timeSpan, out bool identityConsolidator) { identityConsolidator = false; - if (AssertIndicatorHasWarmupPeriod(indicator)) + if (!AssertIndicatorHasWarmupPeriod(indicator)) { return Enumerable.Empty(); } @@ -3143,16 +3143,17 @@ private void WarmUpIndicatorImpl(Symbol symbol, TimeSpan period, Action ha where T : class, IBaseData { IDataConsolidator consolidator; - if (identityConsolidator) - { - period = TimeSpan.Zero; - } if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).Count > 0) { consolidator = Consolidate(symbol, period, handler); } else { + if (identityConsolidator) + { + period = TimeSpan.Zero; + } + var providedType = typeof(T); if (providedType.IsAbstract) { diff --git a/Algorithm/QCAlgorithm.Python.cs b/Algorithm/QCAlgorithm.Python.cs index 4b7625494f87..0a22a1c9e823 100644 --- a/Algorithm/QCAlgorithm.Python.cs +++ b/Algorithm/QCAlgorithm.Python.cs @@ -715,6 +715,36 @@ public void WarmUpIndicator(Symbol symbol, PyObject indicator, Resolution? resol WarmUpIndicator(symbol, WrapPythonIndicator(indicator), resolution, selector?.ConvertToDelegate>()); } + /// + /// Warms up a given indicator with historical data + /// + /// The symbol whose indicator we want + /// The indicator we want to warm up + /// The necessary period to warm up the indicator + /// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x) + [DocumentationAttribute(Indicators)] + [DocumentationAttribute(HistoricalData)] + public void WarmUpIndicator(Symbol symbol, PyObject indicator, TimeSpan period, PyObject selector = null) + { + if (indicator.TryConvert(out IndicatorBase indicatorDataPoint)) + { + WarmUpIndicator(symbol, indicatorDataPoint, period, selector?.ConvertToDelegate>()); + return; + } + if (indicator.TryConvert(out IndicatorBase indicatorDataBar)) + { + WarmUpIndicator(symbol, indicatorDataBar, period, selector?.ConvertToDelegate>()); + return; + } + if (indicator.TryConvert(out IndicatorBase indicatorTradeBar)) + { + WarmUpIndicator(symbol, indicatorTradeBar, period, selector?.ConvertToDelegate>()); + return; + } + + WarmUpIndicator(symbol, WrapPythonIndicator(indicator), period, selector?.ConvertToDelegate>()); + } + /// /// Plot a chart using string series name, with value. /// diff --git a/Tests/Algorithm/AlgorithmIndicatorsTests.cs b/Tests/Algorithm/AlgorithmIndicatorsTests.cs index 584d88cc5791..0ad5631e3ca2 100644 --- a/Tests/Algorithm/AlgorithmIndicatorsTests.cs +++ b/Tests/Algorithm/AlgorithmIndicatorsTests.cs @@ -302,6 +302,69 @@ public void IndicatorsPassingHistory(Language language) Assert.IsTrue(indicator.IsReady); } + [Test] + public void PythonIndicatorCanBeWarmedUpWithTimespan() + { + var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA); + var indicator = new SimpleMovingAverage("SMA", 100); + _algorithm.SetDateTime(new DateTime(2013, 10, 11)); + _algorithm.AddEquity(referenceSymbol); + using (Py.GIL()) + { + var pythonIndicator = indicator.ToPython(); + _algorithm.WarmUpIndicator(referenceSymbol, pythonIndicator, TimeSpan.FromMinutes(60)); + Assert.IsTrue(pythonIndicator.GetAttr("is_ready").GetAndDispose()); + Assert.IsTrue(pythonIndicator.GetAttr("samples").GetAndDispose() >= 100); + } + } + + [Test] + public void IndicatorCanBeWarmedUpWithTimespan() + { + var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA); + _algorithm.AddEquity(referenceSymbol); + var indicator = new SimpleMovingAverage("SMA", 100); + _algorithm.SetDateTime(new DateTime(2013, 10, 11)); + _algorithm.WarmUpIndicator(referenceSymbol, indicator, TimeSpan.FromMinutes(60)); + Assert.IsTrue(indicator.IsReady); + Assert.IsTrue(indicator.Samples >= 100); + } + + [Test] + public void PythonCustomIndicatorCanBeWarmedUpWithTimespan() + { + var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA); + _algorithm.AddEquity(referenceSymbol); + _algorithm.SetDateTime(new DateTime(2013, 10, 11)); + using (Py.GIL()) + { + var testModule = PyModule.FromString("testModule", + @" +from AlgorithmImports import * +from collections import deque + +class CustomSimpleMovingAverage(PythonIndicator): + def __init__(self, name, period): + super().__init__() + self.warm_up_period = period + self.name = name + self.value = 0 + self.queue = deque(maxlen=period) + + # Update method is mandatory + def update(self, input): + self.queue.appendleft(input.value) + count = len(self.queue) + self.value = np.sum(self.queue) / count + return count == self.queue.maxlen"); + + var customIndicator = testModule.GetAttr("CustomSimpleMovingAverage").Invoke("custom".ToPython(), 100.ToPython()); + _algorithm.WarmUpIndicator(referenceSymbol, customIndicator, TimeSpan.FromMinutes(60)); + Assert.IsTrue(customIndicator.GetAttr("is_ready").GetAndDispose()); + Assert.IsTrue(customIndicator.GetAttr("samples").GetAndDispose() >= 100); + } + } + [TestCase("count")] [TestCase("StartAndEndDate")] public void IndicatorUpdatedWithSymbol(string testCase)