/* * 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 QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Interfaces; namespace QuantConnect.Lean.Engine.DataFeeds { /// /// Class in charge of handling Leans internal subscriptions /// public class InternalSubscriptionManager { private readonly Dictionary> _subscriptionRequests; private readonly Resolution _resolution; private readonly IAlgorithm _algorithm; /// /// Event fired when a new internal subscription request is to be added /// public EventHandler Added { get; set; } /// /// Event fired when an existing internal subscription should be removed /// public EventHandler Removed { get; set; } /// /// Creates a new instances /// /// The associated algorithm /// The resolution to use for the internal subscriptions public InternalSubscriptionManager(IAlgorithm algorithm, Resolution resolution) { _algorithm = algorithm; _resolution = resolution; _subscriptionRequests = new Dictionary>(); } /// /// Notifies about a removed subscription request /// /// The removed subscription request public void AddedSubscriptionRequest(SubscriptionRequest request) { if (PreFilter(request)) { var lowResolution = request.Configuration.Resolution > Resolution.Minute; List internalRequests; var existing = _subscriptionRequests.TryGetValue(request.Configuration.Symbol, out internalRequests); var alreadyInternal = existing && internalRequests.Any(internalRequest => internalRequest.Configuration.Type == request.Configuration.Type && request.Configuration.TickType == internalRequest.Configuration.TickType); if (lowResolution && !alreadyInternal) { // low resolution subscriptions we will add internal Resolution.Minute subscriptions // if we don't already have this symbol added var config = new SubscriptionDataConfig(request.Configuration, resolution: _resolution, isInternalFeed: true, extendedHours: true, isFilteredSubscription: false); var startTimeUtc = request.StartTimeUtc; if (_algorithm.IsWarmingUp) { // during warmup in live trading do not add these internal subscription until the algorithm starts // these subscription are only added for realtime price in low resolution subscriptions which isn't required for warmup startTimeUtc = DateTime.UtcNow; } var internalRequest = new SubscriptionRequest(false, null, request.Security, config, startTimeUtc, request.EndTimeUtc); if (existing) { _subscriptionRequests[request.Configuration.Symbol].Add(internalRequest); } else { _subscriptionRequests[request.Configuration.Symbol] = new List{ internalRequest }; } Added?.Invoke(this, internalRequest); } else if (!lowResolution && alreadyInternal) { _subscriptionRequests.Remove(request.Configuration.Symbol); // the user added a higher resolution configuration, we can remove the internal we added foreach (var subscriptionRequest in internalRequests) { Removed?.Invoke(this, subscriptionRequest); } } } } /// /// Notifies about an added subscription request /// /// The added subscription request public void RemovedSubscriptionRequest(SubscriptionRequest request) { if (PreFilter(request) && _subscriptionRequests.ContainsKey(request.Configuration.Symbol)) { var userConfigs = _algorithm.SubscriptionManager.SubscriptionDataConfigService .GetSubscriptionDataConfigs(request.Configuration.Symbol).ToList(); if (userConfigs.Count == 0 || userConfigs.Any(config => config.Resolution <= Resolution.Minute)) { var requests = _subscriptionRequests[request.Configuration.Symbol]; _subscriptionRequests.Remove(request.Configuration.Symbol); // if we had a config and the user no longer has a config for this symbol we remove the internal subscription foreach (var subscriptionRequest in requests) { Removed?.Invoke(this, subscriptionRequest); } } } } /// /// True for for live trading, non internal, non universe subscriptions, non custom data subscriptions /// private bool PreFilter(SubscriptionRequest request) { return _algorithm.LiveMode && !request.Configuration.IsInternalFeed && !request.IsUniverseSubscription && !request.Configuration.IsCustomData; } } }