/* * 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.Interfaces; using System.Collections.Generic; namespace QuantConnect.Data.Auxiliary { /// /// Mapping extensions helper methods /// public static class MappingExtensions { /// /// Helper method to resolve the mapping file to use. /// /// This method is aware of the data type being added for /// to the value /// The map file provider /// The configuration to fetch the map file for /// The mapping file to use public static MapFile ResolveMapFile(this IMapFileProvider mapFileProvider, SubscriptionDataConfig dataConfig) { var resolver = MapFileResolver.Empty; if (dataConfig.TickerShouldBeMapped()) { resolver = mapFileProvider.Get(AuxiliaryDataKey.Create(dataConfig.Symbol)); } return resolver.ResolveMapFile(dataConfig.Symbol, dataConfig.Type.Name); } /// /// Helper method to resolve the mapping file to use. /// /// This method is aware of the data type being added for /// to the value /// The map file resolver /// The symbol that we want to map /// The string data type name if any /// The mapping file to use public static MapFile ResolveMapFile(this MapFileResolver mapFileResolver, Symbol symbol, string dataType = null) { // Load the symbol and date to complete the mapFile checks in one statement var symbolID = symbol.HasUnderlying ? symbol.Underlying.ID.Symbol : symbol.ID.Symbol; if (dataType == null && symbol.SecurityType == SecurityType.Base) { SecurityIdentifier.TryGetCustomDataType(symbol.ID.Symbol, out dataType); } symbolID = symbol.SecurityType == SecurityType.Base && dataType != null ? symbolID.RemoveFromEnd($".{dataType}") : symbolID; MapFile result; if (ReferenceEquals(mapFileResolver, MapFileResolver.Empty)) { result = mapFileResolver.ResolveMapFile(symbol.Value, Time.BeginningOfTime); } else { var date = symbol.HasUnderlying ? symbol.Underlying.ID.Date : symbol.ID.Date; result = mapFileResolver.ResolveMapFile(symbolID, date); } return result; } /// /// Some historical provider supports ancient data. In fact, the ticker could be restructured to new one. /// /// Provides instances of at run time /// Represents a unique security identifier /// The date since we began our search for the historical name of the symbol. /// The end date and time of the historical data range. /// /// An enumerable collection of tuples containing symbol ticker, start date and time, and end date and time /// representing the historical definitions of the symbol within the specified time range. /// /// Thrown when is null. /// /// For instances, get "GOOGL" since 2013 to 2018: /// It returns: { ("GOOG", 2013, 2014), ("GOOGL", 2014, 2018) } /// /// /// GOOGLE: IPO: August 19, 2004 Name = GOOG then it was restructured: from "GOOG" to "GOOGL" on April 2, 2014 /// public static IEnumerable RetrieveSymbolHistoricalDefinitionsInDateRange (this IMapFileProvider mapFileProvider, Symbol symbol, DateTime startDateTime, DateTime endDateTime) { if (mapFileProvider == null) { throw new ArgumentNullException(nameof(mapFileProvider)); } var mapFileResolver = mapFileProvider.Get(AuxiliaryDataKey.Create(symbol)); var symbolMapFile = mapFileResolver.ResolveMapFile(symbol); if (!symbolMapFile.Any()) { yield break; } var newStartDateTime = startDateTime; foreach (var mappedTicker in symbolMapFile.Skip(1)) // Skip: IPO Ticker's DateTime { if (mappedTicker.Date >= newStartDateTime) { // Shifts endDateTime by one day to include all data up to and including the endDateTime. var newEndDateTime = mappedTicker.Date.AddDays(1); if (newEndDateTime > endDateTime) { yield return new(mappedTicker.MappedSymbol, newStartDateTime, endDateTime); // the request EndDateTime was achieved yield break; } yield return new(mappedTicker.MappedSymbol, newStartDateTime, newEndDateTime); // the end of the current request is the start of the next newStartDateTime = newEndDateTime; } } } /// /// Retrieves all Symbol from map files based on specific Symbol. /// /// The provider for map files containing ticker data. /// The symbol to get and generate new Symbol. /// An enumerable collection of /// Throw if is null. public static IEnumerable RetrieveAllMappedSymbolInDateRange(this IMapFileProvider mapFileProvider, Symbol symbol) { if (mapFileProvider == null || symbol == null) { throw new ArgumentException($"The map file provider and symbol cannot be null. {(mapFileProvider == null ? nameof(mapFileProvider) : nameof(symbol))}"); } var mapFileResolver = mapFileProvider.Get(AuxiliaryDataKey.Create(symbol)); var tickerUpperCase = symbol.HasUnderlying ? symbol.Underlying.Value.ToUpperInvariant() : symbol.Value.ToUpperInvariant(); var isOptionSymbol = symbol.SecurityType == SecurityType.Option; foreach (var mapFile in mapFileResolver) { // Check if 'mapFile' contains the desired ticker symbol. if (!mapFile.Any(mapFileRow => mapFileRow.MappedSymbol == tickerUpperCase)) { continue; } foreach (var tickerDateRange in mapFile.GetTickerDateRanges(tickerUpperCase)) { var sid = SecurityIdentifier.GenerateEquity(mapFile.FirstDate, mapFile.FirstTicker, symbol?.ID.Market); var newSymbol = new Symbol(sid, tickerUpperCase); if (isOptionSymbol) { newSymbol = Symbol.CreateCanonicalOption(newSymbol); } yield return new(newSymbol, tickerDateRange.StartDate, tickerDateRange.EndDate); } } } /// /// Retrieves the date ranges associated with a specific ticker symbol from the provided map file. /// /// The map file containing the data ranges for various ticker. /// The ticker for which to retrieve the date ranges. /// An enumerable collection of tuples representing the start and end dates for each date range associated with the specified ticker symbol. private static IEnumerable<(DateTime StartDate, DateTime EndDate)> GetTickerDateRanges(this MapFile mapFile, string ticker) { var previousRowDate = mapFile.FirstOrDefault().Date; foreach (var currentRow in mapFile.Skip(1)) { if (ticker == currentRow.MappedSymbol) { yield return (previousRowDate, currentRow.Date.AddDays(1)); } // MapFile maintains the latest date associated with each ticker name, except first Row previousRowDate = currentRow.Date.AddDays(1); } } } }