Lean  $LEAN_TAG$
SubscriptionDataReaderSubscriptionEnumeratorFactory.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15 */
16 
17 using System;
18 using System.Linq;
19 using QuantConnect.Data;
20 using QuantConnect.Util;
22 using System.Collections.Generic;
23 using System.Collections.Concurrent;
26 
28 {
29  /// <summary>
30  /// Provides an implementation of <see cref="ISubscriptionEnumeratorFactory"/> that used the <see cref="SubscriptionDataReader"/>
31  /// </summary>
32  /// <remarks>Only used on backtesting by the <see cref="FileSystemDataFeed"/></remarks>
34  {
35  private readonly IResultHandler _resultHandler;
36  private readonly IFactorFileProvider _factorFileProvider;
37  private readonly IDataCacheProvider _dataCacheProvider;
38  private readonly ConcurrentDictionary<Symbol, string> _numericalPrecisionLimitedWarnings;
39  private readonly int _numericalPrecisionLimitedWarningsMaxCount = 10;
40  private readonly ConcurrentDictionary<Symbol, string> _startDateLimitedWarnings;
41  private readonly int _startDateLimitedWarningsMaxCount = 10;
42  private readonly IMapFileProvider _mapFileProvider;
43  private readonly bool _enablePriceScaling;
44  private readonly IAlgorithm _algorithm;
45 
46  /// <summary>
47  /// Initializes a new instance of the <see cref="SubscriptionDataReaderSubscriptionEnumeratorFactory"/> class
48  /// </summary>
49  /// <param name="resultHandler">The result handler for the algorithm</param>
50  /// <param name="mapFileProvider">The map file provider</param>
51  /// <param name="factorFileProvider">The factor file provider</param>
52  /// <param name="cacheProvider">Provider used to get data when it is not present on disk</param>
53  /// <param name="algorithm">The algorithm instance to use</param>
54  /// <param name="enablePriceScaling">Applies price factor</param>
56  IMapFileProvider mapFileProvider,
57  IFactorFileProvider factorFileProvider,
58  IDataCacheProvider cacheProvider,
59  IAlgorithm algorithm,
60  bool enablePriceScaling = true
61  )
62  {
63  _algorithm = algorithm;
64  _resultHandler = resultHandler;
65  _mapFileProvider = mapFileProvider;
66  _factorFileProvider = factorFileProvider;
67  _dataCacheProvider = cacheProvider;
68  _numericalPrecisionLimitedWarnings = new ConcurrentDictionary<Symbol, string>();
69  _startDateLimitedWarnings = new ConcurrentDictionary<Symbol, string>();
70  _enablePriceScaling = enablePriceScaling;
71  }
72 
73  /// <summary>
74  /// Creates a <see cref="SubscriptionDataReader"/> to read the specified request
75  /// </summary>
76  /// <param name="request">The subscription request to be read</param>
77  /// <param name="dataProvider">Provider used to get data when it is not present on disk</param>
78  /// <returns>An enumerator reading the subscription request</returns>
79  public IEnumerator<BaseData> CreateEnumerator(SubscriptionRequest request, IDataProvider dataProvider)
80  {
81  var dataReader = new SubscriptionDataReader(request.Configuration,
82  request,
83  _mapFileProvider,
84  _factorFileProvider,
85  _dataCacheProvider,
86  dataProvider,
87  _algorithm.ObjectStore);
88 
89  dataReader.InvalidConfigurationDetected += (sender, args) => { _resultHandler.ErrorMessage(args.Message); };
90  dataReader.StartDateLimited += (sender, args) =>
91  {
92  // Queue this warning into our dictionary to report on dispose
93  if (_startDateLimitedWarnings.Count <= _startDateLimitedWarningsMaxCount)
94  {
95  _startDateLimitedWarnings.TryAdd(args.Symbol, args.Message);
96  }
97  };
98  dataReader.DownloadFailed += (sender, args) => { _resultHandler.ErrorMessage(args.Message, args.StackTrace); };
99  dataReader.ReaderErrorDetected += (sender, args) => { _resultHandler.RuntimeError(args.Message, args.StackTrace); };
100  dataReader.NumericalPrecisionLimited += (sender, args) =>
101  {
102  // Set a hard limit to keep this warning list from getting unnecessarily large
103  if (_numericalPrecisionLimitedWarnings.Count <= _numericalPrecisionLimitedWarningsMaxCount)
104  {
105  _numericalPrecisionLimitedWarnings.TryAdd(args.Symbol, args.Message);
106  }
107  };
108 
109  IEnumerator<BaseData> enumerator = dataReader;
110  if (LeanData.UseDailyStrictEndTimes(_algorithm.Settings, request, request.Configuration.Symbol, request.Configuration.Increment))
111  {
112  // before corporate events which might yield data and we synchronize both feeds
113  enumerator = new StrictDailyEndTimesEnumerator(enumerator, request.ExchangeHours, request.StartTimeLocal);
114  }
115 
117  enumerator,
118  request.Configuration,
119  _factorFileProvider,
120  dataReader,
121  _mapFileProvider,
122  request.StartTimeLocal,
123  request.EndTimeLocal,
124  _enablePriceScaling);
125 
126  return enumerator;
127  }
128 
129  /// <summary>
130  /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
131  /// </summary>
132  /// <filterpriority>2</filterpriority>
133  public void Dispose()
134  {
135  // Log our numerical precision limited warnings if any
136  if (!_numericalPrecisionLimitedWarnings.IsNullOrEmpty())
137  {
138  var message = "Due to numerical precision issues in the factor file, data for the following" +
139  $" symbols was adjust to a later starting date: {string.Join(", ", _numericalPrecisionLimitedWarnings.Values.Take(_numericalPrecisionLimitedWarningsMaxCount))}";
140 
141  // If we reached our max warnings count suggest that more may have been left out
142  if (_numericalPrecisionLimitedWarnings.Count >= _numericalPrecisionLimitedWarningsMaxCount)
143  {
144  message += "...";
145  }
146 
147  _resultHandler.DebugMessage(message);
148  }
149 
150  // Log our start date adjustments because of map files
151  if (!_startDateLimitedWarnings.IsNullOrEmpty())
152  {
153  var message = "The starting dates for the following symbols have been adjusted to match their" +
154  $" map files first date: {string.Join(", ", _startDateLimitedWarnings.Values.Take(_startDateLimitedWarningsMaxCount))}";
155 
156  // If we reached our max warnings count suggest that more may have been left out
157  if (_startDateLimitedWarnings.Count >= _startDateLimitedWarningsMaxCount)
158  {
159  message += "...";
160  }
161 
162  _resultHandler.DebugMessage(message);
163  }
164  }
165  }
166 }