Lean  $LEAN_TAG$
MappingExtensions.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;
20 using System.Collections.Generic;
21 
23 {
24  /// <summary>
25  /// Mapping extensions helper methods
26  /// </summary>
27  public static class MappingExtensions
28  {
29  /// <summary>
30  /// Helper method to resolve the mapping file to use.
31  /// </summary>
32  /// <remarks>This method is aware of the data type being added for <see cref="SecurityType.Base"/>
33  /// to the <see cref="SecurityIdentifier.Symbol"/> value</remarks>
34  /// <param name="mapFileProvider">The map file provider</param>
35  /// <param name="dataConfig">The configuration to fetch the map file for</param>
36  /// <returns>The mapping file to use</returns>
37  public static MapFile ResolveMapFile(this IMapFileProvider mapFileProvider, SubscriptionDataConfig dataConfig)
38  {
39  var resolver = MapFileResolver.Empty;
40  if (dataConfig.TickerShouldBeMapped())
41  {
42  resolver = mapFileProvider.Get(AuxiliaryDataKey.Create(dataConfig.Symbol));
43  }
44  return resolver.ResolveMapFile(dataConfig.Symbol, dataConfig.Type.Name);
45  }
46 
47  /// <summary>
48  /// Helper method to resolve the mapping file to use.
49  /// </summary>
50  /// <remarks>This method is aware of the data type being added for <see cref="SecurityType.Base"/>
51  /// to the <see cref="SecurityIdentifier.Symbol"/> value</remarks>
52  /// <param name="mapFileResolver">The map file resolver</param>
53  /// <param name="symbol">The symbol that we want to map</param>
54  /// <param name="dataType">The string data type name if any</param>
55  /// <returns>The mapping file to use</returns>
56  public static MapFile ResolveMapFile(this MapFileResolver mapFileResolver,
57  Symbol symbol,
58  string dataType = null)
59  {
60  // Load the symbol and date to complete the mapFile checks in one statement
61  var symbolID = symbol.HasUnderlying ? symbol.Underlying.ID.Symbol : symbol.ID.Symbol;
62  if (dataType == null && symbol.SecurityType == SecurityType.Base)
63  {
64  SecurityIdentifier.TryGetCustomDataType(symbol.ID.Symbol, out dataType);
65  }
66  symbolID = symbol.SecurityType == SecurityType.Base && dataType != null ? symbolID.RemoveFromEnd($".{dataType}") : symbolID;
67 
68  MapFile result;
69  if (ReferenceEquals(mapFileResolver, MapFileResolver.Empty))
70  {
71  result = mapFileResolver.ResolveMapFile(symbol.Value, Time.BeginningOfTime);
72  }
73  else
74  {
75  var date = symbol.HasUnderlying ? symbol.Underlying.ID.Date : symbol.ID.Date;
76  result = mapFileResolver.ResolveMapFile(symbolID, date);
77  }
78 
79  return result;
80  }
81 
82  /// <summary>
83  /// Some historical provider supports ancient data. In fact, the ticker could be restructured to new one.
84  /// </summary>
85  /// <param name="mapFileProvider">Provides instances of <see cref="MapFileResolver"/> at run time</param>
86  /// <param name="symbol">Represents a unique security identifier</param>
87  /// <param name="startDateTime">The date since we began our search for the historical name of the symbol.</param>
88  /// <param name="endDateTime">The end date and time of the historical data range.</param>
89  /// <returns>
90  /// An enumerable collection of tuples containing symbol ticker, start date and time, and end date and time
91  /// representing the historical definitions of the symbol within the specified time range.
92  /// </returns>
93  /// <exception cref="ArgumentNullException">Thrown when <paramref name="mapFileProvider"/> is null.</exception>
94  /// <example>
95  /// For instances, get "GOOGL" since 2013 to 2018:
96  /// It returns: { ("GOOG", 2013, 2014), ("GOOGL", 2014, 2018) }
97  /// </example>
98  /// <remarks>
99  /// GOOGLE: IPO: August 19, 2004 Name = GOOG then it was restructured: from "GOOG" to "GOOGL" on April 2, 2014
100  /// </remarks>
101  public static IEnumerable<TickerDateRange> RetrieveSymbolHistoricalDefinitionsInDateRange
102  (this IMapFileProvider mapFileProvider, Symbol symbol, DateTime startDateTime, DateTime endDateTime)
103  {
104  if (mapFileProvider == null)
105  {
106  throw new ArgumentNullException(nameof(mapFileProvider));
107  }
108 
109  var mapFileResolver = mapFileProvider.Get(AuxiliaryDataKey.Create(symbol));
110  var symbolMapFile = mapFileResolver.ResolveMapFile(symbol);
111 
112  if (!symbolMapFile.Any())
113  {
114  yield break;
115  }
116 
117  var newStartDateTime = startDateTime;
118  foreach (var mappedTicker in symbolMapFile.Skip(1)) // Skip: IPO Ticker's DateTime
119  {
120  if (mappedTicker.Date >= newStartDateTime)
121  {
122  // Shifts endDateTime by one day to include all data up to and including the endDateTime.
123  var newEndDateTime = mappedTicker.Date.AddDays(1);
124  if (newEndDateTime > endDateTime)
125  {
126  yield return new(mappedTicker.MappedSymbol, newStartDateTime, endDateTime);
127  // the request EndDateTime was achieved
128  yield break;
129  }
130 
131  yield return new(mappedTicker.MappedSymbol, newStartDateTime, newEndDateTime);
132  // the end of the current request is the start of the next
133  newStartDateTime = newEndDateTime;
134  }
135  }
136  }
137 
138  /// <summary>
139  /// Retrieves all Symbol from map files based on specific Symbol.
140  /// </summary>
141  /// <param name="mapFileProvider">The provider for map files containing ticker data.</param>
142  /// <param name="symbol">The symbol to get <see cref="MapFileResolver"/> and generate new Symbol.</param>
143  /// <returns>An enumerable collection of <see cref="SymbolDateRange"/></returns>
144  /// <exception cref="ArgumentException">Throw if <paramref name="mapFileProvider"/> is null.</exception>
145  public static IEnumerable<SymbolDateRange> RetrieveAllMappedSymbolInDateRange(this IMapFileProvider mapFileProvider, Symbol symbol)
146  {
147  if (mapFileProvider == null || symbol == null)
148  {
149  throw new ArgumentException($"The map file provider and symbol cannot be null. {(mapFileProvider == null ? nameof(mapFileProvider) : nameof(symbol))}");
150  }
151 
152  var mapFileResolver = mapFileProvider.Get(AuxiliaryDataKey.Create(symbol));
153 
154  var tickerUpperCase = symbol.HasUnderlying ? symbol.Underlying.Value.ToUpperInvariant() : symbol.Value.ToUpperInvariant();
155 
156  var isOptionSymbol = symbol.SecurityType == SecurityType.Option;
157  foreach (var mapFile in mapFileResolver)
158  {
159  // Check if 'mapFile' contains the desired ticker symbol.
160  if (!mapFile.Any(mapFileRow => mapFileRow.MappedSymbol == tickerUpperCase))
161  {
162  continue;
163  }
164 
165  foreach (var tickerDateRange in mapFile.GetTickerDateRanges(tickerUpperCase))
166  {
167  var sid = SecurityIdentifier.GenerateEquity(mapFile.FirstDate, mapFile.FirstTicker, symbol?.ID.Market);
168 
169  var newSymbol = new Symbol(sid, tickerUpperCase);
170 
171  if (isOptionSymbol)
172  {
173  newSymbol = Symbol.CreateCanonicalOption(newSymbol);
174  }
175 
176  yield return new(newSymbol, tickerDateRange.StartDate, tickerDateRange.EndDate);
177  }
178  }
179  }
180 
181  /// <summary>
182  /// Retrieves the date ranges associated with a specific ticker symbol from the provided map file.
183  /// </summary>
184  /// <param name="mapFile">The map file containing the data ranges for various ticker.</param>
185  /// <param name="ticker">The ticker for which to retrieve the date ranges.</param>
186  /// <returns>An enumerable collection of tuples representing the start and end dates for each date range associated with the specified ticker symbol.</returns>
187  private static IEnumerable<(DateTime StartDate, DateTime EndDate)> GetTickerDateRanges(this MapFile mapFile, string ticker)
188  {
189  var previousRowDate = mapFile.FirstOrDefault().Date;
190  foreach (var currentRow in mapFile.Skip(1))
191  {
192  if (ticker == currentRow.MappedSymbol)
193  {
194  yield return (previousRowDate, currentRow.Date.AddDays(1));
195  }
196  // MapFile maintains the latest date associated with each ticker name, except first Row
197  previousRowDate = currentRow.Date.AddDays(1);
198  }
199  }
200  }
201 }