-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Expand file tree
/
Copy pathBacktestingSetupHandler.cs
More file actions
278 lines (234 loc) · 12.4 KB
/
BacktestingSetupHandler.cs
File metadata and controls
278 lines (234 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/*
* 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 QuantConnect.Util;
using QuantConnect.Logging;
using QuantConnect.Packets;
using QuantConnect.Algorithm;
using QuantConnect.Interfaces;
using QuantConnect.Configuration;
using System.Collections.Generic;
using QuantConnect.AlgorithmFactory;
using QuantConnect.Lean.Engine.DataFeeds;
using QuantConnect.Brokerages.Backtesting;
namespace QuantConnect.Lean.Engine.Setup
{
/// <summary>
/// Backtesting setup handler processes the algorithm initialize method and sets up the internal state of the algorithm class.
/// </summary>
public class BacktestingSetupHandler : ISetupHandler
{
/// <summary>
/// Get the maximum time that the initialization of an algorithm can take
/// </summary>
protected TimeSpan InitializationTimeOut { get; set; } = BaseSetupHandler.InitializationTimeout;
/// <summary>
/// Get the maximum time that the creation of an algorithm can take
/// </summary>
protected TimeSpan AlgorithmCreationTimeout { get; set; } = BaseSetupHandler.AlgorithmCreationTimeout;
/// <summary>
/// The worker thread instance the setup handler should use
/// </summary>
public WorkerThread WorkerThread { get; set; }
/// <summary>
/// Internal errors list from running the setup procedures.
/// </summary>
public List<Exception> Errors { get; set; }
/// <summary>
/// Maximum runtime of the algorithm in seconds.
/// </summary>
/// <remarks>Maximum runtime is a formula based on the number and resolution of symbols requested, and the days backtesting</remarks>
public TimeSpan MaximumRuntime { get; protected set; }
/// <summary>
/// Starting capital according to the users initialize routine.
/// </summary>
/// <remarks>Set from the user code.</remarks>
/// <seealso cref="QCAlgorithm.SetCash(decimal)"/>
public decimal StartingPortfolioValue { get; protected set; }
/// <summary>
/// Start date for analysis loops to search for data.
/// </summary>
/// <seealso cref="QCAlgorithm.SetStartDate(DateTime)"/>
public DateTime StartingDate { get; protected set; }
/// <summary>
/// Maximum number of orders for this backtest.
/// </summary>
/// <remarks>To stop algorithm flooding the backtesting system with hundreds of megabytes of order data we limit it to 100 per day</remarks>
public int MaxOrders { get; protected set; }
/// <summary>
/// Initialize the backtest setup handler.
/// </summary>
public BacktestingSetupHandler()
{
MaximumRuntime = TimeSpan.FromSeconds(300);
Errors = new List<Exception>();
StartingDate = new DateTime(1998, 01, 01);
}
/// <summary>
/// Create a new instance of an algorithm from a physical dll path.
/// </summary>
/// <param name="assemblyPath">The path to the assembly's location</param>
/// <param name="algorithmNodePacket">Details of the task required</param>
/// <returns>A new instance of IAlgorithm, or throws an exception if there was an error</returns>
public virtual IAlgorithm CreateAlgorithmInstance(AlgorithmNodePacket algorithmNodePacket, string assemblyPath)
{
string error;
IAlgorithm algorithm;
var debugNode = algorithmNodePacket as BacktestNodePacket;
var debugging = debugNode != null && debugNode.Debugging || Config.GetBool("debugging", false);
if (debugging && !BaseSetupHandler.InitializeDebugging(algorithmNodePacket, WorkerThread))
{
throw new AlgorithmSetupException("Failed to initialize debugging");
}
// Limit load times to 90 seconds and force the assembly to have exactly one derived type
var loader = new Loader(debugging, algorithmNodePacket.Language, AlgorithmCreationTimeout, names => names.SingleOrAlgorithmTypeName(Config.Get("algorithm-type-name", algorithmNodePacket.AlgorithmId)), WorkerThread);
var complete = loader.TryCreateAlgorithmInstanceWithIsolator(assemblyPath, algorithmNodePacket.RamAllocation, out algorithm, out error);
if (!complete) throw new AlgorithmSetupException($"During the algorithm initialization, the following exception has occurred: {error}");
return algorithm;
}
/// <summary>
/// Creates a new <see cref="BacktestingBrokerage"/> instance
/// </summary>
/// <param name="algorithmNodePacket">Job packet</param>
/// <param name="uninitializedAlgorithm">The algorithm instance before Initialize has been called</param>
/// <param name="factory">The brokerage factory</param>
/// <returns>The brokerage instance, or throws if error creating instance</returns>
public virtual IBrokerage CreateBrokerage(AlgorithmNodePacket algorithmNodePacket, IAlgorithm uninitializedAlgorithm, out IBrokerageFactory factory)
{
factory = new BacktestingBrokerageFactory();
return new BacktestingBrokerage(uninitializedAlgorithm);
}
/// <summary>
/// Setup the algorithm cash, dates and data subscriptions as desired.
/// </summary>
/// <param name="parameters">The parameters object to use</param>
/// <returns>Boolean true on successfully initializing the algorithm</returns>
public virtual bool Setup(SetupHandlerParameters parameters)
{
var algorithm = parameters.Algorithm;
var job = parameters.AlgorithmNodePacket as BacktestNodePacket;
if (job == null)
{
throw new ArgumentException("Expected BacktestNodePacket but received " + parameters.AlgorithmNodePacket.GetType().Name);
}
BaseSetupHandler.Setup(parameters);
if (algorithm == null)
{
Errors.Add(new AlgorithmSetupException("Could not create instance of algorithm"));
return false;
}
algorithm.Name = job.Name;
//Make sure the algorithm start date ok.
if (job.PeriodStart == default(DateTime))
{
Errors.Add(new AlgorithmSetupException("Algorithm start date was never set"));
return false;
}
var controls = job.Controls;
var isolator = new Isolator();
var initializeComplete = isolator.ExecuteWithTimeLimit(InitializationTimeOut, () =>
{
try
{
parameters.ResultHandler.SendStatusUpdate(AlgorithmStatus.Initializing, "Initializing algorithm...");
//Set our parameters
algorithm.SetParameters(job.Parameters);
algorithm.SetAvailableDataTypes(BaseSetupHandler.GetConfiguredDataFeeds());
//Algorithm is backtesting, not live:
algorithm.SetAlgorithmMode(job.AlgorithmMode);
//Set the source impl for the event scheduling
algorithm.Schedule.SetEventSchedule(parameters.RealTimeHandler);
// set the option chain provider
var optionChainProvider = new BacktestingOptionChainProvider();
var initParameters = new ChainProviderInitializeParameters(parameters.MapFileProvider, algorithm.HistoryProvider);
optionChainProvider.Initialize(initParameters);
algorithm.SetOptionChainProvider(new CachingOptionChainProvider(optionChainProvider));
// set the future chain provider
var futureChainProvider = new BacktestingFutureChainProvider();
futureChainProvider.Initialize(initParameters);
algorithm.SetFutureChainProvider(new CachingFutureChainProvider(futureChainProvider));
// before we call initialize
BaseSetupHandler.LoadBacktestJobAccountCurrency(algorithm, job);
//Initialise the algorithm, get the required data:
algorithm.Initialize();
// set start and end date if present in the job
if (job.PeriodStart.HasValue)
{
algorithm.SetStartDate(job.PeriodStart.Value);
}
if (job.PeriodFinish.HasValue)
{
algorithm.SetEndDate(job.PeriodFinish.Value);
}
if (job.OutOfSampleMaxEndDate.HasValue)
{
if (algorithm.EndDate > job.OutOfSampleMaxEndDate.Value)
{
Log.Trace($"BacktestingSetupHandler.Setup(): setting end date to {job.OutOfSampleMaxEndDate.Value:yyyyMMdd}");
algorithm.SetEndDate(job.OutOfSampleMaxEndDate.Value);
if (algorithm.StartDate > algorithm.EndDate)
{
algorithm.SetStartDate(algorithm.EndDate);
}
}
}
// after we call initialize
BaseSetupHandler.LoadBacktestJobCashAmount(algorithm, job);
// after algorithm was initialized, should set trading days per year for our great portfolio statistics
BaseSetupHandler.SetBrokerageTradingDayPerYear(algorithm);
// finalize initialization
algorithm.PostInitialize();
}
catch (Exception err)
{
Errors.Add(new AlgorithmSetupException("During the algorithm initialization, the following exception has occurred: ", err));
}
}, controls.RamAllocation,
sleepIntervalMillis: 100, // entire system is waiting on this, so be as fast as possible
workerThread: WorkerThread);
if (Errors.Count > 0)
{
// if we already got an error just exit right away
return false;
}
//Before continuing, detect if this is ready:
if (!initializeComplete) return false;
MaximumRuntime = TimeSpan.FromMinutes(job.Controls.MaximumRuntimeMinutes);
BaseSetupHandler.SetupCurrencyConversions(algorithm, parameters.UniverseSelection);
StartingPortfolioValue = algorithm.Portfolio.Cash;
// Get and set maximum orders for this job
MaxOrders = job.Controls.BacktestingMaxOrders;
algorithm.SetMaximumOrders(MaxOrders);
//Starting date of the algorithm:
StartingDate = algorithm.StartDate;
//Put into log for debugging:
Log.Trace("SetUp Backtesting: User: " + job.UserId + " ProjectId: " + job.ProjectId + " AlgoId: " + job.AlgorithmId);
Log.Trace($"Dates: Start: {algorithm.StartDate.ToStringInvariant("d")} " +
$"End: {algorithm.EndDate.ToStringInvariant("d")} " +
$"Cash: {StartingPortfolioValue.ToStringInvariant("C")} " +
$"MaximumRuntime: {MaximumRuntime} " +
$"MaxOrders: {MaxOrders}");
return initializeComplete;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
}
} // End Result Handler Thread:
} // End Namespace