chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user