diff --git a/Engine/Results/LiveTradingResultHandler.cs b/Engine/Results/LiveTradingResultHandler.cs
index 954c95cd3411..9c8eb35e7315 100644
--- a/Engine/Results/LiveTradingResultHandler.cs
+++ b/Engine/Results/LiveTradingResultHandler.cs
@@ -336,21 +336,7 @@ private void Update()
if (utcNow > _nextChartTrimming)
{
Log.Debug("LiveTradingResultHandler.Update(): Trimming charts");
- var timeLimitUtc = utcNow.AddDays(-2);
- lock (ChartLock)
- {
- foreach (var chart in Charts)
- {
- foreach (var series in chart.Value.Series)
- {
- // trim data that's older than 2 days
- series.Value.Values =
- (from v in series.Value.Values
- where v.Time > timeLimitUtc
- select v).ToList();
- }
- }
- }
+ TrimCharts(utcNow);
_nextChartTrimming = DateTime.UtcNow.AddMinutes(10);
Log.Debug("LiveTradingResultHandler.Update(): Finished trimming charts");
}
@@ -382,6 +368,30 @@ protected virtual void SetNextStatusUpdate()
_nextStatusUpdate = DateTime.UtcNow.AddMinutes(10);
}
+ ///
+ /// Removes chart series points older than their retention window: 10 days for performance charts, 2 days for all others.
+ ///
+ protected virtual void TrimCharts(DateTime utcNow)
+ {
+ lock (ChartLock)
+ {
+ foreach (var chart in Charts)
+ {
+ var timeLimitUtc = AlgorithmPerformanceCharts.Contains(chart.Key)
+ ? utcNow.AddDays(-10)
+ : utcNow.AddDays(-2);
+
+ foreach (var series in chart.Value.Series)
+ {
+ series.Value.Values =
+ (from v in series.Value.Values
+ where v.Time > timeLimitUtc
+ select v).ToList();
+ }
+ }
+ }
+ }
+
///
/// Stores the order events
///
diff --git a/Tests/Engine/Results/LiveTradingResultHandlerTests.cs b/Tests/Engine/Results/LiveTradingResultHandlerTests.cs
index ba3da3aac2d1..08f1c1bb6248 100644
--- a/Tests/Engine/Results/LiveTradingResultHandlerTests.cs
+++ b/Tests/Engine/Results/LiveTradingResultHandlerTests.cs
@@ -149,7 +149,7 @@ public void DailySampleValueBasedOnMarketHour(bool extendedMarketHoursEnabled)
using var messagging = new QuantConnect.Messaging.Messaging();
var referenceDate = new DateTime(2020, 11, 25);
var resultHandler = new LiveTradingResultHandler();
- resultHandler.Initialize(new (new LiveNodePacket(), messagging, api, new BacktestingTransactionHandler(), null));
+ resultHandler.Initialize(new(new LiveNodePacket(), messagging, api, new BacktestingTransactionHandler(), null));
try
{
@@ -190,6 +190,62 @@ public void DailySampleValueBasedOnMarketHour(bool extendedMarketHoursEnabled)
}
}
+ [Test]
+ public void TrimChartsUsesLongerWindowForPerformanceCharts()
+ {
+ var handler = new TestableLiveTradingResultHandler();
+ var utcNow = new DateTime(2020, 11, 25, 12, 0, 0, DateTimeKind.Utc);
+
+ var benchmarkChart = new Chart(BaseResultsHandler.BenchmarkKey);
+ benchmarkChart.Series.Add(BaseResultsHandler.BenchmarkKey, new Series(BaseResultsHandler.BenchmarkKey));
+ handler.Charts[BaseResultsHandler.BenchmarkKey] = benchmarkChart;
+
+ // Add a custom user chart to verify it still uses the 2 day window
+ var customChart = new Chart("MyCustomChart");
+ customChart.Series.Add("MyMetric", new Series("MyMetric"));
+ handler.Charts["MyCustomChart"] = customChart;
+
+ var returnSeries = handler.Charts[BaseResultsHandler.StrategyEquityKey].Series[BaseResultsHandler.ReturnKey];
+ var equitySeries = handler.Charts[BaseResultsHandler.StrategyEquityKey].Series[BaseResultsHandler.EquityKey];
+ var benchmarkSeries = benchmarkChart.Series[BaseResultsHandler.BenchmarkKey];
+ var customSeries = customChart.Series["MyMetric"];
+
+ // performance charts: 15 daily samples covering well beyond both trim windows
+ for (var i = 15; i >= 1; i--)
+ {
+ var t = utcNow.AddDays(-i);
+ returnSeries.Values.Add(new ChartPoint(t, i));
+ benchmarkSeries.Values.Add(new ChartPoint(t, i));
+ equitySeries.Values.Add(new Candlestick(t, 100, 110, 90, 105));
+ }
+
+ // custom chart: 5 samples to verify it uses the 2-day window
+ for (var i = 5; i >= 1; i--)
+ {
+ customSeries.Values.Add(new ChartPoint(utcNow.AddDays(-i), i));
+ }
+
+ handler.PublicTrimCharts(utcNow);
+
+ // performance charts keep 10 day window
+ var performanceChartsCutoff = utcNow.AddDays(-10);
+ Assert.IsTrue(returnSeries.Values.All(v => v.Time > performanceChartsCutoff));
+ Assert.IsTrue(benchmarkSeries.Values.All(v => v.Time > performanceChartsCutoff));
+ Assert.IsTrue(equitySeries.Values.All(v => v.Time > performanceChartsCutoff));
+ Assert.AreEqual(9, returnSeries.Values.Count);
+ Assert.AreEqual(9, benchmarkSeries.Values.Count);
+
+ // other charts keep 2 day window
+ var otherChartsCutoff = utcNow.AddDays(-2);
+ Assert.IsTrue(customSeries.Values.All(v => v.Time > otherChartsCutoff));
+ Assert.AreEqual(1, customSeries.Values.Count);
+ }
+
+ private class TestableLiveTradingResultHandler : LiveTradingResultHandler
+ {
+ public void PublicTrimCharts(DateTime utcNow) => TrimCharts(utcNow);
+ }
+
private class TestDataFeed : IDataFeed
{
public bool IsActive { get; }