/*
* 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)
{
SetWarmUp(timeSpan, 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)
{
if (_locked)
{
throw new InvalidOperationException("QCAlgorithm.SetWarmup(): This method cannot be used after algorithm initialized");
}
_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)
{
SetWarmUp(barCount, 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)
{
if (_locked)
{
throw new InvalidOperationException("QCAlgorithm.SetWarmup(): This method cannot be used after algorithm initialized");
}
_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 exchange = GetExchangeHours(x);
var res = GetResolution(x, resolution);
var start = _historyRequestFactory.GetStartTimeAlgoTz(x, periods, res, exchange, config.DataTimeZone);
return _historyRequestFactory.CreateHistoryRequest(config, start, Time, exchange, 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 _historyRequestFactory.CreateHistoryRequest(config, start, end, GetExchangeHours(x), 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);
resolution = GetResolution(symbol, resolution);
var marketHours = GetMarketHours(symbol);
var start = _historyRequestFactory.GetStartTimeAlgoTz(symbol, periods, resolution.Value, marketHours.ExchangeHours, marketHours.DataTimeZone);
return History(symbol, start, Time, resolution);
}
///
/// 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 = _historyRequestFactory.GetStartTimeAlgoTz(symbol, periods, resolution.Value, GetExchangeHours(symbol), config.DataTimeZone);
return History(symbol, start, Time, 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 = _historyRequestFactory.CreateHistoryRequest(config, start, end, GetExchangeHours(symbol), 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)
{
return History(symbol, Time - span, Time, resolution);
}
///
/// 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.");
}
var resolutionToUse = resolution ?? GetResolution(symbol, resolution);
if (resolutionToUse == Resolution.Tick)
{
throw new InvalidOperationException("Calling History method with Resolution.Tick will return an empty result." +
" Please use the generic version with Tick type parameter or provide a list of Symbols to use the Slice history request API.");
}
return History(new[] { symbol }, start, end, resolutionToUse).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();
}
///
/// 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;
}
var configs = SubscriptionManager.SubscriptionDataConfigService
.GetSubscriptionDataConfigs(security.Symbol);
var dataTimeZone = MarketHoursDatabase
.GetDataTimeZone(security.Symbol.ID.Market, security.Symbol, security.Symbol.SecurityType);
// For speed and memory usage, use Resolution.Minute as the minimum resolution
var resolution = (Resolution)Math.Max((int)Resolution.Minute, (int)configs.GetHighestResolution());
var isExtendedMarketHours = configs.IsExtendedMarketHours();
// request QuoteBar for Options, Futures, and Futures Options
var dataType = typeof(BaseData);
if (security.Type == SecurityType.Option || security.Type == SecurityType.Future || security.Type == SecurityType.FutureOption)
{
dataType = LeanData.GetDataType(resolution, TickType.Quote);
}
// Get the config with the largest resolution
var subscriptionDataConfig = GetMatchingSubscription(security.Symbol, dataType);
TickType tickType;
if (subscriptionDataConfig == null)
{
dataType = typeof(TradeBar);
tickType = LeanData.GetCommonTickTypeForCommonDataTypes(dataType, security.Type);
}
else
{
// if subscription resolution is Tick, we also need to update the data type from Tick to TradeBar/QuoteBar
if (subscriptionDataConfig.Resolution == Resolution.Tick)
{
dataType = LeanData.GetDataType(resolution, subscriptionDataConfig.TickType);
subscriptionDataConfig = new SubscriptionDataConfig(subscriptionDataConfig, dataType, resolution: resolution);
}
dataType = subscriptionDataConfig.Type;
tickType = subscriptionDataConfig.TickType;
}
Func getLastKnownPriceForPeriods = backwardsPeriods =>
{
var startTimeUtc = _historyRequestFactory
.GetStartTimeAlgoTz(security.Symbol, backwardsPeriods, resolution, security.Exchange.Hours, dataTimeZone)
.ConvertToUtc(_localTimeKeeper.TimeZone);
var request = new HistoryRequest(
startTimeUtc,
UtcTime,
dataType,
security.Symbol,
resolution,
security.Exchange.Hours,
dataTimeZone,
resolution,
isExtendedMarketHours,
configs.IsCustomData(),
configs.DataNormalizationMode(),
tickType
);
BaseData result = null;
History(new List { request })
.PushThrough(bar =>
{
if (!bar.IsFillForward)
result = bar;
});
return result;
};
var lastKnownPrice = getLastKnownPriceForPeriods(1);
if (lastKnownPrice != null)
{
return lastKnownPrice;
}
// If the first attempt to get the last know price returns null, it maybe the case of an illiquid security.
// We increase the look-back period for this case accordingly to the resolution to cover 3 trading days
var periods =
resolution == Resolution.Daily ? 3 :
resolution == Resolution.Hour ? 24 : 1440;
return getLastKnownPriceForPeriods(periods);
}
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), resolution))
{
var request = _historyRequestFactory.CreateHistoryRequest(config, startAlgoTz, endAlgoTz, GetExchangeHours(x), resolution);
// apply overrides
var res = GetResolution(x, resolution);
if (fillForward.HasValue) request.FillForwardResolution = fillForward.Value ? res : (Resolution?)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 exchange = GetExchangeHours(x);
var configs = GetMatchingSubscriptions(x, typeof(BaseData), resolution).ToList();
if (!configs.Any())
{
return Enumerable.Empty();
}
var start = _historyRequestFactory.GetStartTimeAlgoTz(x, periods, res, exchange, configs.First().DataTimeZone);
var end = Time;
return configs.Select(config => _historyRequestFactory.CreateHistoryRequest(config, start, end, exchange, res));
});
}
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, Resolution? resolution = null)
{
Security security;
if (Securities.TryGetValue(symbol, out security))
{
// find all subscriptions matching the requested type with a higher resolution than requested
var matchingSubscriptions = from sub in security.Subscriptions.OrderByDescending(s => s.Resolution)
where type.IsAssignableFrom(sub.Type)
select sub;
if (resolution.HasValue
&& (resolution == Resolution.Daily || resolution == Resolution.Hour)
&& symbol.SecurityType == SecurityType.Equity)
{
// for Daily and Hour resolution, for equities, we have to
// filter out any existing subscriptions that could be of Quote type
// This could happen if they were Resolution.Minute/Second/Tick
matchingSubscriptions = matchingSubscriptions.Where(s => s.TickType != TickType.Quote);
}
return matchingSubscriptions;
}
else
{
var entry = MarketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType);
resolution = GetResolution(symbol, resolution);
return SubscriptionManager
.LookupSubscriptionConfigDataTypes(symbol.SecurityType, resolution.Value, symbol.IsCanonical())
.Select(x => new SubscriptionDataConfig(
x.Item1,
symbol,
resolution.Value,
entry.DataTimeZone,
entry.ExchangeHours.TimeZone,
UniverseSettings.FillForward,
UniverseSettings.ExtendedMarketHours,
true,
false,
x.Item2,
true,
UniverseSettings.DataNormalizationMode));
}
}
private SecurityExchangeHours GetExchangeHours(Symbol symbol)
{
return GetMarketHours(symbol).ExchangeHours;
}
private MarketHoursDatabase.Entry GetMarketHours(Symbol symbol)
{
var hoursEntry = MarketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType);
// user can override the exchange hours in algorithm, i.e. HistoryAlgorithm
Security security;
if (Securities.TryGetValue(symbol, out security))
{
return new MarketHoursDatabase.Entry(hoursEntry.DataTimeZone, security.Exchange.Hours);
}
return hoursEntry;
}
private Resolution GetResolution(Symbol symbol, Resolution? resolution)
{
Security security;
if (Securities.TryGetValue(symbol, out security))
{
return resolution ?? SubscriptionManager.SubscriptionDataConfigService
.GetSubscriptionDataConfigs(symbol)
.GetHighestResolution();
}
else
{
return resolution ?? UniverseSettings.Resolution;
}
}
}
}