/* * 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 NAMESPACES **********************************************************/ using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Algorithm; using QuantConnect.AlgorithmFactory; using QuantConnect.Brokerages; using QuantConnect.Interfaces; using QuantConnect.Lean.Engine.Results; using QuantConnect.Logging; using QuantConnect.Packets; namespace QuantConnect.Lean.Engine.Setup { /// /// Backtesting setup handler processes the algorithm initialize method and sets up the internal state of the algorithm class. /// public class BacktestingSetupHandler : ISetupHandler { /******************************************************** * PRIVATE VARIABLES *********************************************************/ private TimeSpan _maxRuntime = TimeSpan.FromSeconds(300); private decimal _startingCaptial = 0; private int _maxOrders = 0; private DateTime _startingDate = new DateTime(1998, 01, 01); /******************************************************** * PUBLIC PROPERTIES *********************************************************/ /// /// Internal errors list from running the setup proceedures. /// public List Errors { get; set; } /// /// Maximum runtime of the algorithm in seconds. /// /// Maximum runtime is a formula based on the number and resolution of symbols requested, and the days backtesting public TimeSpan MaximumRuntime { get { return _maxRuntime; } } /// /// Starting capital according to the users initialize routine. /// /// Set from the user code. /// public decimal StartingCapital { get { return _startingCaptial; } } /// /// Start date for analysis loops to search for data. /// /// public DateTime StartingDate { get { return _startingDate; } } /// /// Maximum number of orders for this backtest. /// /// To stop algorithm flooding the backtesting system with hundreds of megabytes of order data we limit it to 100 per day public int MaxOrders { get { return _maxOrders; } } /******************************************************** * PUBLIC CONSTRUCTOR *********************************************************/ /// /// Initialize the backtest setup handler. /// public BacktestingSetupHandler() { Errors = new List(); } /******************************************************** * PUBLIC METHODS *********************************************************/ /// /// Creates a new algorithm instance. Verified there's only one defined in the assembly and requires /// instantiation to take less than 10 seconds /// /// Physical location of the assembly. /// Algorithm instance. public IAlgorithm CreateAlgorithmInstance(string assemblyPath) { string error; IAlgorithm algorithm; // limit load times to 10 seconds and force the assembly to have exactly one derived type var loader = new Loader(TimeSpan.FromSeconds(10), names => names.SingleOrDefault()); bool complete = loader.TryCreateAlgorithmInstanceWithIsolator(assemblyPath, out algorithm, out error); if (!complete) throw new Exception(error + " Try re-building algorithm."); return algorithm; } /// /// Setup the algorithm cash, dates and data subscriptions as desired. /// /// Algorithm instance /// Brokerage instance /// Algorithm job /// Boolean true on successfully initializing the algorithm public bool Setup(IAlgorithm algorithm, out IBrokerage brokerage, AlgorithmNodePacket baseJob) { var job = baseJob as BacktestNodePacket; brokerage = new Brokerage(); //Not used. if (algorithm == null) { Errors.Add("Could not create instance of algorithm"); return false; } //Make sure the algorithm start date ok. if (job.PeriodStart == null) { Errors.Add("Algorithm start date is null"); return false; } //Execute the initialize code: var initializeComplete = Isolator.ExecuteWithTimeLimit(TimeSpan.FromSeconds(10), () => { try { //0.0 Set the algorithm time before we even initialize: algorithm.SetDateTime(job.PeriodStart); //1.0 Initialise the algorithm, get the required data: algorithm.Initialize(); //1.2 Set the algorithm to locked to avoid messing with cash: algorithm.SetLocked(); } catch (Exception err) { Errors.Add("Failed to initialize algorithm: Initialize(): " + err.Message); } }); //Before continuing, detect if this is ready: if (!initializeComplete) return false; //Calculate the max runtime for the strategy _maxRuntime = GetMaximumRuntime(job.PeriodStart, job.PeriodFinish, algorithm.SubscriptionManager.Count); //Get starting capital: _startingCaptial = algorithm.Portfolio.Cash; //Max Orders: 100 per day: _maxOrders = (int)(job.PeriodFinish - job.PeriodStart).TotalDays * 100; //Starting date of the algorithm: _startingDate = job.PeriodStart; //Put into log for debugging: Log.Trace("SetUp Backtesting: User: " + job.UserId + " ProjectId: " + job.ProjectId + " AlgoId: " + job.AlgorithmId); Log.Trace("Dates: Start: " + job.PeriodStart.ToShortDateString() + " End: " + job.PeriodFinish.ToShortDateString() + " Cash: " + _startingCaptial.ToString("C")); if (Errors.Count > 0) { initializeComplete = false; } return initializeComplete; } /// /// Calculate the maximum runtime for this algorithm job. /// /// State date of the algorithm /// End date of the algorithm /// Number of data feeds the user has requested /// Timespan maximum run period private TimeSpan GetMaximumRuntime(DateTime start, DateTime finish, int subscriptionCount) { double maxRunTime = 0; var jobDays = (finish - start).TotalDays; maxRunTime = 10 * subscriptionCount * jobDays; //Rationalize: if ((maxRunTime / 3600) > 12) { //12 hours maximum maxRunTime = 3600 * 12; } else if (maxRunTime < 60) { //If less than 60 seconds. maxRunTime = 60; } Log.Trace("BacktestingSetupHandler.GetMaxRunTime(): Job Days: " + jobDays + " Max Runtime: " + Math.Round(maxRunTime / 60) + " min"); //Override for windows: if (OS.IsWindows) { maxRunTime = 24 * 60 * 60; } return TimeSpan.FromSeconds(maxRunTime); } /// /// Setup error handlers for the backtest. /// /// Result handler /// Brokerage interface /// Boolean true on successful setup /// Not used in a backtesting setup handler. This is primarily for setting up brokerage error handler functions public bool SetupErrorHandler(IResultHandler results, IBrokerage brokerage) { return true; } } // End Result Handler Thread: } // End Namespace