18 using System.Collections.Generic;
38 private const double Samples = 4000;
39 private const double MinimumSamplePeriod = 4;
42 private DateTime _nextUpdate;
43 private DateTime _nextS3Update;
44 private string _errorMessage;
45 private int _daysProcessedFrontier;
46 private readonly HashSet<string> _chartSeriesExceededDataPoints;
47 private readonly HashSet<string> _chartSeriesCount;
48 private bool _chartSeriesCountExceededError;
58 private DateTime _nextSample;
59 private string _algorithmId;
60 private int _projectId;
75 _chartSeriesExceededDataPoints =
new();
76 _chartSeriesCount =
new();
91 if (_job ==
null)
throw new Exception(
"BacktestingResultHandler.Constructor(): Submitted Job type invalid.");
92 base.Initialize(parameters);
102 protected override void Run()
117 if (
Messages.TryDequeue(out packet))
128 catch (Exception err)
131 Algorithm.SetRuntimeError(err,
"ResultHandler");
134 Log.
Trace(
"BacktestingResultHandler.Run(): Ending Thread...");
140 private void Update()
150 var utcNow = DateTime.UtcNow;
151 if (utcNow <= _nextUpdate || _progressMonitor.
ProcessedDays < _daysProcessedFrontier)
return;
161 _nextUpdate = utcNow.AddSeconds(3);
163 catch (Exception err)
165 Log.
Error(err,
"Can't update variables");
168 var deltaCharts =
new Dictionary<string, Chart>();
170 var performanceCharts =
new Dictionary<string, Chart>();
175 foreach (var kvp
in Charts)
177 var chart = kvp.Value;
180 var updates = chart.GetUpdates();
181 if (!updates.IsEmpty())
183 deltaCharts.Add(chart.Name, updates);
189 performanceCharts[kvp.Key] = chart.Clone();
203 var progress = _progressMonitor.
Progress;
206 if (utcNow > _nextS3Update)
210 const int maxOrders = 100;
217 new Dictionary<string, string>(),
219 new Dictionary<string, AlgorithmPerformance>(),
225 _nextS3Update = DateTime.UtcNow.AddSeconds(30);
229 var splitPackets =
SplitPackets(deltaCharts, deltaOrders, runtimeStatistics, progress, serverStatistics);
231 foreach (var backtestingPacket
in splitPackets)
239 catch (Exception err)
248 public virtual IEnumerable<BacktestResultPacket>
SplitPackets(Dictionary<string, Chart> deltaCharts, Dictionary<int, Order> deltaOrders, SortedDictionary<string, string> runtimeStatistics, decimal progress, Dictionary<string, string> serverStatistics)
251 var splitPackets =
new List<BacktestResultPacket>();
252 foreach (var chart
in deltaCharts.Values)
256 Charts =
new Dictionary<string, Chart>
264 if (deltaOrders.Count > 0)
294 var key = $
"{AlgorithmId}.json";
300 result.Results.Charts.ToDictionary(x => x.Key, x => x.Value.Clone()),
301 result.Results.Orders,
302 result.Results.ProfitLoss,
303 result.Results.Statistics,
304 result.Results.RuntimeStatistics,
305 result.Results.RollingWindow,
307 result.Results.TotalPerformance,
308 result.Results.AlgorithmConfiguration,
309 result.Results.State));
324 Log.
Error(
"BacktestingResultHandler.StoreResult(): Result Null.");
327 catch (Exception err)
340 var endTime = DateTime.UtcNow;
346 var charts =
new Dictionary<string, Chart>(
Charts);
355 foreach (var ap
in statisticsResults.RollingPerformances.Values)
357 ap.ClosedTrades.Clear();
363 statisticsResults.RollingPerformances, orderEvents, statisticsResults.TotalPerformance,
386 Log.
Trace(
"BacktestingResultHandler.SendAnalysisResult(): Processed final packet");
388 catch (Exception err)
414 var resampleMinutes = totalMinutes < MinimumSamplePeriod * Samples ? MinimumSamplePeriod : totalMinutes / Samples;
416 Log.
Trace(
"BacktestingResultHandler(): Sample Period Set: " + resampleMinutes.ToStringInvariant(
"00.00"));
419 var types =
new List<SecurityType>();
422 var security = kvp.Value;
424 if (!types.Contains(security.Type)) types.Add(security.Type);
491 :
"Algorithm Initialization: " + message;
493 base.AddToLogStore(messageToLog);
513 public virtual void ErrorMessage(
string message,
string stacktrace =
"")
515 if (message == _errorMessage)
return;
518 _errorMessage = message;
526 public virtual void RuntimeError(
string message,
string stacktrace =
"")
530 _errorMessage = message;
565 if (!
Charts.TryGetValue(chartName, out chart))
567 chart =
new Chart(chartName);
568 Charts.AddOrUpdate(chartName, chart);
573 if (!chart.Series.TryGetValue(seriesName, out series))
576 chart.Series.Add(seriesName, series);
580 if (series.Values.Count == 0 || value.
Time > series.Values[series.Values.Count - 1].Time
596 var roundedCapacity = _capacityEstimate.
Capacity;
608 foreach (var update
in updates)
612 if (!
Charts.TryGetValue(update.Name, out chart))
614 chart =
new Chart(update.Name);
615 Charts.AddOrUpdate(update.Name, chart);
619 foreach (var series
in update.Series.Values)
624 _chartSeriesCount.Add(series.Name);
626 else if (!_chartSeriesCount.Contains(series.Name))
629 if(!_chartSeriesCountExceededError)
631 _chartSeriesCountExceededError =
true;
632 DebugMessage($
"Exceeded maximum chart series count for organization tier, new series will be ignored. Limit is currently set at {_job.Controls.MaximumChartSeries}. https://qnt.co/docs-charting-quotas");
637 if (series.Values.Count > 0)
639 var thisSeries = chart.TryAddAndGetSeries(series.Name, series, forceAddNew:
false);
642 var dataPoint = series.ConsolidateChartPoints();
643 if (dataPoint !=
null)
645 thisSeries.AddPoint(dataPoint);
650 var values = thisSeries.Values;
654 values.AddRange(series.Values);
656 else if (!_chartSeriesExceededDataPoints.Contains(chart.Name + series.Name))
658 _chartSeriesExceededDataPoints.Add(chart.Name + series.Name);
659 DebugMessage($
"Exceeded maximum data points per series for organization tier, chart update skipped. Chart Name {update.Name}. Series name {series.Name}. https://qnt.co/docs-charting-quotas" +
660 $
"Limit is currently set at {_job.Controls.MaximumDataPointsPerChartSeries}");
677 Log.
Trace(
"BacktestingResultHandler.Exit(): starting...");
684 Log.
Trace(
"BacktestingResultHandler.Exit(): Saving logs...");
685 var logLocation =
SaveLogs(_algorithmId, copy);
686 SystemDebugMessage(
"Your log was successfully created and can be retrieved from: " + logLocation);
752 if (time > _nextSample || forceProcess)