diff --git a/Indicators/RelativeDailyVolume.cs b/Indicators/RelativeDailyVolume.cs index cca43f2c9a99..722b19d7da4c 100644 --- a/Indicators/RelativeDailyVolume.cs +++ b/Indicators/RelativeDailyVolume.cs @@ -26,7 +26,7 @@ namespace QuantConnect.Indicators /// /// Current volume from open to current time of day / Average over the past x days from open to current time of day /// - public class RelativeDailyVolume : TradeBarIndicator + public class RelativeDailyVolume : TradeBarIndicator, IIndicatorWarmUpPeriodProvider { private readonly SortedDictionary _relativeData; private readonly Dictionary _currentData; @@ -39,6 +39,18 @@ public class RelativeDailyVolume : TradeBarIndicator /// public override bool IsReady => _days >= _period; + /// + /// Required period, in data points, for the indicator to be ready and fully initialized. + /// + /// + /// This indicator is ready once it has seen period complete days of data, so the number of data points + /// it needs depends on the resolution it is updated with and on the market hours of the security, and it cannot + /// be derived from the period alone. It defaults to zero, which means no warm up is performed. Set it to the + /// number of bars, at the resolution the indicator is updated with, that span the required number of days to + /// have WarmUpIndicator warm it up. + /// + public int WarmUpPeriod { get; set; } + /// /// Initializes a new instance of the RelativeDailyVolume class using the specified period /// @@ -86,7 +98,7 @@ protected override decimal ComputeNextValue(TradeBar input) } _currentData.Clear(); _previousDay = input.Time.Day; - _days += 1; // _days is starting from -1, to reach IsReady => _days == WarmUpPeriod; also means WarmUpPeriod+1 + _days += 1; // _days is starting from -1, to reach IsReady => _days == _period; also means _period+1 days } _currentData[input.Time] = input.Volume; diff --git a/Tests/Indicators/RelativeDailyVolumeTests.cs b/Tests/Indicators/RelativeDailyVolumeTests.cs index 678611e54361..f8fd08d343fd 100644 --- a/Tests/Indicators/RelativeDailyVolumeTests.cs +++ b/Tests/Indicators/RelativeDailyVolumeTests.cs @@ -113,6 +113,40 @@ public override void WarmsUpProperly() Assert.IsFalse(rdv8.IsReady); } + [Test] + public void WarmUpPeriodDefaultsToZeroAndSkipsWarmUp() + { + var algorithm = CreateAlgorithm(); + algorithm.SetDateTime(new DateTime(2013, 10, 11, 15, 0, 0)); + algorithm.AddEquity("SPY", Resolution.Minute); + + var rdv = new RelativeDailyVolume(2); + Assert.AreEqual(0, rdv.WarmUpPeriod); + + algorithm.WarmUpIndicator(Symbols.SPY, rdv, Resolution.Minute); + + Assert.IsFalse(rdv.IsReady); + Assert.AreEqual(0, rdv.Samples); + } + + [TestCase(Resolution.Minute, 3 * 390)] + [TestCase(Resolution.Hour, 3 * 7)] + public void WarmsUpWithUserDefinedWarmUpPeriod(Resolution resolution, int warmUpPeriod) + { + // Regression test for GH #8629: the number of bars needed to warm up this indicator depends on the + // resolution and the market hours, so it is left for the user to define through WarmUpPeriod + var algorithm = CreateAlgorithm(); + algorithm.SetDateTime(new DateTime(2013, 10, 11, 15, 0, 0)); + algorithm.AddEquity("SPY", resolution); + + var rdv = new RelativeDailyVolume(2) { WarmUpPeriod = warmUpPeriod }; + algorithm.WarmUpIndicator(Symbols.SPY, rdv, resolution); + + Assert.IsTrue(rdv.IsReady); + Assert.AreEqual(warmUpPeriod, rdv.Samples); + Assert.AreNotEqual(0m, rdv.Current.Value); + } + [Test] public void UsesMostRecentHistoricalSlotForIntradayGap() {