chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
+158
View File
@@ -0,0 +1,158 @@
/*
* 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 QuantConnect.Util;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using QuantConnect.Optimizer.Parameters;
using Log = QuantConnect.Logging.Log;
namespace QuantConnect.Optimizer.Launcher
{
/// <summary>
/// Optimizer implementation that launches Lean as a local process
/// </summary>
public class ConsoleLeanOptimizer : LeanOptimizer
{
private readonly string _leanLocation;
private readonly string _rootResultDirectory;
private readonly string _extraLeanArguments;
private readonly ConcurrentDictionary<string, Process> _processByBacktestId;
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="nodePacket">The optimization node packet to handle</param>
public ConsoleLeanOptimizer(OptimizationNodePacket nodePacket) : base(nodePacket)
{
_processByBacktestId = new ConcurrentDictionary<string, Process>();
_rootResultDirectory = Configuration.Config.Get("results-destination-folder",
Path.Combine(Directory.GetCurrentDirectory(), $"opt-{nodePacket.OptimizationId}"));
Directory.CreateDirectory(_rootResultDirectory);
_leanLocation = Configuration.Config.Get("lean-binaries-location",
Path.Combine(Directory.GetCurrentDirectory(), "../../../Launcher/bin/Debug/QuantConnect.Lean.Launcher"));
var closeLeanAutomatically = Configuration.Config.GetBool("optimizer-close-automatically", true);
_extraLeanArguments = $"--close-automatically {closeLeanAutomatically}";
var algorithmTypeName = Configuration.Config.Get("algorithm-type-name");
if (!string.IsNullOrEmpty(algorithmTypeName))
{
_extraLeanArguments += $" --algorithm-type-name \"{algorithmTypeName}\"";
}
var algorithmLanguage = Configuration.Config.Get("algorithm-language");
if (!string.IsNullOrEmpty(algorithmLanguage))
{
_extraLeanArguments += $" --algorithm-language \"{algorithmLanguage}\"";
}
var algorithmLocation = Configuration.Config.Get("algorithm-location");
if (!string.IsNullOrEmpty(algorithmLocation))
{
_extraLeanArguments += $" --algorithm-location \"{algorithmLocation}\"";
}
}
/// <summary>
/// Handles starting Lean for a given parameter set
/// </summary>
/// <param name="parameterSet">The parameter set for the backtest to run</param>
/// <param name="backtestName">The backtest name to use</param>
/// <returns>The new unique backtest id</returns>
protected override string RunLean(ParameterSet parameterSet, string backtestName)
{
var backtestId = Guid.NewGuid().ToString();
var optimizationId = NodePacket.OptimizationId;
// start each lean instance in its own directory so they store their logs & results, else they fight for the log.txt file
var resultDirectory = Path.Combine(_rootResultDirectory, backtestId);
Directory.CreateDirectory(resultDirectory);
// Use ProcessStartInfo class
var startInfo = new ProcessStartInfo
{
FileName = _leanLocation,
WorkingDirectory = Directory.GetParent(_leanLocation).FullName,
Arguments = $"--results-destination-folder \"{resultDirectory}\" --algorithm-id \"{backtestId}\" --optimization-id \"{optimizationId}\" --parameters {parameterSet} --backtest-name \"{backtestName}\" {_extraLeanArguments}",
WindowStyle = ProcessWindowStyle.Minimized
};
var process = new Process
{
StartInfo = startInfo,
EnableRaisingEvents = true
};
_processByBacktestId[backtestId] = process;
process.Exited += (sender, args) =>
{
if (Disposed)
{
// handle abort
return;
}
_processByBacktestId.TryRemove(backtestId, out process);
var backtestResult = $"{backtestId}.json";
var resultJson = Path.Combine(_rootResultDirectory, backtestId, backtestResult);
NewResult(File.Exists(resultJson) ? File.ReadAllText(resultJson) : null, backtestId);
process.DisposeSafely();
};
process.Start();
return backtestId;
}
/// <summary>
/// Stops lean process
/// </summary>
/// <param name="backtestId">Specified backtest id</param>
protected override void AbortLean(string backtestId)
{
Process process;
if (_processByBacktestId.TryRemove(backtestId, out process))
{
process.Kill();
process.DisposeSafely();
}
}
/// <summary>
/// Sends an update of the current optimization status to the user
/// </summary>
protected override void SendUpdate()
{
// end handler will already log a nice message on end
if (Status != OptimizationStatus.Completed && Status != OptimizationStatus.Aborted)
{
var currentEstimate = GetCurrentEstimate();
var stats = GetRuntimeStatistics();
var message = $"ConsoleLeanOptimizer.SendUpdate(): {currentEstimate} {string.Join(", ", stats.Select(pair => $"{pair.Key}:{pair.Value}"))}";
var currentBestBacktest = Strategy.Solution;
if (currentBestBacktest != null)
{
message += $". Best id:'{currentBestBacktest.BacktestId}'. {OptimizationTarget}. Parameters ({currentBestBacktest.ParameterSet})";
}
Log.Trace(message);
}
}
}
}
+105
View File
@@ -0,0 +1,105 @@
/*
* 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 Newtonsoft.Json;
using QuantConnect.Configuration;
using QuantConnect.Logging;
using QuantConnect.Optimizer.Objectives;
using QuantConnect.Optimizer.Parameters;
using QuantConnect.Optimizer.Strategies;
using QuantConnect.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
namespace QuantConnect.Optimizer.Launcher
{
public class Program
{
public static void Main(string[] args)
{
// Parse report arguments and merge with config to use in the optimizer
if (args.Length > 0)
{
Config.MergeCommandLineArgumentsWithConfiguration(OptimizerArgumentParser.ParseArguments(args));
}
using var endedEvent = new ManualResetEvent(false);
try
{
Log.DebuggingEnabled = Config.GetBool("debug-mode");
Log.FilePath = Path.Combine(Config.Get("results-destination-folder"), "log.txt");
Log.LogHandler = Composer.Instance.GetExportedValueByTypeName<ILogHandler>(Config.Get("log-handler", "CompositeLogHandler"));
var optimizationStrategyName = Config.Get("optimization-strategy",
"QuantConnect.Optimizer.GridSearchOptimizationStrategy");
var channel = Config.Get("data-channel");
var optimizationId = Config.Get("optimization-id", Guid.NewGuid().ToString());
var packet = new OptimizationNodePacket
{
OptimizationId = optimizationId,
OptimizationStrategy = optimizationStrategyName,
OptimizationStrategySettings = (OptimizationStrategySettings)JsonConvert.DeserializeObject(Config.Get(
"optimization-strategy-settings",
"{\"$type\":\"QuantConnect.Optimizer.Strategies.OptimizationStrategySettings, QuantConnect.Optimizer\"}"), new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.All}),
Criterion = JsonConvert.DeserializeObject<Target>(Config.Get("optimization-criterion", "{\"target\":\"Statistics.TotalProfit\", \"extremum\": \"max\"}")),
Constraints = JsonConvert.DeserializeObject<List<Constraint>>(Config.Get("constraints", "[]")).AsReadOnly(),
OptimizationParameters = JsonConvert.DeserializeObject<HashSet<OptimizationParameter>>(Config.Get("parameters", "[]")),
MaximumConcurrentBacktests = Config.GetInt("maximum-concurrent-backtests", Math.Max(1, Environment.ProcessorCount / 2)),
Channel = channel,
};
var outOfSampleMaxEndDate = Config.Get("out-of-sample-max-end-date");
if (!string.IsNullOrEmpty(outOfSampleMaxEndDate))
{
packet.OutOfSampleMaxEndDate = Time.ParseDate(outOfSampleMaxEndDate);
}
packet.OutOfSampleDays = Config.GetInt("out-of-sample-days");
var optimizerType = Config.Get("optimization-launcher", typeof(ConsoleLeanOptimizer).Name);
var optimizer = (LeanOptimizer)Activator.CreateInstance(Composer.Instance.GetExportedTypes<LeanOptimizer>().Single(x => x.Name == optimizerType), packet);
if (Config.GetBool("estimate", false))
{
var backtestsCount = optimizer.GetCurrentEstimate();
Log.Trace($"Optimization estimate: {backtestsCount}");
optimizer.DisposeSafely();
endedEvent.Set();
}
else
{
optimizer.Start();
optimizer.Ended += (s, e) =>
{
optimizer.DisposeSafely();
endedEvent.Set();
};
}
}
catch (Exception e)
{
Log.Error(e);
}
// Wait until the optimizer has stopped running before exiting
endedEvent.WaitOne();
}
}
}
@@ -0,0 +1,17 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("QuantConnect.Optimizer.Launcher")]
[assembly: AssemblyProduct("QuantConnect.Optimizer.Launcher")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("d46d2a8d-340c-4b40-8ee6-6baa7b1198ab")]
@@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>Exe</OutputType>
<RootNamespace>QuantConnect.Optimizer.Launcher</RootNamespace>
<AssemblyName>QuantConnect.Optimizer.Launcher</AssemblyName>
<TargetFramework>net10.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputPath>bin\$(Configuration)\</OutputPath>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Description>QuantConnect LEAN Engine: Optimizer Launcher Project - The Lean optimization engine launcher</Description>
<NoWarn>CA1062</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE</DefineConstants>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<Features>flow-analysis</Features>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="config.example.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\QuantConnect.csproj" />
<ProjectReference Include="..\Configuration\QuantConnect.Configuration.csproj" />
<ProjectReference Include="..\Logging\QuantConnect.Logging.csproj" />
<ProjectReference Include="..\Optimizer\QuantConnect.Optimizer.csproj" />
</ItemGroup>
</Project>
+68
View File
@@ -0,0 +1,68 @@
{
// optional: algorithm class selector
"algorithm-type-name": "ParameterizedAlgorithm",
// optional: Algorithm language selector - options CSharp, Python
"algorithm-language": "CSharp",
// optional: Physical DLL location
"algorithm-location": "QuantConnect.Algorithm.CSharp.dll",
"optimizer-close-automatically": true,
// How we manage solutions and make decision to continue or stop
"optimization-strategy": "QuantConnect.Optimizer.Strategies.EulerSearchOptimizationStrategy",
// on-demand settings required for different optimization strategies
"optimization-strategy-settings": {
"$type": "QuantConnect.Optimizer.Strategies.StepBaseOptimizationStrategySettings, QuantConnect.Optimizer",
"default-segment-amount": 10
},
// optimization problem
"optimization-criterion": {
// path in algorithm output json
"target": "Statistics.Sharpe Ratio",
// optimization: available options max, min
"extremum": "max",
// optional, if defined and backtest complies with the targets then trigger ended event
"target-value": 3
},
// if it doesn't comply just drop the backtest
"constraints": [
{
"target": "Drawdown",
"operator": "lessOrEqual", // less, greaterOrEqual, greater, notEqual, equals
"target-value": 0.15
},
{
"target": "Total Trades",
"operator": "greater",
"target-value": 2
}
],
// optional: default is process count / 2
//"maximum-concurrent-backtests": 10,
// optimization parameters
"parameters": [
{
"name": "ema-slow",
"min": 10,
"max": 50
},
{
"name": "ema-fast",
"min": 50,
"max": 150,
"step": 50,
// optional
"min-step": 0.0001
}
]
}