/*
* 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);
}
}
}
}