diff --git a/Engine/Engine.cs b/Engine/Engine.cs index 691d8db248c9..d207b3cadb70 100644 --- a/Engine/Engine.cs +++ b/Engine/Engine.cs @@ -381,7 +381,7 @@ public void Run(AlgorithmNodePacket job, AlgorithmManager manager, string assemb // Algorithm runtime error: if (algorithm.RunTimeError != null) { - HandleAlgorithmError(job, algorithm.RunTimeError); + HandleAlgorithmError(job, algorithm, algorithm.RunTimeError); } // notify the LEAN manager that the algorithm has finished @@ -477,13 +477,18 @@ public void Run(AlgorithmNodePacket job, AlgorithmManager manager, string assemb /// Handle an error in the algorithm.Run method. /// /// Job we're processing + /// The algorithm instance that raised the error /// Error from algorithm stack - private void HandleAlgorithmError(AlgorithmNodePacket job, Exception err) + private void HandleAlgorithmError(AlgorithmNodePacket job, IAlgorithm algorithm, Exception err) { AlgorithmHandlers.DataFeed?.Exit(); if (AlgorithmHandlers.Results != null) { var message = $"Runtime Error: {err.Message}"; + if (TryGetOutOfMemoryErrorDetails(job, algorithm, err, out var outOfMemoryDetails)) + { + message += outOfMemoryDetails; + } Log.Trace("Engine.Run(): Sending runtime error to user..."); AlgorithmHandlers.Results.LogMessage(message); @@ -495,6 +500,42 @@ private void HandleAlgorithmError(AlgorithmNodePacket job, Exception err) } } + /// + /// Checks whether the given exception is an , + /// producing details about the algorithm state and the common causes to be appended to the runtime error message + /// + /// Job we're processing + /// The algorithm instance that raised the error + /// Error from algorithm stack + /// The out of memory details, null if the error is not an out of memory error + /// True if the error is an out of memory error + private static bool TryGetOutOfMemoryErrorDetails(AlgorithmNodePacket job, IAlgorithm algorithm, Exception err, out string details) + { + details = null; + if (err is not OutOfMemoryException) + { + return false; + } + + details = Invariant($" The algorithm exhausted its {job.RamAllocation}MB of RAM."); + if (algorithm != null) + { + try + { + details += Invariant($" State: {algorithm.Securities.Count} securities, {algorithm.SubscriptionManager.Count} subscriptions, {algorithm.UniverseManager.Count} universes."); + } + catch (Exception stateException) + { + // best effort: don't let diagnostics collection replace the original error + Log.Error(stateException); + } + } + details += " Common causes: universe/subscription count too high for the allocated RAM; unbounded buffers such as rolling windows," + + " consolidators or accumulated history results. Fixes: reduce universe size/subscriptions, use coarser resolutions," + + " or bound buffers."; + return true; + } + /// /// Load the history provider from the Composer ///