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