/* * 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.Linq; using QuantConnect.Data; using QuantConnect.Util; using QuantConnect.Interfaces; using System.Collections.Generic; using System.Collections.Concurrent; using QuantConnect.Lean.Engine.Results; using QuantConnect.Data.UniverseSelection; namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators.Factories { /// /// Provides an implementation of that used the /// /// Only used on backtesting by the public class SubscriptionDataReaderSubscriptionEnumeratorFactory : ISubscriptionEnumeratorFactory, IDisposable { private readonly IResultHandler _resultHandler; private readonly IFactorFileProvider _factorFileProvider; private readonly IDataCacheProvider _dataCacheProvider; private readonly ConcurrentDictionary _numericalPrecisionLimitedWarnings; private readonly int _numericalPrecisionLimitedWarningsMaxCount = 10; private readonly ConcurrentDictionary _startDateLimitedWarnings; private readonly int _startDateLimitedWarningsMaxCount = 10; private readonly IMapFileProvider _mapFileProvider; private readonly bool _enablePriceScaling; private readonly IAlgorithm _algorithm; /// /// Initializes a new instance of the class /// /// The result handler for the algorithm /// The map file provider /// The factor file provider /// Provider used to get data when it is not present on disk /// The algorithm instance to use /// Applies price factor public SubscriptionDataReaderSubscriptionEnumeratorFactory(IResultHandler resultHandler, IMapFileProvider mapFileProvider, IFactorFileProvider factorFileProvider, IDataCacheProvider cacheProvider, IAlgorithm algorithm, bool enablePriceScaling = true ) { _algorithm = algorithm; _resultHandler = resultHandler; _mapFileProvider = mapFileProvider; _factorFileProvider = factorFileProvider; _dataCacheProvider = cacheProvider; _numericalPrecisionLimitedWarnings = new ConcurrentDictionary(); _startDateLimitedWarnings = new ConcurrentDictionary(); _enablePriceScaling = enablePriceScaling; } /// /// Creates a to read the specified request /// /// The subscription request to be read /// Provider used to get data when it is not present on disk /// An enumerator reading the subscription request public IEnumerator CreateEnumerator(SubscriptionRequest request, IDataProvider dataProvider) { var dataReader = new SubscriptionDataReader(request.Configuration, request, _mapFileProvider, _factorFileProvider, _dataCacheProvider, dataProvider, _algorithm.ObjectStore); dataReader.InvalidConfigurationDetected += (sender, args) => { _resultHandler.ErrorMessage(args.Message); }; dataReader.StartDateLimited += (sender, args) => { // Queue this warning into our dictionary to report on dispose if (_startDateLimitedWarnings.Count <= _startDateLimitedWarningsMaxCount) { _startDateLimitedWarnings.TryAdd(args.Symbol, args.Message); } }; dataReader.DownloadFailed += (sender, args) => { _resultHandler.ErrorMessage(args.Message, args.StackTrace); }; dataReader.ReaderErrorDetected += (sender, args) => { _resultHandler.RuntimeError(args.Message, args.StackTrace); }; dataReader.NumericalPrecisionLimited += (sender, args) => { // Set a hard limit to keep this warning list from getting unnecessarily large if (_numericalPrecisionLimitedWarnings.Count <= _numericalPrecisionLimitedWarningsMaxCount) { _numericalPrecisionLimitedWarnings.TryAdd(args.Symbol, args.Message); } }; IEnumerator enumerator = dataReader; if (LeanData.UseDailyStrictEndTimes(_algorithm.Settings, request, request.Configuration.Symbol, request.Configuration.Increment)) { // before corporate events which might yield data and we synchronize both feeds enumerator = new StrictDailyEndTimesEnumerator(enumerator, request.ExchangeHours, request.StartTimeLocal); } enumerator = CorporateEventEnumeratorFactory.CreateEnumerators( enumerator, request.Configuration, _factorFileProvider, dataReader, _mapFileProvider, request.StartTimeLocal, request.EndTimeLocal, _enablePriceScaling); return enumerator; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// 2 public void Dispose() { // Log our numerical precision limited warnings if any if (!_numericalPrecisionLimitedWarnings.IsNullOrEmpty()) { var message = "Due to numerical precision issues in the factor file, data for the following" + $" symbols was adjust to a later starting date: {string.Join(", ", _numericalPrecisionLimitedWarnings.Values.Take(_numericalPrecisionLimitedWarningsMaxCount))}"; // If we reached our max warnings count suggest that more may have been left out if (_numericalPrecisionLimitedWarnings.Count >= _numericalPrecisionLimitedWarningsMaxCount) { message += "..."; } _resultHandler.DebugMessage(message); } // Log our start date adjustments because of map files if (!_startDateLimitedWarnings.IsNullOrEmpty()) { var message = "The starting dates for the following symbols have been adjusted to match their" + $" map files first date: {string.Join(", ", _startDateLimitedWarnings.Values.Take(_startDateLimitedWarningsMaxCount))}"; // If we reached our max warnings count suggest that more may have been left out if (_startDateLimitedWarnings.Count >= _startDateLimitedWarningsMaxCount) { message += "..."; } _resultHandler.DebugMessage(message); } } } }