Lean  $LEAN_TAG$
LocalZipMapFileProvider.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 using System;
17 using QuantConnect.Util;
18 using QuantConnect.Logging;
19 using System.Threading.Tasks;
21 using System.Collections.Generic;
22 
24 {
25  /// <summary>
26  /// Provides an implementation of <see cref="IMapFileProvider"/> that reads from a local zip file
27  /// </summary>
29  {
30  private Dictionary<AuxiliaryDataKey, MapFileResolver> _cache;
31  private IDataProvider _dataProvider;
32  private object _lock;
33 
34  /// <summary>
35  /// The cached refresh period for the map files
36  /// </summary>
37  /// <remarks>Exposed for testing</remarks>
38  protected virtual TimeSpan CacheRefreshPeriod
39  {
40  get
41  {
42  var dueTime = Time.GetNextLiveAuxiliaryDataDueTime();
43  if (dueTime > TimeSpan.FromMinutes(10))
44  {
45  // Clear the cache before the auxiliary due time to avoid race conditions with consumers
46  return dueTime - TimeSpan.FromMinutes(10);
47  }
48  return dueTime;
49  }
50  }
51 
52  /// <summary>
53  /// Creates a new instance of the <see cref="LocalDiskFactorFileProvider"/>
54  /// </summary>
56  {
57  _lock = new object();
58  _cache = new Dictionary<AuxiliaryDataKey, MapFileResolver>();
59  }
60 
61  /// <summary>
62  /// Initializes our MapFileProvider by supplying our dataProvider
63  /// </summary>
64  /// <param name="dataProvider">DataProvider to use</param>
65  public void Initialize(IDataProvider dataProvider)
66  {
67  if (_dataProvider != null)
68  {
69  return;
70  }
71 
72  _dataProvider = dataProvider;
74  }
75 
76  /// <summary>
77  /// Gets a <see cref="MapFileResolver"/> representing all the map files for the specified market
78  /// </summary>
79  /// <param name="auxiliaryDataKey">Key used to fetch a map file resolver. Specifying market and security type</param>
80  /// <returns>A <see cref="MapFileResolver"/> containing all map files for the specified market</returns>
81  public MapFileResolver Get(AuxiliaryDataKey auxiliaryDataKey)
82  {
83  MapFileResolver result;
84  // we use a lock so that only 1 thread loads the map file resolver while the rest wait
85  // else we could have multiple threads loading the map file resolver at the same time!
86  lock (_lock)
87  {
88  if (!_cache.TryGetValue(auxiliaryDataKey, out result))
89  {
90  _cache[auxiliaryDataKey] = result = GetMapFileResolver(auxiliaryDataKey);
91  }
92  }
93  return result;
94  }
95 
96  /// <summary>
97  /// Helper method that will clear any cached factor files in a daily basis, this is useful for live trading
98  /// </summary>
99  protected virtual void StartExpirationTask()
100  {
101  lock (_lock)
102  {
103  // we clear the seeded markets so they are reloaded
104  _cache = new Dictionary<AuxiliaryDataKey, MapFileResolver>();
105  }
106  _ = Task.Delay(CacheRefreshPeriod).ContinueWith(_ => StartExpirationTask());
107  }
108 
109  private MapFileResolver GetMapFileResolver(AuxiliaryDataKey auxiliaryDataKey)
110  {
111  var market = auxiliaryDataKey.Market;
112  var timestamp = DateTime.UtcNow.ConvertFromUtc(TimeZones.NewYork);
113  var todayNewYork = timestamp.Date;
114  var yesterdayNewYork = todayNewYork.AddDays(-1);
115 
116  // start the search with yesterday, today's file will be available tomorrow
117  var count = 0;
118  var date = yesterdayNewYork;
119  do
120  {
121  var zipFileName = MapFileZipHelper.GetMapFileZipFileName(market, date, auxiliaryDataKey.SecurityType);
122 
123  // Fetch a stream for our zip from our data provider
124  var stream = _dataProvider.Fetch(zipFileName);
125 
126  // If we found a file we can read it
127  if (stream != null)
128  {
129  Log.Trace("LocalZipMapFileProvider.Get({0}): Fetched map files for: {1} NY", market, date.ToShortDateString());
130  var result = new MapFileResolver(MapFileZipHelper.ReadMapFileZip(stream, market, auxiliaryDataKey.SecurityType));
131  stream.DisposeSafely();
132  return result;
133  }
134 
135  // prevent infinite recursion if something is wrong
136  if (count++ > 30)
137  {
138  throw new InvalidOperationException($"LocalZipMapFileProvider couldn't find any map files going all the way back to {date} for {market}");
139  }
140 
141  date = date.AddDays(-1);
142  }
143  while (true);
144  }
145  }
146 }