/* * 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.Data; using QuantConnect.Data.Market; using QuantConnect.Interfaces; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Forex; namespace QuantConnect.Algorithm { /******************************************************** * CLASS DEFINITIONS *********************************************************/ /// /// QC Algorithm Base Class - Handle the basic requirements of a trading algorithm, /// allowing user to focus on event methods. The QCAlgorithm class implements Portfolio, /// Securities, Transactions and Data Subscription Management. /// public partial class QCAlgorithm : MarshalByRefObject, IAlgorithm { /******************************************************** * CLASS PRIVATE VARIABLES *********************************************************/ private DateTime _time; private DateTime _startDate; //Default start and end dates. private DateTime _endDate; //Default end to yesterday private RunMode _runMode = RunMode.Series; private bool _locked; private bool _quit; private bool _liveMode; private string _algorithmId = ""; private List _debugMessages = new List(); private List _logMessages = new List(); private List _errorMessages = new List(); //Error tracking to avoid message flooding: private string _previousDebugMessage = ""; private string _previousErrorMessage = ""; private bool _sentNoDataError = false; /******************************************************** * CLASS CONSTRUCTOR *********************************************************/ /// /// QCAlgorithm Base Class Constructor - Initialize the underlying QCAlgorithm components. /// QCAlgorithm manages the transactions, portfolio, charting and security subscriptions for the users algorithms. /// public QCAlgorithm() { //Initialise the Algorithm Helper Classes: //- Note - ideally these wouldn't be here, but because of the DLL we need to make the classes shared across // the Worker & Algorithm, limiting ability to do anything else. //Initialise Data Manager SubscriptionManager = new SubscriptionManager(); Securities = new SecurityManager(); Transactions = new SecurityTransactionManager(Securities); Portfolio = new SecurityPortfolioManager(Securities, Transactions); Notify = new NotificationManager(false); // Notification manager defaults to disabled. //Initialise Algorithm RunMode to Series - Parallel Mode deprecated: _runMode = RunMode.Series; //Initialise to unlocked: _locked = false; //Initialise Start and End Dates: _startDate = new DateTime(1998, 01, 01); _endDate = DateTime.Now.AddDays(-1); } /******************************************************** * CLASS PUBLIC VARIABLES *********************************************************/ /// /// Security collection is an array of the security objects such as Equities and FOREX. Securities data /// manages the properties of tradeable assets such as price, open and close time and holdings information. /// public SecurityManager Securities { get; set; } /// /// Portfolio object provieds easy access to the underlying security-holding properties; summed together in a way to make them useful. /// This saves the user time by providing common portfolio requests in a single /// public SecurityPortfolioManager Portfolio { get; set; } /// /// Generic Data Manager - Required for compiling all data feeds in order, and passing them into algorithm event methods. /// The subscription manager contains a list of the data feed's we're subscribed to and properties of each data feed. /// public SubscriptionManager SubscriptionManager { get; set; } /// /// Notification Manager for Sending Live Runtime Notifications to users about important events. /// public NotificationManager Notify { get; set; } /// /// Public name for the algorithm as automatically generated by the IDE. Intended for helping distinguish logs by noting /// the algorithm-id. /// /// public string Name { get; set; } /// /// Read-only value for current time frontier of the algorithm and event horizon. /// /// During backtesting this is primarily sourced from the data feed. During live trading the time is updated from the system clock. public DateTime Time { get { return _time; } } /// /// Value of the user set start-date from the backtest. /// /// This property is set with SetStartDate() and defaults to the earliest QuantConnect data available - Jan 1st 1998. It is ignored during live trading /// public DateTime StartDate { get { return _startDate; } } /// /// Value of the user set start-date from the backtest. Controls the period of the backtest. /// /// This property is set with SetEndDate() and defaults to today. It is ignored during live trading. /// public DateTime EndDate { get { return _endDate; } } /// /// Algorithm Id for this backtest or live algorithm. /// /// A unique identifier for public string AlgorithmId { get { return _algorithmId; } } /// /// Control the server setup run style for the backtest: Automatic, Parallel or Series. /// /// /// Series mode runs all days through one computer, allowing memory of the previous days. /// Parallel mode runs all days separately which maximises speed but gives no memory of a previous day trading. /// /// The RunMode enum propert is now obsolete. All algorithms will default to RunMode.Series for series backtests. [Obsolete("The RunMode enum propert is now obsolete. All algorithms will default to RunMode.Series for series backtests.")] public RunMode RunMode { get { return _runMode; } } /// /// Boolean property indicating the algorithm is currently running in live mode. /// /// Intended for use where certain behaviors will be enabled while the algorithm is trading live: such as notification emails, or displaying runtime statistics. public bool LiveMode { get { return _liveMode; } } /// /// Storage for debugging messages before the event handler has passed control back to the Lean Engine. /// /// public List DebugMessages { get { return _debugMessages; } set { _debugMessages = value; } } /// /// Storage for log messages before the event handlers have passed control back to the Lean Engine. /// /// public List LogMessages { get { return _logMessages; } set { _logMessages = value; } } /// /// Gets the run time error from the algorithm, or null if none was encountered. /// public Exception RunTimeError { get; set; } /// /// List of error messages generated by the user's code calling the "Error" function. /// /// This method is best used within a try-catch bracket to handle any runtime errors from a user algorithm. /// public List ErrorMessages { get { return _errorMessages; } set { _errorMessages = value; } } /******************************************************** * CLASS METHODS *********************************************************/ /// /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. /// /// /// /// public virtual void Initialize() { //Setup Required Data throw new NotImplementedException("Please override the Intitialize() method"); } /// /// Event handler for TradeBar data subscriptions packets. This method was deprecated June 2014 and replaced with OnData(TradeBars data) /// /// Dictionary of MarketData Objects /// This method is obsolete, please use 'void OnData(TradeBars data)' instead [Obsolete("'override void OnTradeBar' method is obsolete, please use 'void OnData(TradeBars data)' instead")] public virtual void OnTradeBar(Dictionary data) { //Algorithm Implementation //throw new NotImplementedException("OnTradeBar has been made obsolete. Please use OnData(TradeBars data) instead."); } /// /// Event handler for Tick data subscriptions. This method was deprecated June 2014 and replaced with OnData(Ticks data). /// /// Ticks arriving at the same moment come in a list. Because the "tick" data is actually list ordered within a second, you can get lots of ticks at once. /// This method is obsolete, please use 'void OnData(Ticks data)' instead [Obsolete("'override void OnTick' method is obsolete, please use 'void OnData(Ticks data)' instead")] public virtual void OnTick(Dictionary> data) { //Algorithm Implementation //throw new NotImplementedException("OnTick has been made obsolete. Please use OnData(Ticks data) instead."); } // // Event - v2.0 TRADEBAR EVENT HANDLER: (Pattern) Basic template for user to override when requesting tradebar data. // // //public void OnData(TradeBars data) //{ // //} // // Event - v2.0 TICK EVENT HANDLER: (Pattern) Basic template for user to override when requesting tick data. // // List of Tick Data //public void OnData(Ticks data) //{ // //} /// /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). /// /// Method is called 10 minutes before closing to allow user to close out position. public virtual void OnEndOfDay() { } /// /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). /// /// Asset symbol for this end of day event. Forex and equities have different closing hours. public virtual void OnEndOfDay(string symbol) { } /// /// End of algorithm run event handler. This method is called at the end of a backtest or live trading operation. Intended for closing out logs. /// public virtual void OnEndOfAlgorithm() { } /// /// Order fill event handler. On an order fill update the resulting information is passed to this method. /// /// Order event details containing details of the evemts /// This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects public virtual void OnOrderEvent(OrderEvent orderEvent) { } /// /// Update the interal algorithm time frontier. /// /// For internal use only to advance time. /// Current datetime. public void SetDateTime(DateTime frontier) { _time = frontier; } /// /// Set the RunMode for the Servers. If you are running an overnight algorithm, you must select series. /// Automatic will analyse the selected data, and if you selected only minute data we'll select series for you. /// /// This method is now obsolete and has no replacement. All algorithms now run in Series mode. /// Enum RunMode with options Series, Parallel or Automatic. Automatic scans your requested symbols and resolutions and makes a decision on the fastest analysis [Obsolete("This method is now obsolete and has no replacement. All algorithms now run in Series mode.")] public void SetRunMode(RunMode mode) { if (mode != RunMode.Parallel) return; Debug("Algorithm.SetRunMode(): RunMode-Parallel Type has been deprecated. Series analysis selected instead"); mode = RunMode.Series; } /// /// Set initial cash for the strategy while backtesting. During live mode this value is ignored /// and replaced with the actual cash of your brokerage account. /// /// Starting cash for the strategy backtest /// Alias of SetCash(decimal) public void SetCash(double startingCash) { SetCash((decimal)startingCash); } /// /// Set initial cash for the strategy while backtesting. During live mode this value is ignored /// and replaced with the actual cash of your brokerage account. /// /// Starting cash for the strategy backtest /// Alias of SetCash(decimal) public void SetCash(int startingCash) { SetCash((decimal)startingCash); } /// /// Set initial cash for the strategy while backtesting. During live mode this value is ignored /// and replaced with the actual cash of your brokerage account. /// /// Starting cash for the strategy backtest public void SetCash(decimal startingCash) { if (!_locked) { Portfolio.SetCash(startingCash); } else { throw new Exception("Algorithm.SetCash(): Cannot change cash available after algorithm initialized."); } } /// /// Set the cash for the specified symbol /// /// The cash symbol to set /// Decimal cash value of portfolio /// The current conversion rate for the public void SetCash(string symbol, decimal startingCash, decimal conversionRate) { if (!_locked) { Portfolio.SetCash(symbol, startingCash, conversionRate); } else { throw new Exception("Algorithm.SetCash(): Cannot change cash available after algorithm initialized."); } } /// /// Set the start date for backtest. /// /// Int starting date 1-30 /// Int month starting date /// Int year starting date /// /// Wrapper for SetStartDate(DateTime). /// Must be less than end date. /// Ignored in live trading mode. /// public void SetStartDate(int year, int month, int day) { try { var start = new DateTime(year, month, day); // We really just want the date of the start, so it's 12am of the requested day (first moment of the day) start = start.Date; SetStartDate(start); } catch (Exception err) { throw new Exception("Date Invalid: " + err.Message); } } /// /// Set the end date for a backtest run /// /// Int end date 1-30 /// Int month end date /// Int year end date /// Wrapper for SetEndDate(datetime). /// public void SetEndDate(int year, int month, int day) { try { var end = new DateTime(year, month, day); // we want the end date to be just before the next day (last moment of the day) end = end.Date.AddDays(1).Subtract(TimeSpan.FromTicks(1)); SetEndDate(end); } catch (Exception err) { throw new Exception("Date Invalid: " + err.Message); } } /// /// Set the algorithm id (backtestId or live deployId for the algorithmm). /// /// String Algorithm Id /// Intended for internal QC Lean Engine use only as a setter for AlgorihthmId public void SetAlgorithmId(string algorithmId) { _algorithmId = algorithmId; } /// /// Set the start date for the backtest /// /// Datetime Start date for backtest /// Must be less than end date and within data available /// public void SetStartDate(DateTime start) { //Validate the start date: //1. Check range; if (start < (new DateTime(1900, 01, 01))) { throw new Exception("Please select a start date after January 1st, 1900."); } //2. Check end date greater: if (_endDate != new DateTime()) { if (start > _endDate) { throw new Exception("Please select start date less than end date."); } } //3. Round up and subtract one tick: start = start.RoundDown(TimeSpan.FromDays(1)); //3. Check not locked already: if (!_locked) { _startDate = start; } else { throw new Exception("Algorithm.SetStartDate(): Cannot change start date after algorithm initialized."); } } /// /// Set the end date for a backtest. /// /// Datetime value for end date /// Must be greater than the start date /// public void SetEndDate(DateTime end) { //Validate: //1. Check Range: if (end > DateTime.Now.Date.AddDays(-1)) { end = DateTime.Now.Date.AddDays(-1); } //2. Check start date less: if (_startDate != new DateTime()) { if (end < _startDate) { throw new Exception("Please select end date greater than start date."); } } //3. Make this at the very end of the requested date end = end.RoundDown(TimeSpan.FromDays(1)).AddDays(1).AddTicks(-1); //4. Check not locked already: if (!_locked) { _endDate = end; } else { throw new Exception("Algorithm.SetEndDate(): Cannot change end date after algorithm initialized."); } } /// /// Lock the algorithm initialization to avoid user modifiying cash and data stream subscriptions /// /// Intended for Internal QC Lean Engine use only to prevent accidental manipulation of important properties public void SetLocked() { _locked = true; } /// /// Set live mode state of the algorithm run: Public setter for the algorithm property LiveMode. /// public void SetLiveMode(bool live) { if (!_locked) { _liveMode = live; Notify = new NotificationManager(live); } } /// /// Set the maximum number of assets allowable to ensure good memory usage / avoid linux killing job. /// /// Maximum number of minute level assets the live mode can support with selected server /// Maximum number of second level assets the live mode can support with selected server /// /// Maximum number of tick level assets the live mode can support with selected server /// Sets the live behaviour of the algorithm including the selected server (ram) limits. public void SetAssetLimits(int minuteLimit = 500, int secondLimit = 100, int tickLimit = 30) { if (!_locked) { Securities.SetLimits(minuteLimit, secondLimit, tickLimit); } } /// /// Add specified data to our data subscriptions. QuantConnect will funnel this data to the handle data routine. /// /// MarketType Type: Equity, Commodity, Future or FOREX /// Symbol Reference for the MarketType /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Show the after market data as well public void AddSecurity(SecurityType securityType, string symbol, Resolution resolution = Resolution.Minute, bool fillDataForward = true, bool extendedMarketHours = false) { AddSecurity(securityType, symbol, resolution, fillDataForward, 0, extendedMarketHours); } /// /// Add specified data to required list. QC will funnel this data to the handle data routine. /// /// MarketType Type: Equity, Commodity, Future or FOREX /// Symbol Reference for the MarketType /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// Extended market hours /// AddSecurity(SecurityType securityType, string symbol, Resolution resolution, bool fillDataForward, decimal leverage, bool extendedMarketHours) public void AddSecurity(SecurityType securityType, string symbol, Resolution resolution, bool fillDataForward, decimal leverage, bool extendedMarketHours) { try { if (_locked) { throw new Exception("Algorithm.AddSecurity(): Cannot add another security after algorithm running."); } symbol = symbol.ToUpper(); //If it hasn't been set, use some defaults based on the portfolio type: if (leverage <= 0) { switch (securityType) { case SecurityType.Equity: leverage = 2; //Cash Ac. = 1, RegT Std = 2 or PDT = 4. break; case SecurityType.Forex: leverage = 50; break; } } //Add the symbol to Data Manager -- generate unified data streams for algorithm events var config = SubscriptionManager.Add(securityType, symbol, resolution, fillDataForward, extendedMarketHours); Security security; switch (config.SecurityType) { case SecurityType.Equity: security = new Equity(config, leverage, false); break; case SecurityType.Forex: // decompose the symbol into each currency pair string baseCurrency, quoteCurrency; QuantConnect.Securities.Forex.Forex.DecomposeCurrencyPair(symbol, out baseCurrency, out quoteCurrency); if (!Portfolio.CashBook.ContainsKey(baseCurrency)) { // since we have none it's safe to say the conversion is zero Portfolio.CashBook.Add(baseCurrency, 0, 0); } if (!Portfolio.CashBook.ContainsKey(quoteCurrency)) { // since we have none it's safe to say the conversion is zero Portfolio.CashBook.Add(quoteCurrency, 0, 0); } security = new Forex(Portfolio.CashBook[quoteCurrency], config, leverage, false); break; default: case SecurityType.Base: security = new Security(config, leverage, false); break; } //Add the symbol to Securities Manager -- manage collection of portfolio entities for easy access. Securities.Add(config.Symbol, security); } catch (Exception err) { Error("Algorithm.AddSecurity(): " + err.Message); } } /// /// AddData a new user defined data source, requiring only the minimum config options. /// /// Key/Symbol for data /// Resolution of the data /// Generic type T must implement base data public void AddData(string symbol, Resolution resolution = Resolution.Minute) { if (_locked) return; //Add this new generic data as a tradeable security: // Defaults:extended market hours" = true because we want events 24 hours, // fillforward = false because only want to trigger when there's new custom data. // leverage = 1 because no leverage on nonmarket data? AddData(symbol, resolution, fillDataForward: false, leverage: 1m, isTradeBar: false, hasVolume: false); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// /// Key/Symbol for data /// Resolution of the data /// Set to true if this data has Open, High, Low, and Close properties /// Set to true if this data has a Volume property /// Generic type T must implement base data public void AddData(string symbol, Resolution resolution, bool isTradeBar, bool hasVolume) { if (_locked) return; AddData(symbol, resolution, fillDataForward: false, leverage: 1m, isTradeBar: isTradeBar, hasVolume: hasVolume); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// /// Key/Symbol for data /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// Set to true if this data has Open, High, Low, and Close properties /// Set to true if this data has a Volume property /// Generic type T must implement base data public void AddData(string symbol, Resolution resolution, bool fillDataForward, decimal leverage = 1.0m, bool isTradeBar = false, bool hasVolume = false) { if (_locked) return; symbol = symbol.ToUpper(); //Add this to the data-feed subscriptions var config = SubscriptionManager.Add(typeof(T), SecurityType.Base, symbol, resolution, fillDataForward, extendedMarketHours: true, isTradeBar: isTradeBar, hasVolume: hasVolume); //Add this new generic data as a tradeable security: var security = new Security(config, leverage, true); Securities.Add(symbol, security); } /// /// Send a debug message to the web console: /// /// Message to send to debug console /// /// public void Debug(string message) { if (!_liveMode && (message == "" || _previousDebugMessage == message)) return; _debugMessages.Add(message); _previousDebugMessage = message; } /// /// Added another method for logging if user guessed. /// /// String message to log. /// /// public void Log(string message) { if (message == "") return; _logMessages.Add(message); } /// /// Send a string error message to the Console. /// /// Message to display in errors grid /// /// public void Error(string message) { if (message == "" || _previousErrorMessage == message) return; _errorMessages.Add(message); _previousErrorMessage = message; } /// /// Send a string error message to the Console. /// /// Exception object captured from a try catch loop /// /// public void Error(Exception error) { var message = error.Message; if (message == "" || _previousErrorMessage == message) return; _errorMessages.Add(message); _previousErrorMessage = message; } /// /// Terminate the algorithm after processing the current event handler. /// /// Exit message to display on quitting public void Quit(string message = "") { Debug("Quit(): " + message); _quit = true; } /// /// Set the Quit flag property of the algorithm. /// /// Intended for internal use by the QuantConnect Lean Engine only. /// Boolean quit state /// /// public void SetQuit(bool quit) { _quit = quit; } /// /// Get the quit state of the algorithm /// /// Boolean true if set to quit event loop. /// Intended for internal use by the QuantConnect Lean Engine only. /// /// public bool GetQuit() { return _quit; } } // End Algorithm Template } // End QC Namespace