From 381660a3ad20ca7d3bee87d2004230f1b6e6baf7 Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Tue, 12 May 2026 02:15:33 -0500 Subject: [PATCH 1/5] Fix SessionConsolidator skipping daily bars on manual Update --- .../Data/Consolidators/SessionConsolidator.cs | 6 ++++- Tests/Indicators/SessionTests.cs | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Common/Data/Consolidators/SessionConsolidator.cs b/Common/Data/Consolidators/SessionConsolidator.cs index 564f444dadd9..9bb16accc473 100644 --- a/Common/Data/Consolidators/SessionConsolidator.cs +++ b/Common/Data/Consolidators/SessionConsolidator.cs @@ -90,7 +90,11 @@ public override void Update(BaseData data) { if (!_initialized) { - _workingBar.Time = data.Time.Date; + // Set the working bar time only if it hasn't been set yet + if (_workingBar.Time == DateTime.MaxValue) + { + _workingBar.Time = data.Time.Date; + } _initialized = true; } base.Update(data); diff --git a/Tests/Indicators/SessionTests.cs b/Tests/Indicators/SessionTests.cs index 3a12e3674286..3d840a3e74cd 100644 --- a/Tests/Indicators/SessionTests.cs +++ b/Tests/Indicators/SessionTests.cs @@ -171,6 +171,28 @@ 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); + } + private static Session GetSession(TickType tickType, int initialSize) { var symbol = Symbols.SPY; From a8ff6302994c5b24f40c4d5886156a37354d40bb Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Tue, 12 May 2026 16:45:39 -0500 Subject: [PATCH 2/5] Fix SessionConsolidator bar timestamp when no data arrives for a trading day --- .../Data/Consolidators/SessionConsolidator.cs | 6 ++++++ Tests/Indicators/SessionTests.cs | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/Common/Data/Consolidators/SessionConsolidator.cs b/Common/Data/Consolidators/SessionConsolidator.cs index 9bb16accc473..96edc2e99c94 100644 --- a/Common/Data/Consolidators/SessionConsolidator.cs +++ b/Common/Data/Consolidators/SessionConsolidator.cs @@ -78,6 +78,12 @@ protected override void AggregateBar(ref SessionBar workingBar, BaseData data) return; } + // Correct the timestamp if data skipped the predicted next trading day + if (workingBar.Time != DateTime.MaxValue && data.Time.Date > workingBar.Time.Date) + { + workingBar.Time = data.Time.Date; + } + // Update the working session bar workingBar.Update(data, Consolidated); } diff --git a/Tests/Indicators/SessionTests.cs b/Tests/Indicators/SessionTests.cs index 3d840a3e74cd..825b8fab8de5 100644 --- a/Tests/Indicators/SessionTests.cs +++ b/Tests/Indicators/SessionTests.cs @@ -193,6 +193,27 @@ public void ManualDailyBarUpdateProducesOneConsolidationPerBar() 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; From c14dabaf9a4f87d9399089e75c9185c4f3c19050 Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Thu, 14 May 2026 11:21:27 -0500 Subject: [PATCH 3/5] Address review comments --- Common/Data/Consolidators/SessionConsolidator.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Common/Data/Consolidators/SessionConsolidator.cs b/Common/Data/Consolidators/SessionConsolidator.cs index 96edc2e99c94..ac97f09aa909 100644 --- a/Common/Data/Consolidators/SessionConsolidator.cs +++ b/Common/Data/Consolidators/SessionConsolidator.cs @@ -78,8 +78,7 @@ protected override void AggregateBar(ref SessionBar workingBar, BaseData data) return; } - // Correct the timestamp if data skipped the predicted next trading day - if (workingBar.Time != DateTime.MaxValue && data.Time.Date > workingBar.Time.Date) + if (workingBar.Time == DateTime.MaxValue || data.Time.Date > workingBar.Time.Date) { workingBar.Time = data.Time.Date; } @@ -96,11 +95,6 @@ public override void Update(BaseData data) { if (!_initialized) { - // Set the working bar time only if it hasn't been set yet - if (_workingBar.Time == DateTime.MaxValue) - { - _workingBar.Time = data.Time.Date; - } _initialized = true; } base.Update(data); From 55a5c2f61141c7e73300e9647b8d8ac062174b45 Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Thu, 14 May 2026 11:54:05 -0500 Subject: [PATCH 4/5] Minor fix --- Common/Data/Consolidators/SessionConsolidator.cs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/Common/Data/Consolidators/SessionConsolidator.cs b/Common/Data/Consolidators/SessionConsolidator.cs index ac97f09aa909..88010b3f8752 100644 --- a/Common/Data/Consolidators/SessionConsolidator.cs +++ b/Common/Data/Consolidators/SessionConsolidator.cs @@ -83,23 +83,12 @@ protected override void AggregateBar(ref SessionBar workingBar, BaseData data) workingBar.Time = data.Time.Date; } + _initialized = true; + // Update the working session bar 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) - { - _initialized = true; - } - base.Update(data); - } - /// /// Validates the current local time and triggers Scan() if a new day is detected. /// From f28b5e14b0d7faccb00e9de1709e9ad6f0dbc4c4 Mon Sep 17 00:00:00 2001 From: Josue Nina Date: Fri, 15 May 2026 01:26:05 -0500 Subject: [PATCH 5/5] Move initialization logic to start of AggregateBar --- Common/Data/Consolidators/SessionConsolidator.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Common/Data/Consolidators/SessionConsolidator.cs b/Common/Data/Consolidators/SessionConsolidator.cs index 88010b3f8752..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) { @@ -78,13 +87,6 @@ protected override void AggregateBar(ref SessionBar workingBar, BaseData data) return; } - if (workingBar.Time == DateTime.MaxValue || data.Time.Date > workingBar.Time.Date) - { - workingBar.Time = data.Time.Date; - } - - _initialized = true; - // Update the working session bar workingBar.Update(data, Consolidated); }