/* * 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.Consolidators; using QuantConnect.Data.Market; using QuantConnect.Interfaces; using QuantConnect.Util; namespace QuantConnect.Data { /// /// Enumerable Subscription Management Class /// public class SubscriptionManager { private IAlgorithmSubscriptionManager _subscriptionManager; /// /// Instance that implements /// public ISubscriptionDataConfigService SubscriptionDataConfigService => _subscriptionManager; /// /// Returns an IEnumerable of Subscriptions /// public IEnumerable Subscriptions => _subscriptionManager.SubscriptionManagerSubscriptions; /// /// Flags the existence of custom data in the subscriptions /// public bool HasCustomData { get { return _subscriptionManager.HasCustomData; } set { _subscriptionManager.HasCustomData = value; } } /// /// The different each supports /// public Dictionary> AvailableDataTypes => _subscriptionManager.AvailableDataTypes; /// /// Get the count of assets: /// public int Count => _subscriptionManager.SubscriptionManagerCount(); /// /// Add Market Data Required (Overloaded method for backwards compatibility). /// /// Symbol of the asset we're like /// Resolution of Asset Required /// The time zone the subscription's data is time stamped in /// /// Specifies the time zone of the exchange for the security this subscription is for. This /// is this output time zone, that is, the time zone that will be used on BaseData instances /// /// True if this is custom user supplied data, false for normal QC data /// when there is no data pass the last tradebar forward /// Request premarket data as well when true /// /// The newly created or existing instance if it already existed /// public SubscriptionDataConfig Add( Symbol symbol, Resolution resolution, DateTimeZone timeZone, DateTimeZone exchangeTimeZone, bool isCustomData = false, bool fillDataForward = true, bool extendedMarketHours = false ) { //Set the type: market data only comes in two forms -- ticks(trade by trade) or tradebar(time summaries) var dataType = typeof(TradeBar); if (resolution == Resolution.Tick) { dataType = typeof(Tick); } var tickType = LeanData.GetCommonTickTypeForCommonDataTypes(dataType, symbol.SecurityType); return Add(dataType, tickType, symbol, resolution, timeZone, exchangeTimeZone, isCustomData, fillDataForward, extendedMarketHours); } /// /// Add Market Data Required - generic data typing support as long as Type implements BaseData. /// /// Set the type of the data we're subscribing to. /// Tick type for the subscription. /// Symbol of the asset we're like /// Resolution of Asset Required /// The time zone the subscription's data is time stamped in /// /// Specifies the time zone of the exchange for the security this subscription is for. This /// is this output time zone, that is, the time zone that will be used on BaseData instances /// /// True if this is custom user supplied data, false for normal QC data /// when there is no data pass the last tradebar forward /// Request premarket data as well when true /// /// Set to true to prevent data from this subscription from being sent into the algorithm's /// OnData events /// /// /// True if this subscription should have filters applied to it (market hours/user /// filters from security), false otherwise /// /// Define how data is normalized /// /// The newly created or existing instance if it already existed /// public SubscriptionDataConfig Add( Type dataType, TickType tickType, Symbol symbol, Resolution resolution, DateTimeZone dataTimeZone, DateTimeZone exchangeTimeZone, bool isCustomData, bool fillDataForward = true, bool extendedMarketHours = false, bool isInternalFeed = false, bool isFilteredSubscription = true, DataNormalizationMode dataNormalizationMode = DataNormalizationMode.Adjusted ) { return SubscriptionDataConfigService.Add(symbol, resolution, fillDataForward, extendedMarketHours, isFilteredSubscription, isInternalFeed, isCustomData, new List> {new Tuple(dataType, tickType)}, dataNormalizationMode).First(); } /// /// Add a consolidator for the symbol /// /// Symbol of the asset to consolidate /// The consolidator public void AddConsolidator(Symbol symbol, IDataConsolidator consolidator) { // Find the right subscription and add the consolidator to it var subscriptions = Subscriptions.Where(x => x.Symbol == symbol).ToList(); if (subscriptions.Count == 0) { // If we made it here it is because we never found the symbol in the subscription list throw new ArgumentException("Please subscribe to this symbol before adding a consolidator for it. Symbol: " + symbol.Value); } foreach (var subscription in subscriptions) { // we need to be able to pipe data directly from the data feed into the consolidator if (IsSubscriptionValidForConsolidator(subscription, consolidator)) { subscription.Consolidators.Add(consolidator); return; } } throw new ArgumentException("Type mismatch found between consolidator and symbol. " + $"Symbol: {symbol.Value} does not support input type: {consolidator.InputType.Name}. " + $"Supported types: {string.Join(",", subscriptions.Select(x => x.Type.Name))}."); } /// /// Removes the specified consolidator for the symbol /// /// The symbol the consolidator is receiving data from /// The consolidator instance to be removed public void RemoveConsolidator(Symbol symbol, IDataConsolidator consolidator) { // remove consolidator from each subscription foreach (var subscription in Subscriptions.Where(x => x.Symbol == symbol)) { subscription.Consolidators.Remove(consolidator); } // dispose of the consolidator to remove any remaining event handlers consolidator.DisposeSafely(); } /// /// Hard code the set of default available data feeds /// public static Dictionary> DefaultDataTypes() { return new Dictionary> { {SecurityType.Base, new List {TickType.Trade}}, {SecurityType.Forex, new List {TickType.Quote}}, {SecurityType.Equity, new List {TickType.Trade}}, {SecurityType.Option, new List {TickType.Quote, TickType.Trade, TickType.OpenInterest}}, {SecurityType.Cfd, new List {TickType.Quote}}, {SecurityType.Future, new List {TickType.Quote, TickType.Trade, TickType.OpenInterest}}, {SecurityType.Commodity, new List {TickType.Trade}}, {SecurityType.Crypto, new List {TickType.Trade, TickType.Quote}} }; } /// /// Get the available data types for a security /// public IReadOnlyList GetDataTypesForSecurity(SecurityType securityType) { return AvailableDataTypes[securityType]; } /// /// Get the data feed types for a given /// /// The used to determine the types /// The resolution of the data requested /// Indicates whether the security is Canonical (future and options) /// Types that should be added to the public List> LookupSubscriptionConfigDataTypes( SecurityType symbolSecurityType, Resolution resolution, bool isCanonical ) { return _subscriptionManager.LookupSubscriptionConfigDataTypes(symbolSecurityType, resolution, isCanonical); } /// /// Sets the Subscription Manager /// public void SetDataManager(IAlgorithmSubscriptionManager subscriptionManager) { _subscriptionManager = subscriptionManager; } /// /// Checks if the subscription is valid for the consolidator /// /// The subscription configuration /// The consolidator /// true if the subscription is valid for the consolidator public static bool IsSubscriptionValidForConsolidator(SubscriptionDataConfig subscription, IDataConsolidator consolidator) { if (subscription.Type == typeof(Tick)) { var tickType = LeanData.GetCommonTickTypeForCommonDataTypes(consolidator.OutputType, subscription.Symbol.SecurityType); return subscription.TickType == tickType; } return consolidator.InputType.IsAssignableFrom(subscription.Type); } } }