17 using System.Threading;
21 using System.Collections.Concurrent;
22 using System.Collections.Generic;
23 using System.Globalization;
35 private readonly
int _optimizationUpdateInterval =
Config.
GetInt(
"optimization-update-interval", 10);
37 private DateTime _startedAt = DateTime.UtcNow;
39 private DateTime _lastUpdate;
40 private int _failedBacktest;
41 private int _completedBacktest;
42 private volatile bool _disposed;
52 private object _statusLock =
new object();
93 public event EventHandler<OptimizationResult>
Ended;
103 throw new ArgumentException(
"Cannot start an optimization job with no parameter to optimize");
108 throw new ArgumentException(
"Cannot start an optimization job with no target to optimize");
128 if (parameterSet ==
null)
131 Log.
Error($
"Strategy.NewParameterSet({GetLogDetails()}): generated a null {nameof(ParameterSet)} instance");
134 LaunchLeanForParameterSet(parameterSet);
151 throw new InvalidOperationException($
"LeanOptimizer.Start({GetLogDetails()}): failed to start");
153 Log.
Trace($
"LeanOptimizer.Start({GetLogDetails()}): start ended. Waiting on {RunningParameterSetForBacktest.Count + PendingParameterSet.Count} backtests");
157 ProcessUpdate(forceSend:
true);
174 var constraint =
NodePacket.
Constraints !=
null ? $
"Constraints: ({string.Join(",
", NodePacket.Constraints)})" :
string.Empty;
175 Log.
Trace($
"LeanOptimizer.TriggerOnEndEvent({GetLogDetails()}): Optimization has ended. " +
176 $
"Result for {OptimizationTarget}: was reached using ParameterSet: ({result.ParameterSet}) backtestId '{result.BacktestId}'. " +
181 Log.
Trace($
"LeanOptimizer.TriggerOnEndEvent({GetLogDetails()}): Optimization has ended. Result was not reached");
185 CleanUpRunningInstance();
186 ProcessUpdate(forceSend:
true);
188 Ended?.Invoke(
this, result);
204 return "OptimizationBacktest";
212 protected virtual void NewResult(
string jsonBacktestResult,
string backtestId)
223 Interlocked.Increment(ref _failedBacktest);
225 $
"LeanOptimizer.NewResult({GetLogDetails()}): Optimization compute job with id '{backtestId}' was not found");
233 LaunchLeanForParameterSet(pendingParameterSet);
237 if (
string.IsNullOrEmpty(jsonBacktestResult))
239 Interlocked.Increment(ref _failedBacktest);
241 $
"LeanOptimizer.NewResult({GetLogDetails()}): Got null/empty backtest result for backtest id '{backtestId}'");
245 Interlocked.Increment(ref _completedBacktest);
274 CleanUpRunningInstance();
290 var completedCount = _completedBacktest;
291 var totalEndedCount = completedCount + _failedBacktest;
292 var runtime = DateTime.UtcNow - _startedAt;
293 var result =
new Dictionary<string, string>
295 {
"Completed", $
"{completedCount}"},
296 {
"Failed", $
"{_failedBacktest}"},
297 {
"Running", $
"{RunningParameterSetForBacktest.Count}"},
298 {
"In Queue", $
"{PendingParameterSet.Count}"},
299 {
"Average Length", $
"{(totalEndedCount > 0 ? new TimeSpan(runtime.Ticks / totalEndedCount) : TimeSpan.Zero).ToString(@"hh\:mm\:ss
", CultureInfo.InvariantCulture)}"},
300 {
"Total Runtime", $
"{runtime.ToString(@"hh\:mm\:ss
", CultureInfo.InvariantCulture)}" }
313 return $
"OID {NodePacket.OptimizationId}";
315 return $
"UI {NodePacket.UserId} PID {NodePacket.ProjectId} OID {NodePacket.OptimizationId} S {Status}";
322 protected abstract void AbortLean(
string backtestId);
340 Status = optimizationStatus;
348 private void CleanUpRunningInstance()
359 Interlocked.Increment(ref _failedBacktest);
377 private void ProcessUpdate(
bool forceSend =
false)
387 var now = DateTime.UtcNow;
388 if (forceSend || (now - _lastUpdate > TimeSpan.FromSeconds(_optimizationUpdateInterval)))
391 Log.
Debug($
"LeanOptimizer.ProcessUpdate({GetLogDetails()}): start sending update...");
395 Log.
Debug($
"LeanOptimizer.ProcessUpdate({GetLogDetails()}): finished sending update successfully.");
400 Log.
Error(e,
"Failed to send status update");
404 private void LaunchLeanForParameterSet(
ParameterSet parameterSet)
423 var backtestId =
RunLean(parameterSet, backtestName);
425 if (!
string.IsNullOrEmpty(backtestId))
427 Log.
Trace($
"LeanOptimizer.LaunchLeanForParameterSet({GetLogDetails()}): launched backtest '{backtestId}' with parameters '{parameterSet}'");
432 Interlocked.Increment(ref _failedBacktest);
435 Log.
Error($
"LeanOptimizer.LaunchLeanForParameterSet({GetLogDetails()}): Initial/null optimization compute job could not be placed into the queue");
442 Log.
Error($
"LeanOptimizer.LaunchLeanForParameterSet({GetLogDetails()}): Error encountered while placing optimization message into the queue: {ex.Message}");