From a27d5ca2f9e4c140ed3bf32c92bb26be0cf11d92 Mon Sep 17 00:00:00 2001 From: Martin Molinero Date: Tue, 21 Apr 2026 10:42:42 -0300 Subject: [PATCH] Minor fix for schedule universe to always be 8 am ny - Avoid schedule universe running 7 or 8 NY depending on daylight savings, now will always run 8 am NY. Adding unit tests --- Engine/DataFeeds/LiveTradingDataFeed.cs | 25 ++++++++++++++----- .../DataFeeds/LiveTradingDataFeedTests.cs | 11 ++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Engine/DataFeeds/LiveTradingDataFeed.cs b/Engine/DataFeeds/LiveTradingDataFeed.cs index 4a4c829f3405..b6bf32b3f71c 100644 --- a/Engine/DataFeeds/LiveTradingDataFeed.cs +++ b/Engine/DataFeeds/LiveTradingDataFeed.cs @@ -55,13 +55,12 @@ public class LiveTradingDataFeed : FileSystemDataFeed private SubscriptionCollection _subscriptions; private IFactorFileProvider _factorFileProvider; private IDataChannelProvider _channelProvider; - // in live trading we delay scheduled universe selection between 11 & 12 hours after midnight UTC so that we allow new selection data to be piped in - // NY goes from -4/-5 UTC time, so: - // 11 UTC - 4 => 7am NY - // 12 UTC - 4 => 8am NY - private readonly TimeSpan _scheduledUniverseUtcTimeShift = TimeSpan.FromMinutes(11 * 60 + DateTime.UtcNow.Second); private readonly HashSet _unsupportedConfigurations = new(); + // in live trading we delay scheduled universe selection to 8 am NY, but NY goes from -4/-5 UTC time, so we adjust it + private static ReferenceWrapper _lastUtcDateShiftUpdate; + private static ReferenceWrapper _scheduledUniverseUtcTimeShift; + /// /// Public flag indicator that the thread is still busy. /// @@ -389,7 +388,7 @@ request.Universe is OptionChainUniverse || enumerator = AddScheduleWrapper(request, enumerator, new PredicateTimeProvider(_frontierTimeProvider, (currentUtcDateTime) => { // will only let time advance after it's passed the live time shift frontier - return currentUtcDateTime.TimeOfDay > _scheduledUniverseUtcTimeShift; + return currentUtcDateTime.TimeOfDay > GetScheduledUniverseUtcTimeShift(currentUtcDateTime); })); enumerator = GetWarmupEnumerator(request, enumerator); @@ -402,6 +401,20 @@ request.Universe is OptionChainUniverse || return subscription; } + public static TimeSpan GetScheduledUniverseUtcTimeShift(DateTime currentUtcDateTime) + { + var currentDate = currentUtcDateTime.Date; + if (currentDate != _lastUtcDateShiftUpdate?.Value) + { + // on every date change, we will update the scheduled time shift to be 8am NY time, which is 12-13 UTC depending on DST + _lastUtcDateShiftUpdate = new(currentDate); + _scheduledUniverseUtcTimeShift = new(currentDate.ConvertFromUtc(TimeZones.NewYork).Date.AddHours(8).ConvertToUtc(TimeZones.NewYork).TimeOfDay + // some minor randomness + + TimeSpan.FromSeconds(DateTime.UtcNow.Second)); + } + return _scheduledUniverseUtcTimeShift.Value; + } + /// /// Build and apply the warmup enumerators when required /// diff --git a/Tests/Engine/DataFeeds/LiveTradingDataFeedTests.cs b/Tests/Engine/DataFeeds/LiveTradingDataFeedTests.cs index 94a39684da83..74c4d2ad2df5 100644 --- a/Tests/Engine/DataFeeds/LiveTradingDataFeedTests.cs +++ b/Tests/Engine/DataFeeds/LiveTradingDataFeedTests.cs @@ -2899,6 +2899,17 @@ public void FillForwardsWarmUpDataToLiveFeed( } } + [TestCase(0, 13)] + [TestCase(2, 13)] + [TestCase(10, 12)] + [TestCase(100, 12)] + [TestCase(280, 13)] + public void UniverseScheduleUtcShitft(int dateShitft, int expectedTimeShift) + { + var result = LiveTradingDataFeed.GetScheduledUniverseUtcTimeShift(new DateTime(2026, 3, 1).AddDays(dateShitft)); + Assert.AreEqual(expectedTimeShift, (int)result.TotalHours); + } + private IDataFeed RunDataFeed(Resolution resolution = Resolution.Second, List equities = null, List forex = null, List crypto = null, Func> getNextTicksFunction = null, Func> lookupSymbolsFunction = null,