Lean  $LEAN_TAG$
MapFileResolver.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.Collections;
19 using System.Collections.Generic;
20 using System.Data;
21 using System.IO;
22 using System.Linq;
24 using QuantConnect.Util;
25 
27 {
28  /// <summary>
29  /// Provides a means of mapping a symbol at a point in time to the map file
30  /// containing that share class's mapping information
31  /// </summary>
32  public class MapFileResolver : IEnumerable<MapFile>
33  {
34  private readonly Dictionary<string, MapFile> _mapFilesByPermtick;
35  private readonly Dictionary<string, SortedList<DateTime, MapFileRowEntry>> _bySymbol;
36 
37  /// <summary>
38  /// Gets an empty <see cref="MapFileResolver"/>, that is an instance that contains
39  /// zero mappings
40  /// </summary>
41  public static readonly MapFileResolver Empty = new MapFileResolver(Enumerable.Empty<MapFile>());
42 
43  /// <summary>
44  /// Initializes a new instance of the <see cref="MapFileResolver"/> by reading
45  /// in all files in the specified directory.
46  /// </summary>
47  /// <param name="mapFiles">The data used to initialize this resolver.</param>
48  public MapFileResolver(IEnumerable<MapFile> mapFiles)
49  {
50  _mapFilesByPermtick = new Dictionary<string, MapFile>(StringComparer.InvariantCultureIgnoreCase);
51  _bySymbol = new Dictionary<string, SortedList<DateTime, MapFileRowEntry>>(StringComparer.InvariantCultureIgnoreCase);
52 
53  foreach (var mapFile in mapFiles)
54  {
55  // add to our by path map
56  _mapFilesByPermtick.Add(mapFile.Permtick, mapFile);
57 
58  foreach (var row in mapFile)
59  {
60  SortedList<DateTime, MapFileRowEntry> entries;
61  var mapFileRowEntry = new MapFileRowEntry(mapFile.Permtick, row);
62 
63  if (!_bySymbol.TryGetValue(row.MappedSymbol, out entries))
64  {
65  entries = new SortedList<DateTime, MapFileRowEntry>();
66  _bySymbol[row.MappedSymbol] = entries;
67  }
68 
69  if (entries.ContainsKey(mapFileRowEntry.MapFileRow.Date))
70  {
71  // check to verify it' the same data
72  if (!entries[mapFileRowEntry.MapFileRow.Date].Equals(mapFileRowEntry))
73  {
74  throw new DuplicateNameException("Attempted to assign different history for symbol.");
75  }
76  }
77  else
78  {
79  entries.Add(mapFileRowEntry.MapFileRow.Date, mapFileRowEntry);
80  }
81  }
82  }
83  }
84 
85  /// <summary>
86  /// Gets the map file matching the specified permtick
87  /// </summary>
88  /// <param name="permtick">The permtick to match on</param>
89  /// <returns>The map file matching the permtick, or null if not found</returns>
90  public MapFile GetByPermtick(string permtick)
91  {
92  MapFile mapFile;
93  _mapFilesByPermtick.TryGetValue(permtick.LazyToUpper(), out mapFile);
94  return mapFile;
95  }
96 
97  /// <summary>
98  /// Resolves the map file path containing the mapping information for the symbol defined at <paramref name="date"/>
99  /// </summary>
100  /// <param name="symbol">The symbol as of <paramref name="date"/> to be mapped</param>
101  /// <param name="date">The date associated with the <paramref name="symbol"/></param>
102  /// <returns>The map file responsible for mapping the symbol, if no map file is found, null is returned</returns>
103  public MapFile ResolveMapFile(string symbol, DateTime date)
104  {
105  // lookup the symbol's history
106  SortedList<DateTime, MapFileRowEntry> entries;
107  if (_bySymbol.TryGetValue(symbol, out entries))
108  {
109  if (entries.Count == 0)
110  {
111  return new MapFile(symbol, Enumerable.Empty<MapFileRow>());
112  }
113 
114  // Return value of BinarySearch (from MSDN):
115  // The zero-based index of item in the sorted List<T>, if item is found;
116  // otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item
117  // or, if there is no larger element, the bitwise complement of Count.
118  var indexOf = entries.Keys.BinarySearch(date);
119  if (indexOf >= 0)
120  {
121  symbol = entries.Values[indexOf].EntitySymbol;
122  }
123  else
124  {
125  if (indexOf == ~entries.Keys.Count)
126  {
127  // the searched date is greater than the last date in the list, return the last entry
128  indexOf = entries.Keys.Count - 1;
129  }
130  else
131  {
132  // if negative, it's the bitwise complement of where it should be
133  indexOf = ~indexOf;
134  }
135 
136  symbol = entries.Values[indexOf].EntitySymbol;
137  }
138  }
139  // secondary search for exact mapping, find path than ends with symbol.csv
140  MapFile mapFile;
141  if (!_mapFilesByPermtick.TryGetValue(symbol, out mapFile)
142  || mapFile.FirstDate > date && date != SecurityIdentifier.DefaultDate)
143  {
144  return new MapFile(symbol, Enumerable.Empty<MapFileRow>());
145  }
146  return mapFile;
147  }
148 
149  /// <summary>
150  /// Combines the map file row with the map file path that produced the row
151  /// </summary>
152  class MapFileRowEntry : IEquatable<MapFileRowEntry>
153  {
154  /// <summary>
155  /// Gets the map file row
156  /// </summary>
157  public MapFileRow MapFileRow { get; private set; }
158 
159  /// <summary>
160  /// Gets the full path to the map file that produced this row
161  /// </summary>
162  public string EntitySymbol { get; private set; }
163 
164  /// <summary>
165  /// Initializes a new instance of the <see cref="MapFileRowEntry"/> class
166  /// </summary>
167  /// <param name="entitySymbol">The map file that produced this row</param>
168  /// <param name="mapFileRow">The map file row data</param>
169  public MapFileRowEntry(string entitySymbol, MapFileRow mapFileRow)
170  {
171  MapFileRow = mapFileRow;
172  EntitySymbol = entitySymbol;
173  }
174 
175  /// <summary>
176  /// Indicates whether the current object is equal to another object of the same type.
177  /// </summary>
178  /// <returns>
179  /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
180  /// </returns>
181  /// <param name="other">An object to compare with this object.</param>
182  public bool Equals(MapFileRowEntry other)
183  {
184  if (other == null) return false;
185  return other.MapFileRow.Date == MapFileRow.Date
186  && other.MapFileRow.MappedSymbol == MapFileRow.MappedSymbol;
187  }
188 
189  /// <summary>
190  /// Returns a string that represents the current object.
191  /// </summary>
192  /// <returns>
193  /// A string that represents the current object.
194  /// </returns>
195  /// <filterpriority>2</filterpriority>
196  public override string ToString()
197  {
198  return MapFileRow.Date + ": " + MapFileRow.MappedSymbol + ": " + EntitySymbol;
199  }
200  }
201 
202  #region Implementation of IEnumerable
203 
204  /// <summary>
205  /// Returns an enumerator that iterates through the collection.
206  /// </summary>
207  /// <returns>
208  /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
209  /// </returns>
210  /// <filterpriority>1</filterpriority>
211  public IEnumerator<MapFile> GetEnumerator()
212  {
213  return _mapFilesByPermtick.Values.GetEnumerator();
214  }
215 
216  /// <summary>
217  /// Returns an enumerator that iterates through a collection.
218  /// </summary>
219  /// <returns>
220  /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
221  /// </returns>
222  /// <filterpriority>2</filterpriority>
223  IEnumerator IEnumerable.GetEnumerator()
224  {
225  return GetEnumerator();
226  }
227 
228  #endregion
229  }
230 }