Files
quantconnect--lean/Launcher/Program.cs
T
2026-07-13 13:02:50 +08:00

158 lines
6.4 KiB
C#

/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Threading;
using QuantConnect.Configuration;
using QuantConnect.Lean.Engine;
using QuantConnect.Logging;
using QuantConnect.Packets;
using QuantConnect.Python;
using QuantConnect.Util;
namespace QuantConnect.Lean.Launcher
{
public class Program
{
private const string _collapseMessage = "Unhandled exception breaking past controls and causing collapse of algorithm node. This is likely a memory leak of an external dependency or the underlying OS terminating the LEAN engine.";
private static LeanEngineSystemHandlers leanEngineSystemHandlers;
private static LeanEngineAlgorithmHandlers leanEngineAlgorithmHandlers;
private static AlgorithmNodePacket job;
private static AlgorithmManager algorithmManager;
static Program()
{
AppDomain.CurrentDomain.AssemblyLoad += (sender, e) =>
{
if (e.LoadedAssembly.FullName.ToLowerInvariant().Contains("python"))
{
Log.Trace($"Python for .NET Assembly: {e.LoadedAssembly.GetName()}");
}
};
}
static void Main(string[] args)
{
if (OS.IsWindows)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
}
// expect first argument to be config file name
if (args.Length > 0)
{
Config.MergeCommandLineArgumentsWithConfiguration(LeanArgumentParser.ParseArguments(args));
}
//Name thread for the profiler:
Thread.CurrentThread.Name = "Algorithm Analysis Thread";
Initializer.Start();
leanEngineSystemHandlers = Initializer.GetSystemHandlers();
//-> Pull job from QuantConnect job queue, or, pull local build:
job = leanEngineSystemHandlers.JobQueue.NextJob(out var assemblyPath);
leanEngineAlgorithmHandlers = Initializer.GetAlgorithmHandlers();
if (job == null)
{
const string jobNullMessage = "Engine.Main(): Sorry we could not process this algorithm request.";
Log.Error(jobNullMessage);
Exit(1);
}
// Activate our PythonVirtualEnvironment
PythonInitializer.ActivatePythonVirtualEnvironment(job.PythonVirtualEnvironment);
// if the job version doesn't match this instance version then we can't process it
// we also don't want to reprocess redelivered jobs
if (job.Redelivered)
{
Log.Error("Engine.Run(): Job Version: " + job.Version + " Deployed Version: " + Globals.Version + " Redelivered: " + job.Redelivered);
//Tiny chance there was an uncontrolled collapse of a server, resulting in an old user task circulating.
//In this event kill the old algorithm and leave a message so the user can later review.
leanEngineSystemHandlers.Api.SetAlgorithmStatus(job.AlgorithmId, AlgorithmStatus.RuntimeError, _collapseMessage);
leanEngineSystemHandlers.Notify.SetAuthentication(job);
leanEngineSystemHandlers.Notify.Send(new RuntimeErrorPacket(job.UserId, job.AlgorithmId, _collapseMessage));
leanEngineSystemHandlers.JobQueue.AcknowledgeJob(job);
Exit(1);
}
try
{
// Set our exit handler for the algorithm
Console.CancelKeyPress += new ConsoleCancelEventHandler(ExitKeyPress);
// Create the algorithm manager and start our engine
algorithmManager = new AlgorithmManager(Globals.LiveMode, job);
leanEngineSystemHandlers.LeanManager.Initialize(leanEngineSystemHandlers, leanEngineAlgorithmHandlers, job, algorithmManager);
OS.Initialize();
var engine = new Engine.Engine(leanEngineSystemHandlers, leanEngineAlgorithmHandlers, Globals.LiveMode);
engine.Run(job, algorithmManager, assemblyPath, WorkerThread.Instance);
}
finally
{
var algorithmStatus = algorithmManager?.State ?? AlgorithmStatus.DeployError;
Exit(algorithmStatus != AlgorithmStatus.Completed ? 1 : 0);
}
}
public static void ExitKeyPress(object sender, ConsoleCancelEventArgs args)
{
// Allow our process to resume after this event
args.Cancel = true;
// Stop the algorithm
algorithmManager.SetStatus(AlgorithmStatus.Stopped);
Log.Trace("Program.ExitKeyPress(): Lean instance has been cancelled, shutting down safely now");
}
public static void Exit(int exitCode)
{
// The job can be null if the algorithm file was not found
if (job != null)
{
//Delete the message from the job queue:
leanEngineSystemHandlers.JobQueue.AcknowledgeJob(job);
Log.Trace("Engine.Main(): Packet removed from queue: " + job.AlgorithmId);
}
// clean up resources
leanEngineSystemHandlers.DisposeSafely();
leanEngineAlgorithmHandlers.DisposeSafely();
OS.Dispose();
try
{
var isolator = new Isolator();
isolator.ExecuteWithTimeLimit(TimeSpan.FromSeconds(10), PythonInitializer.Shutdown, -1);
}
catch (Exception ex)
{
Log.Error(ex, $"Failed to shutdown python");
}
Log.Trace("Program.Main(): Exiting Lean...");
Log.LogHandler.DisposeSafely();
Environment.Exit(exitCode);
}
}
}