/*
* 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 System.Collections.Generic;
using System.Linq;
using NodaTime;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Algorithm
{
public partial class QCAlgorithm
{
///
/// Gets or sets the history provider for the algorithm
///
public IHistoryProvider HistoryProvider
{
get;
set;
}
///
/// Gets whether or not this algorithm is still warming up
///
public bool IsWarmingUp
{
get;
private set;
}
///
/// Sets the warm up period to the specified value
///
/// The amount of time to warm up, this does not take into account market hours/weekends
public void SetWarmup(TimeSpan timeSpan)
{
_warmupBarCount = null;
_warmupTimeSpan = timeSpan;
_warmupResolution = null;
}
///
/// Sets the warm up period to the specified value
///
/// The amount of time to warm up, this does not take into account market hours/weekends
public void SetWarmUp(TimeSpan timeSpan)
{
SetWarmup(timeSpan);
}
///
/// Sets the warm up period to the specified value
///
/// The amount of time to warm up, this does not take into account market hours/weekends
/// The resolution to request
public void SetWarmup(TimeSpan timeSpan, Resolution resolution)
{
_warmupBarCount = null;
_warmupTimeSpan = timeSpan;
_warmupResolution = resolution;
}
///
/// Sets the warm up period to the specified value
///
/// The amount of time to warm up, this does not take into account market hours/weekends
/// The resolution to request
public void SetWarmUp(TimeSpan timeSpan, Resolution resolution)
{
SetWarmup(timeSpan, resolution);
}
///
/// Sets the warm up period by resolving a start date that would send that amount of data into
/// the algorithm. The highest (smallest) resolution in the securities collection will be used.
/// For example, if an algorithm has minute and daily data and 200 bars are requested, that would
/// use 200 minute bars.
///
/// The number of data points requested for warm up
public void SetWarmup(int barCount)
{
_warmupTimeSpan = null;
_warmupBarCount = barCount;
_warmupResolution = null;
}
///
/// Sets the warm up period by resolving a start date that would send that amount of data into
/// the algorithm. The highest (smallest) resolution in the securities collection will be used.
/// For example, if an algorithm has minute and daily data and 200 bars are requested, that would
/// use 200 minute bars.
///
/// The number of data points requested for warm up
public void SetWarmUp(int barCount)
{
SetWarmup(barCount);
}
///
/// Sets the warm up period by resolving a start date that would send that amount of data into
/// the algorithm.
///
/// The number of data points requested for warm up
/// The resolution to request
public void SetWarmup(int barCount, Resolution resolution)
{
_warmupTimeSpan = null;
_warmupBarCount = barCount;
_warmupResolution = resolution;
}
///
/// Sets the warm up period by resolving a start date that would send that amount of data into
/// the algorithm.
///
/// The number of data points requested for warm up
/// The resolution to request
public void SetWarmUp(int barCount, Resolution resolution)
{
SetWarmup(barCount, resolution);
}
///
/// Sets to false to indicate this algorithm has finished its warm up
///
public void SetFinishedWarmingUp()
{
IsWarmingUp = false;
// notify the algorithm
OnWarmupFinished();
}
///
/// Message for exception that is thrown when the implicit conversion between symbol and string fails
///
private readonly string _symbolEmptyErrorMessage = "Cannot create history for the given ticker. " +
"Either explicitly use a symbol object to make the history request " +
"or ensure the symbol has been added using the AddSecurity() method before making the history request.";
///
/// Gets the history requests required for provide warm up data for the algorithm
///
///
public IEnumerable GetWarmupHistoryRequests()
{
if (_warmupBarCount.HasValue)
{
return CreateBarCountHistoryRequests(Securities.Keys, _warmupBarCount.Value, _warmupResolution);
}
if (_warmupTimeSpan.HasValue)
{
var end = UtcTime.ConvertFromUtc(TimeZone);
return CreateDateRangeHistoryRequests(Securities.Keys, end - _warmupTimeSpan.Value, end, _warmupResolution);
}
// if not warmup requested return nothing
return Enumerable.Empty();
}
///
/// Get the history for all configured securities over the requested span.
/// This will use the resolution and other subscription settings for each security.
/// The symbols must exist in the Securities collection.
///
/// The span over which to request data. This is a calendar span, so take into consideration weekends and such
/// The resolution to request
/// An enumerable of slice containing data over the most recent span for all configured securities
public IEnumerable History(TimeSpan span, Resolution? resolution = null)
{
return History(Securities.Keys, Time - span, Time, resolution).Memoize();
}
///
/// Get the history for all configured securities over the requested span.
/// This will use the resolution and other subscription settings for each security.
/// The symbols must exist in the Securities collection.
///
/// The number of bars to request
/// The resolution to request
/// An enumerable of slice containing data over the most recent span for all configured securities
public IEnumerable History(int periods, Resolution? resolution = null)
{
return History(Securities.Keys, periods, resolution).Memoize();
}
///
/// Gets the historical data for all symbols of the requested type over the requested span.
/// The symbol's configured values for resolution and fill forward behavior will be used
/// The symbols must exist in the Securities collection.
///
/// The span over which to retrieve recent historical data
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable> History(TimeSpan span, Resolution? resolution = null)
where T : IBaseData
{
return History(Securities.Keys, span, resolution).Memoize();
}
///
/// Gets the historical data for the specified symbols over the requested span.
/// The symbols must exist in the Securities collection.
///
/// The data type of the symbols
/// The symbols to retrieve historical data for
/// The span over which to retrieve recent historical data
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable> History(IEnumerable symbols, TimeSpan span, Resolution? resolution = null)
where T : IBaseData
{
return History(symbols, Time - span, Time, resolution).Memoize();
}
///
/// Gets the historical data for the specified symbols. The exact number of bars will be returned for
/// each symbol. This may result in some data start earlier/later than others due to when various
/// exchanges are open. The symbols must exist in the Securities collection.
///
/// The data type of the symbols
/// The symbols to retrieve historical data for
/// The number of bars to request
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable> History(IEnumerable symbols, int periods, Resolution? resolution = null)
where T : IBaseData
{
var requests = symbols.Select(x =>
{
var config = GetMatchingSubscription(x, typeof(T));
if (config == null) return null;
var res = GetResolution(x, resolution);
var start = GetStartTimeAlgoTz(x, periods, res).ConvertToUtc(TimeZone);
return CreateHistoryRequest(config, start, UtcTime.RoundDown(res.Value.ToTimeSpan()), res);
});
return History(requests.Where(x => x != null)).Get().Memoize();
}
///
/// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection.
///
/// The data type of the symbols
/// The symbols to retrieve historical data for
/// The start time in the algorithm's time zone
/// The end time in the algorithm's time zone
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable> History(IEnumerable symbols, DateTime start, DateTime end, Resolution? resolution = null)
where T : IBaseData
{
var requests = symbols.Select(x =>
{
var config = GetMatchingSubscription(x, typeof(T));
if (config == null) return null;
return CreateHistoryRequest(config, start, end, resolution);
});
return History(requests.Where(x => x != null)).Get().Memoize();
}
///
/// Gets the historical data for the specified symbol over the request span. The symbol must exist in the Securities collection.
///
/// The data type of the symbol
/// The symbol to retrieve historical data for
/// The span over which to retrieve recent historical data
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable History(Symbol symbol, TimeSpan span, Resolution? resolution = null)
where T : IBaseData
{
return History(symbol, Time - span, Time, resolution).Memoize();
}
///
/// Gets the historical data for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// The symbol to retrieve historical data for
/// The number of bars to request
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable History(Symbol symbol, int periods, Resolution? resolution = null)
{
if (symbol == null) throw new ArgumentException(_symbolEmptyErrorMessage);
var securityType = symbol.ID.SecurityType;
if (securityType == SecurityType.Forex || securityType == SecurityType.Cfd)
{
Error("Calling History method on a Forex or CFD security will return an empty result. Please use the generic version with QuoteBar type parameter.");
}
resolution = GetResolution(symbol, resolution);
var start = GetStartTimeAlgoTz(symbol, periods, resolution);
return History(new[] { symbol }, start, Time.RoundDown(resolution.Value.ToTimeSpan()), resolution).Get(symbol).Memoize();
}
///
/// Gets the historical data for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// The data type of the symbol
/// The symbol to retrieve historical data for
/// The number of bars to request
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable History(Symbol symbol, int periods, Resolution? resolution = null)
where T : IBaseData
{
if (resolution == Resolution.Tick) throw new ArgumentException("History functions that accept a 'periods' parameter can not be used with Resolution.Tick");
if (symbol == null) throw new ArgumentException(_symbolEmptyErrorMessage);
// verify the types match
var requestedType = typeof(T);
var config = GetMatchingSubscription(symbol, requestedType);
if (config == null)
{
var actualType = Securities[symbol].Subscriptions.Select(x => x.Type.Name).DefaultIfEmpty("[None]").FirstOrDefault();
throw new ArgumentException("The specified security is not of the requested type. Symbol: " + symbol.ToString() + " Requested Type: " + requestedType.Name + " Actual Type: " + actualType);
}
resolution = GetResolution(symbol, resolution);
var start = GetStartTimeAlgoTz(symbol, periods, resolution);
return History(symbol, start, Time.RoundDown(resolution.Value.ToTimeSpan()), resolution).Memoize();
}
///
/// Gets the historical data for the specified symbol between the specified dates. The symbol must exist in the Securities collection.
///
/// The symbol to retrieve historical data for
/// The start time in the algorithm's time zone
/// The end time in the algorithm's time zone
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable History(Symbol symbol, DateTime start, DateTime end, Resolution? resolution = null)
where T : IBaseData
{
if (symbol == null) throw new ArgumentException(_symbolEmptyErrorMessage);
// verify the types match
var requestedType = typeof(T);
var config = GetMatchingSubscription(symbol, requestedType);
if (config == null)
{
var actualType = Securities[symbol].Subscriptions.Select(x => x.Type.Name).DefaultIfEmpty("[None]").FirstOrDefault();
throw new ArgumentException("The specified security is not of the requested type. Symbol: " + symbol.ToString() + " Requested Type: " + requestedType.Name + " Actual Type: " + actualType);
}
var request = CreateHistoryRequest(config, start, end, resolution);
return History(request).Get(symbol).Memoize();
}
///
/// Gets the historical data for the specified symbol over the request span. The symbol must exist in the Securities collection.
///
/// The symbol to retrieve historical data for
/// The span over which to retrieve recent historical data
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable History(Symbol symbol, TimeSpan span, Resolution? resolution = null)
{
var securityType = symbol.ID.SecurityType;
if (securityType == SecurityType.Forex || securityType == SecurityType.Cfd)
{
Error("Calling History method on a Forex or CFD security will return an empty result. Please use the generic version with QuoteBar type parameter.");
}
return History(new[] { symbol }, span, resolution).Get(symbol).Memoize();
}
///
/// Gets the historical data for the specified symbol over the request span. The symbol must exist in the Securities collection.
///
/// The symbol to retrieve historical data for
/// The start time in the algorithm's time zone
/// The end time in the algorithm's time zone
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable History(Symbol symbol, DateTime start, DateTime end, Resolution? resolution = null)
{
var securityType = symbol.ID.SecurityType;
if (securityType == SecurityType.Forex || securityType == SecurityType.Cfd)
{
Error("Calling History method on a Forex or CFD security will return an empty result. Please use the generic version with QuoteBar type parameter.");
}
return History(new[] { symbol }, start, end, resolution).Get(symbol).Memoize();
}
///
/// Gets the historical data for the specified symbols over the requested span.
/// The symbol's configured values for resolution and fill forward behavior will be used
/// The symbols must exist in the Securities collection.
///
/// The symbols to retrieve historical data for
/// The span over which to retrieve recent historical data
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable History(IEnumerable symbols, TimeSpan span, Resolution? resolution = null)
{
return History(symbols, Time - span, Time, resolution).Memoize();
}
///
/// Gets the historical data for the specified symbols. The exact number of bars will be returned for
/// each symbol. This may result in some data start earlier/later than others due to when various
/// exchanges are open. The symbols must exist in the Securities collection.
///
/// The symbols to retrieve historical data for
/// The number of bars to request
/// The resolution to request
/// An enumerable of slice containing the requested historical data
public IEnumerable History(IEnumerable symbols, int periods, Resolution? resolution = null)
{
if (resolution == Resolution.Tick) throw new ArgumentException("History functions that accept a 'periods' parameter can not be used with Resolution.Tick");
return History(CreateBarCountHistoryRequests(symbols, periods, resolution)).Memoize();
}
///
/// Gets the historical data for the specified symbols between the specified dates. The symbols must exist in the Securities collection.
///
/// The symbols to retrieve historical data for
/// The start time in the algorithm's time zone
/// The end time in the algorithm's time zone
/// The resolution to request
/// True to fill forward missing data, false otherwise
/// True to include extended market hours data, false otherwise
/// An enumerable of slice containing the requested historical data
public IEnumerable History(IEnumerable symbols, DateTime start, DateTime end, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarket = null)
{
return History(CreateDateRangeHistoryRequests(symbols, start, end, resolution, fillForward, extendedMarket)).Memoize();
}
///
/// Gets the start time required for the specified bar count in terms of the algorithm's time zone
///
private DateTime GetStartTimeAlgoTz(Symbol symbol, int periods, Resolution? resolution = null)
{
Security security;
resolution = GetResolution(symbol, resolution);
var exchange = GetExchangeHours(symbol);
var isExtendedMarketHours = Securities.TryGetValue(symbol, out security) ? security.IsExtendedMarketHours : false;
var timeSpan = resolution.Value.ToTimeSpan();
// make this a minimum of one second
timeSpan = timeSpan < QuantConnect.Time.OneSecond ? QuantConnect.Time.OneSecond : timeSpan;
var localStartTime = QuantConnect.Time.GetStartTimeForTradeBars(exchange, UtcTime.ConvertFromUtc(exchange.TimeZone), timeSpan, periods, isExtendedMarketHours);
return localStartTime.ConvertTo(exchange.TimeZone, TimeZone);
}
///
/// Executes the specified history request
///
/// the history request to execute
/// An enumerable of slice satisfying the specified history request
public IEnumerable History(HistoryRequest request)
{
return History(new[] { request }).Memoize();
}
///
/// Executes the specified history requests
///
/// the history requests to execute
/// An enumerable of slice satisfying the specified history request
public IEnumerable History(IEnumerable requests)
{
return History(requests, TimeZone).Memoize();
}
///
/// Get the last known price using the history provider.
/// Useful for seeding securities with the correct price
///
/// object for which to retrieve historical data
/// A single object with the last known price
public BaseData GetLastKnownPrice(Security security)
{
if (security.Symbol.IsCanonical() || HistoryProvider == null)
{
return null;
}
// For speed and memory usage, use Resolution.Minute as the minimum resolution
var resolution = (Resolution)Math.Max((int)Resolution.Minute, (int)security.Resolution);
var startTime = GetStartTimeAlgoTzForSecurity(security, 1, resolution);
var endTime = Time.RoundDown(resolution.ToTimeSpan());
// request QuoteBar for Options and Futures
var dataType = typeof(BaseData);
if (security.Type == SecurityType.Option || security.Type == SecurityType.Future)
{
dataType = LeanData.GetDataType(resolution, TickType.Quote);
}
// Get the config with the largest resolution
var subscriptionDataConfig = GetMatchingSubscription(security.Symbol, dataType);
// if subscription resolution is Tick, we also need to update the data type from Tick to TradeBar/QuoteBar
if (subscriptionDataConfig != null && subscriptionDataConfig.Resolution == Resolution.Tick)
{
dataType = LeanData.GetDataType(resolution, subscriptionDataConfig.TickType);
subscriptionDataConfig = new SubscriptionDataConfig(subscriptionDataConfig, dataType, resolution: resolution);
}
var request = new HistoryRequest(
startTime.ConvertToUtc(_localTimeKeeper.TimeZone),
endTime.ConvertToUtc(_localTimeKeeper.TimeZone),
subscriptionDataConfig == null ? typeof(TradeBar) : subscriptionDataConfig.Type,
security.Symbol,
resolution,
security.Exchange.Hours,
MarketHoursDatabase.FromDataFolder().GetDataTimeZone(security.Symbol.ID.Market, security.Symbol, security.Symbol.SecurityType),
resolution,
security.IsExtendedMarketHours,
security.IsCustomData(),
security.DataNormalizationMode,
subscriptionDataConfig == null ? LeanData.GetCommonTickTypeForCommonDataTypes(typeof(TradeBar), security.Type) : subscriptionDataConfig.TickType
);
var history = History(new List { request }).ToList();
if (history.Any() && history.First().Values.Any())
{
return history.First().Values.First();
}
return null;
}
///
/// Gets the start time required for the specified bar count for a security in terms of the algorithm's time zone
/// Used when the security has not yet been subscribed to
///
private DateTime GetStartTimeAlgoTzForSecurity(Security security, int periods, Resolution? resolution = null)
{
var timeSpan = (resolution ?? security.Resolution).ToTimeSpan();
// make this a minimum of one second
timeSpan = timeSpan < QuantConnect.Time.OneSecond ? QuantConnect.Time.OneSecond : timeSpan;
var localStartTime = QuantConnect.Time.GetStartTimeForTradeBars(security.Exchange.Hours, UtcTime.ConvertFromUtc(security.Exchange.TimeZone), timeSpan, periods, security.IsExtendedMarketHours);
return localStartTime.ConvertTo(security.Exchange.TimeZone, TimeZone);
}
private IEnumerable History(IEnumerable requests, DateTimeZone timeZone)
{
var sentMessage = false;
// filter out any universe securities that may have made it this far
var reqs = requests.Where(hr => !UniverseManager.ContainsKey(hr.Symbol)) .ToList();
foreach (var request in reqs)
{
// prevent future requests
if (request.EndTimeUtc > UtcTime)
{
request.EndTimeUtc = UtcTime;
if (request.StartTimeUtc > request.EndTimeUtc)
{
request.StartTimeUtc = request.EndTimeUtc;
}
if (!sentMessage)
{
sentMessage = true;
Debug("Request for future history modified to end now.");
}
}
}
// filter out future data to prevent look ahead bias
return ((IAlgorithm)this).HistoryProvider.GetHistory(reqs, timeZone);
}
///
/// Helper method to create history requests from a date range
///
private IEnumerable CreateDateRangeHistoryRequests(IEnumerable symbols, DateTime startAlgoTz, DateTime endAlgoTz, Resolution? resolution = null, bool? fillForward = null, bool? extendedMarket = null)
{
return symbols.Where(x => !x.IsCanonical()).SelectMany(x =>
{
var requests = new List();
foreach (var config in GetMatchingSubscriptions(x, typeof(BaseData)))
{
var request = CreateHistoryRequest(config, startAlgoTz, endAlgoTz, resolution);
// apply overrides
var res = GetResolution(x, resolution);
if (fillForward.HasValue) request.FillForwardResolution = fillForward.Value ? res : null;
if (extendedMarket.HasValue) request.IncludeExtendedMarketHours = extendedMarket.Value;
requests.Add(request);
}
return requests;
});
}
///
/// Helper methods to create a history request for the specified symbols and bar count
///
private IEnumerable CreateBarCountHistoryRequests(IEnumerable symbols, int periods, Resolution? resolution = null)
{
return symbols.Where(x => !x.IsCanonical()).SelectMany(x =>
{
var res = GetResolution(x, resolution);
var start = GetStartTimeAlgoTz(x, periods, res);
var end = Time.RoundDown(res.Value.ToTimeSpan());
return GetMatchingSubscriptions(x, typeof(BaseData))
.Select(config => CreateHistoryRequest(config, start, end, resolution));
});
}
private HistoryRequest CreateHistoryRequest(SubscriptionDataConfig subscription, DateTime startAlgoTz, DateTime endAlgoTz, Resolution? resolution)
{
resolution = resolution ?? subscription.Resolution;
// find the correct data type for the history request
var dataType = subscription.IsCustomData ? subscription.Type : LeanData.GetDataType(resolution.Value, subscription.TickType);
var request = new HistoryRequest(subscription, GetExchangeHours(subscription.Symbol), startAlgoTz.ConvertToUtc(TimeZone), endAlgoTz.ConvertToUtc(TimeZone))
{
DataType = dataType,
Resolution = resolution.Value,
FillForwardResolution = subscription.FillDataForward ? resolution : null,
TickType = subscription.TickType
};
return request;
}
private SubscriptionDataConfig GetMatchingSubscription(Symbol symbol, Type type)
{
// find the first subscription matching the requested type with a higher resolution than requested
return GetMatchingSubscriptions(symbol, type).FirstOrDefault();
}
private IEnumerable GetMatchingSubscriptions(Symbol symbol, Type type)
{
Security security;
if (Securities.TryGetValue(symbol, out security))
{
// find all subscriptions matching the requested type with a higher resolution than requested
return from sub in security.Subscriptions.OrderByDescending(s => s.Resolution)
where type.IsAssignableFrom(sub.Type)
select sub;
}
else
{
var resolution = UniverseSettings.Resolution;
var timeZone = GetExchangeHours(symbol).TimeZone;
var subscriptionDataTypes = SubscriptionManager.LookupSubscriptionConfigDataTypes(symbol.SecurityType, resolution, symbol.IsCanonical());
return subscriptionDataTypes.Select(x => new SubscriptionDataConfig(x.Item1, symbol, resolution, timeZone, timeZone, UniverseSettings.FillForward, UniverseSettings.ExtendedMarketHours, false, false, x.Item2));
}
}
private SecurityExchangeHours GetExchangeHours(Symbol symbol)
{
Security security;
if (Securities.TryGetValue(symbol, out security))
{
return security.Exchange.Hours;
}
return MarketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType).ExchangeHours;
}
private Resolution? GetResolution(Symbol symbol, Resolution? resolution)
{
Security security;
if (Securities.TryGetValue(symbol, out security))
{
return resolution ?? security.Resolution;
}
else
{
return resolution ?? UniverseSettings.Resolution;
}
}
}
}