diff --git a/Common/Data/Consolidators/SessionConsolidator.cs b/Common/Data/Consolidators/SessionConsolidator.cs index 564f444dadd9..efd416db6529 100644 --- a/Common/Data/Consolidators/SessionConsolidator.cs +++ b/Common/Data/Consolidators/SessionConsolidator.cs @@ -65,6 +65,15 @@ public SessionConsolidator(SecurityExchangeHours exchangeHours, TickType sourceT /// The new data protected override void AggregateBar(ref SessionBar workingBar, BaseData data) { + if (!_initialized) + { + if (workingBar.Time == DateTime.MaxValue || data.Time.Date > workingBar.Time.Date) + { + workingBar.Time = data.Time.Date; + } + _initialized = true; + } + // Handle open interest if (data.DataType == MarketDataType.Tick && data is Tick oiTick && oiTick.TickType == TickType.OpenInterest) { @@ -82,20 +91,6 @@ protected override void AggregateBar(ref SessionBar workingBar, BaseData data) workingBar.Update(data, Consolidated); } - /// - /// Updates the session with new market data and initializes the consolidator if needed - /// - /// The new data to update the session with - public override void Update(BaseData data) - { - if (!_initialized) - { - _workingBar.Time = data.Time.Date; - _initialized = true; - } - base.Update(data); - } - /// /// Validates the current local time and triggers Scan() if a new day is detected. /// diff --git a/Tests/Indicators/SessionTests.cs b/Tests/Indicators/SessionTests.cs index 3a12e3674286..825b8fab8de5 100644 --- a/Tests/Indicators/SessionTests.cs +++ b/Tests/Indicators/SessionTests.cs @@ -171,6 +171,49 @@ private static IEnumerable NextSessionTradingDayCases() yield return new TestCaseData(new DateTime(2025, 8, 29, 10, 0, 0), new DateTime(2025, 9, 2)); } + [Test] + public void ManualDailyBarUpdateProducesOneConsolidationPerBar() + { + var symbol = Symbols.SPY; + var barCount = 20; + var exchangeHours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType); + var session = new Session(TickType.Trade, exchangeHours, symbol, barCount + 1); + + var barDate = new DateTime(2025, 9, 2, 9, 30, 0); + for (var i = 0; i < barCount; i++) + { + session.Update(new TradeBar(barDate, symbol, 100 + i, 101 + i, 99 + i, 100 + i, 1000, Time.OneDay)); + barDate = barDate.AddDays(1); + while (!exchangeHours.IsDateOpen(barDate.Date, false)) + { + barDate = barDate.AddDays(1); + } + } + + Assert.AreEqual(barCount, session.Samples); + } + + [Test] + public void GapDayDataPreservesCorrectTimestampAndContent() + { + var symbol = Symbols.SPY; + var exchangeHours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType); + var session = new Session(TickType.Trade, exchangeHours, symbol, 5); + + var sep2 = new DateTime(2025, 9, 2, 9, 30, 0); + var sep4 = new DateTime(2025, 9, 4, 9, 30, 0); + var sep5 = new DateTime(2025, 9, 5, 9, 30, 0); + + session.Update(new TradeBar(sep2, symbol, 100, 110, 90, 105, 1000, Time.OneDay)); + session.Update(new TradeBar(sep4, symbol, 200, 210, 190, 205, 2000, Time.OneDay)); + session.Update(new TradeBar(sep5, symbol, 300, 310, 290, 305, 3000, Time.OneDay)); + + Assert.AreEqual(sep4.Date, session[1].Time); + Assert.AreEqual(200, session[1].Open); + Assert.AreEqual(sep2.Date, session[2].Time); + Assert.AreEqual(100, session[2].Open); + } + private static Session GetSession(TickType tickType, int initialSize) { var symbol = Symbols.SPY;