From d3e44f00d08b6e05fc1209d00964d0d84fa7538f Mon Sep 17 00:00:00 2001 From: DG47 Date: Thu, 1 Oct 2026 16:52:51 -0400 Subject: [PATCH] Allow RelativeDailyVolume to be warmed up through a user-defined WarmUpPeriod RelativeDailyVolume did not implement IIndicatorWarmUpPeriodProvider, so WarmUpIndicator always skipped it with a warning and there was no way to warm it up, even when the user knows how many bars are needed for the resolution they feed it with. The indicator now implements the interface with a settable WarmUpPeriod that defaults to zero, so nothing is warmed up unless the user sets it. The number of data points required still depends on the resolution and on the market hours of the security, which is why it is not derived from the period automatically. Closes #8629 --- Indicators/RelativeDailyVolume.cs | 16 +++++++-- Tests/Indicators/RelativeDailyVolumeTests.cs | 34 ++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) 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() {