/*
* 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