Skip to content

Commit 6349d5f

Browse files
committed
Fix chart trimming breaking live trading statistics
1 parent 261366a commit 6349d5f

2 files changed

Lines changed: 82 additions & 16 deletions

File tree

Engine/Results/LiveTradingResultHandler.cs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -336,21 +336,7 @@ private void Update()
336336
if (utcNow > _nextChartTrimming)
337337
{
338338
Log.Debug("LiveTradingResultHandler.Update(): Trimming charts");
339-
var timeLimitUtc = utcNow.AddDays(-2);
340-
lock (ChartLock)
341-
{
342-
foreach (var chart in Charts)
343-
{
344-
foreach (var series in chart.Value.Series)
345-
{
346-
// trim data that's older than 2 days
347-
series.Value.Values =
348-
(from v in series.Value.Values
349-
where v.Time > timeLimitUtc
350-
select v).ToList();
351-
}
352-
}
353-
}
339+
TrimCharts(utcNow);
354340
_nextChartTrimming = DateTime.UtcNow.AddMinutes(10);
355341
Log.Debug("LiveTradingResultHandler.Update(): Finished trimming charts");
356342
}
@@ -382,6 +368,30 @@ protected virtual void SetNextStatusUpdate()
382368
_nextStatusUpdate = DateTime.UtcNow.AddMinutes(10);
383369
}
384370

371+
/// <summary>
372+
/// Removes chart series points older than their retention window: 10 days for performance charts, 2 days for all others.
373+
/// </summary>
374+
protected virtual void TrimCharts(DateTime utcNow)
375+
{
376+
lock (ChartLock)
377+
{
378+
foreach (var chart in Charts)
379+
{
380+
var timeLimitUtc = AlgorithmPerformanceCharts.Contains(chart.Key)
381+
? utcNow.AddDays(-10)
382+
: utcNow.AddDays(-2);
383+
384+
foreach (var series in chart.Value.Series)
385+
{
386+
series.Value.Values =
387+
(from v in series.Value.Values
388+
where v.Time > timeLimitUtc
389+
select v).ToList();
390+
}
391+
}
392+
}
393+
}
394+
385395
/// <summary>
386396
/// Stores the order events
387397
/// </summary>

Tests/Engine/Results/LiveTradingResultHandlerTests.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public void DailySampleValueBasedOnMarketHour(bool extendedMarketHoursEnabled)
149149
using var messagging = new QuantConnect.Messaging.Messaging();
150150
var referenceDate = new DateTime(2020, 11, 25);
151151
var resultHandler = new LiveTradingResultHandler();
152-
resultHandler.Initialize(new (new LiveNodePacket(), messagging, api, new BacktestingTransactionHandler(), null));
152+
resultHandler.Initialize(new(new LiveNodePacket(), messagging, api, new BacktestingTransactionHandler(), null));
153153

154154
try
155155
{
@@ -190,6 +190,62 @@ public void DailySampleValueBasedOnMarketHour(bool extendedMarketHoursEnabled)
190190
}
191191
}
192192

193+
[Test]
194+
public void TrimChartsUsesLongerWindowForPerformanceCharts()
195+
{
196+
var handler = new TestableLiveTradingResultHandler();
197+
var utcNow = new DateTime(2020, 11, 25, 12, 0, 0, DateTimeKind.Utc);
198+
199+
var benchmarkChart = new Chart(BaseResultsHandler.BenchmarkKey);
200+
benchmarkChart.Series.Add(BaseResultsHandler.BenchmarkKey, new Series(BaseResultsHandler.BenchmarkKey));
201+
handler.Charts[BaseResultsHandler.BenchmarkKey] = benchmarkChart;
202+
203+
// Add a custom user chart to verify it still uses the 2 day window
204+
var customChart = new Chart("MyCustomChart");
205+
customChart.Series.Add("MyMetric", new Series("MyMetric"));
206+
handler.Charts["MyCustomChart"] = customChart;
207+
208+
var returnSeries = handler.Charts[BaseResultsHandler.StrategyEquityKey].Series[BaseResultsHandler.ReturnKey];
209+
var equitySeries = handler.Charts[BaseResultsHandler.StrategyEquityKey].Series[BaseResultsHandler.EquityKey];
210+
var benchmarkSeries = benchmarkChart.Series[BaseResultsHandler.BenchmarkKey];
211+
var customSeries = customChart.Series["MyMetric"];
212+
213+
// performance charts: 15 daily samples covering well beyond both trim windows
214+
for (var i = 15; i >= 1; i--)
215+
{
216+
var t = utcNow.AddDays(-i);
217+
returnSeries.Values.Add(new ChartPoint(t, i));
218+
benchmarkSeries.Values.Add(new ChartPoint(t, i));
219+
equitySeries.Values.Add(new Candlestick(t, 100, 110, 90, 105));
220+
}
221+
222+
// custom chart: 5 samples to verify it uses the 2-day window
223+
for (var i = 5; i >= 1; i--)
224+
{
225+
customSeries.Values.Add(new ChartPoint(utcNow.AddDays(-i), i));
226+
}
227+
228+
handler.PublicTrimCharts(utcNow);
229+
230+
// performance charts keep 10 day window
231+
var performanceChartsCutoff = utcNow.AddDays(-10);
232+
Assert.IsTrue(returnSeries.Values.All(v => v.Time > performanceChartsCutoff));
233+
Assert.IsTrue(benchmarkSeries.Values.All(v => v.Time > performanceChartsCutoff));
234+
Assert.IsTrue(equitySeries.Values.All(v => v.Time > performanceChartsCutoff));
235+
Assert.AreEqual(9, returnSeries.Values.Count);
236+
Assert.AreEqual(9, benchmarkSeries.Values.Count);
237+
238+
// other charts keep 2 day window
239+
var otherChartsCutoff = utcNow.AddDays(-2);
240+
Assert.IsTrue(customSeries.Values.All(v => v.Time > otherChartsCutoff));
241+
Assert.AreEqual(1, customSeries.Values.Count);
242+
}
243+
244+
private class TestableLiveTradingResultHandler : LiveTradingResultHandler
245+
{
246+
public void PublicTrimCharts(DateTime utcNow) => TrimCharts(utcNow);
247+
}
248+
193249
private class TestDataFeed : IDataFeed
194250
{
195251
public bool IsActive { get; }

0 commit comments

Comments
 (0)