Skip to content

Commit 2996695

Browse files
committed
Align OnWarmupFinished time to StartDate when ScheduledUniverse skips midnight
1 parent 3f5eefd commit 2996695

2 files changed

Lines changed: 122 additions & 0 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Collections.Generic;
18+
using QuantConnect.Algorithm.Framework.Selection;
19+
using QuantConnect.Interfaces;
20+
21+
namespace QuantConnect.Algorithm.CSharp
22+
{
23+
/// <summary>
24+
/// Regression algorithm asserting OnWarmupFinished fires at StartDate (midnight)
25+
/// when using a ScheduledUniverseSelectionModel that triggers at 8 AM, skipping midnight entirely.
26+
/// </summary>
27+
public class OnWarmupFinishedScheduledUniverseRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
28+
{
29+
private bool _onWarmupFinishedCalled;
30+
31+
public override void Initialize()
32+
{
33+
SetStartDate(2013, 10, 08);
34+
SetEndDate(2013, 10, 11);
35+
SetCash(100000);
36+
37+
UniverseSettings.Resolution = Resolution.Minute;
38+
SetWarmup(TimeSpan.FromDays(1));
39+
40+
// Universe triggers at 8 AM
41+
SetUniverseSelection(new ScheduledUniverseSelectionModel(
42+
DateRules.EveryDay(),
43+
TimeRules.At(8, 0),
44+
_ => new[] { QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA) }
45+
));
46+
}
47+
48+
public override void OnWarmupFinished()
49+
{
50+
_onWarmupFinishedCalled = true;
51+
52+
if (Time != StartDate)
53+
{
54+
throw new RegressionTestException(
55+
$"Expected OnWarmupFinished to fire at StartDate ({StartDate:yyyy-MM-dd HH:mm:ss}), " +
56+
$"but fired at {Time:yyyy-MM-dd HH:mm:ss}");
57+
}
58+
}
59+
60+
public override void OnEndOfAlgorithm()
61+
{
62+
if (!_onWarmupFinishedCalled)
63+
{
64+
throw new RegressionTestException("OnWarmupFinished was never called");
65+
}
66+
}
67+
68+
public bool CanRunLocally { get; } = true;
69+
70+
public List<Language> Languages { get; } = new() { Language.CSharp };
71+
72+
public long DataPoints => 3948;
73+
74+
public int AlgorithmHistoryDataPoints => 0;
75+
76+
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
77+
78+
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
79+
{
80+
{"Total Orders", "0"},
81+
{"Average Win", "0%"},
82+
{"Average Loss", "0%"},
83+
{"Compounding Annual Return", "0%"},
84+
{"Drawdown", "0%"},
85+
{"Expectancy", "0"},
86+
{"Start Equity", "100000"},
87+
{"End Equity", "100000"},
88+
{"Net Profit", "0%"},
89+
{"Sharpe Ratio", "0"},
90+
{"Sortino Ratio", "0"},
91+
{"Probabilistic Sharpe Ratio", "0%"},
92+
{"Loss Rate", "0%"},
93+
{"Win Rate", "0%"},
94+
{"Profit-Loss Ratio", "0"},
95+
{"Alpha", "0"},
96+
{"Beta", "0"},
97+
{"Annual Standard Deviation", "0"},
98+
{"Annual Variance", "0"},
99+
{"Information Ratio", "-31.448"},
100+
{"Tracking Error", "0.164"},
101+
{"Treynor Ratio", "0"},
102+
{"Total Fees", "$0.00"},
103+
{"Estimated Strategy Capacity", "$0"},
104+
{"Lowest Capacity Asset", ""},
105+
{"Portfolio Turnover", "0%"},
106+
{"Drawdown Recovery", "0"},
107+
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
108+
};
109+
}
110+
}

Engine/AlgorithmManager.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,18 @@ private IEnumerable<TimeSlice> Stream(IAlgorithm algorithm, ISynchronizer synchr
727727
{
728728
// warmup finished, send an update
729729
warmingUp = false;
730+
731+
// Align time to StartDate so OnWarmupFinished always fires at midnight,
732+
// even when the first post warmup slice arrives later (e.g. a ScheduledUniverse at 8 AM).
733+
if (!algorithm.LiveMode)
734+
{
735+
var warmupEndUtc = algorithm.StartDate.ConvertToUtc(algorithm.TimeZone);
736+
if (algorithm.UtcTime > warmupEndUtc)
737+
{
738+
algorithm.SetDateTime(warmupEndUtc);
739+
}
740+
}
741+
730742
// we trigger this callback here and not internally in the algorithm so that we can go through python if required
731743
algorithm.OnWarmupFinished();
732744
algorithm.Debug("Algorithm finished warming up.");

0 commit comments

Comments
 (0)