17 using System.Collections.Generic;
18 using System.Globalization;
21 using System.Runtime.CompilerServices;
42 private static readonly HashSet<Type> _strictDailyEndTimesDataTypes =
new()
53 .Select(x => x.ToLowerInvariant()).Union(
new[] {
"alternative" }).ToHashSet();
63 var clone = data.
Clone();
64 clone.
Time = data.
Time.ConvertTo(exchangeTimeZone, dataTimeZone);
65 return GenerateLine(clone, clone.Symbol.ID.SecurityType, resolution);
78 var index = line.IndexOf(
',', StringComparison.InvariantCulture);
79 return date.AddTicks(Convert.ToInt64(10000 * decimal.Parse(line.AsSpan(0, index))));
84 throw new ArgumentOutOfRangeException(nameof(resolution), resolution,
null);
93 var milliseconds = data.
Time.TimeOfDay.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
102 var tick = (
Tick)data;
103 if (tick.TickType ==
TickType.Trade)
105 return ToCsv(milliseconds, Scale(tick.LastPrice), tick.Quantity, tick.ExchangeCode, tick.SaleCondition, tick.Suspicious ?
"1" :
"0");
107 if (tick.TickType ==
TickType.Quote)
109 return ToCsv(milliseconds, Scale(tick.BidPrice), tick.BidSize, Scale(tick.AskPrice), tick.AskSize, tick.ExchangeCode, tick.SaleCondition, tick.Suspicious ?
"1" :
"0");
115 if (tradeBar !=
null)
117 return ToCsv(milliseconds, Scale(tradeBar.Open), Scale(tradeBar.High), Scale(tradeBar.Low), Scale(tradeBar.Close), tradeBar.Volume);
120 if (quoteBar !=
null)
122 return ToCsv(milliseconds,
123 ToScaledCsv(quoteBar.Bid), quoteBar.LastBidSize,
124 ToScaledCsv(quoteBar.Ask), quoteBar.LastAskSize);
131 if (bigTradeBar !=
null)
133 return ToCsv(longTime, Scale(bigTradeBar.Open), Scale(bigTradeBar.High), Scale(bigTradeBar.Low), Scale(bigTradeBar.Close), bigTradeBar.Volume);
136 if (bigQuoteBar !=
null)
138 return ToCsv(longTime,
139 ToScaledCsv(bigQuoteBar.Bid), bigQuoteBar.LastBidSize,
140 ToScaledCsv(bigQuoteBar.Ask), bigQuoteBar.LastAskSize);
151 var tick = data as
Tick;
154 throw new ArgumentException($
"{securityType} tick could not be created", nameof(data));
156 if (tick.TickType ==
TickType.Trade)
158 return ToCsv(milliseconds, tick.LastPrice, tick.Quantity, tick.Suspicious ?
"1" :
"0");
160 if (tick.TickType ==
TickType.Quote)
162 return ToCsv(milliseconds, tick.BidPrice, tick.BidSize, tick.AskPrice, tick.AskSize, tick.Suspicious ?
"1" :
"0");
164 throw new ArgumentException($
"{securityType} tick could not be created");
168 if (quoteBar !=
null)
170 return ToCsv(milliseconds,
171 ToNonScaledCsv(quoteBar.Bid), quoteBar.LastBidSize,
172 ToNonScaledCsv(quoteBar.Ask), quoteBar.LastAskSize);
175 if (tradeBar !=
null)
177 return ToCsv(milliseconds, tradeBar.Open, tradeBar.High, tradeBar.Low, tradeBar.Close, tradeBar.Volume);
179 throw new ArgumentException($
"{securityType} minute/second bar could not be created", nameof(data));
184 if (bigQuoteBar !=
null)
186 return ToCsv(longTime,
187 ToNonScaledCsv(bigQuoteBar.Bid), bigQuoteBar.LastBidSize,
188 ToNonScaledCsv(bigQuoteBar.Ask), bigQuoteBar.LastAskSize);
191 if (bigTradeBar !=
null)
193 return ToCsv(longTime,
200 throw new ArgumentException($
"{securityType} hour/daily bar could not be created", nameof(data));
208 var tick = data as
Tick;
211 throw new ArgumentException(
"Expected data of type 'Tick'", nameof(data));
213 return ToCsv(milliseconds, tick.BidPrice, tick.AskPrice);
220 throw new ArgumentException(
"Expected data of type 'QuoteBar'", nameof(data));
222 return ToCsv(milliseconds,
223 ToNonScaledCsv(bar.Bid), bar.LastBidSize,
224 ToNonScaledCsv(bar.Ask), bar.LastAskSize);
231 throw new ArgumentException(
"Expected data of type 'QuoteBar'", nameof(data));
233 return ToCsv(longTime,
234 ToNonScaledCsv(bigBar.Bid), bigBar.LastBidSize,
235 ToNonScaledCsv(bigBar.Ask), bigBar.LastAskSize);
243 var tick = (
Tick)data;
244 return ToCsv(milliseconds, tick.LastPrice, tick.Quantity,
string.Empty,
string.Empty,
"0");
250 throw new ArgumentException(
"Expected data of type 'TradeBar'", nameof(data));
252 return ToCsv(milliseconds, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume);
256 return ToCsv(longTime, bigTradeBar.Open, bigTradeBar.High, bigTradeBar.Low, bigTradeBar.Close, bigTradeBar.Volume);
265 var tick = (
Tick)data;
266 if (tick.TickType ==
TickType.Trade)
268 return ToCsv(milliseconds,
269 Scale(tick.LastPrice), tick.Quantity, tick.ExchangeCode, tick.SaleCondition, tick.Suspicious ?
"1" :
"0");
271 if (tick.TickType ==
TickType.Quote)
273 return ToCsv(milliseconds,
274 Scale(tick.BidPrice), tick.BidSize, Scale(tick.AskPrice), tick.AskSize, tick.ExchangeCode, tick.Suspicious ?
"1" :
"0");
276 if (tick.TickType ==
TickType.OpenInterest)
278 return ToCsv(milliseconds, tick.Value);
286 if (quoteBar !=
null)
288 return ToCsv(milliseconds,
289 ToScaledCsv(quoteBar.Bid), quoteBar.LastBidSize,
290 ToScaledCsv(quoteBar.Ask), quoteBar.LastAskSize);
293 if (tradeBar !=
null)
295 return ToCsv(milliseconds,
296 Scale(tradeBar.Open), Scale(tradeBar.High), Scale(tradeBar.Low), Scale(tradeBar.Close), tradeBar.Volume);
299 if (openInterest !=
null)
301 return ToCsv(milliseconds, openInterest.Value);
309 if (bigQuoteBar !=
null)
311 return ToCsv(longTime,
312 ToScaledCsv(bigQuoteBar.Bid), bigQuoteBar.LastBidSize,
313 ToScaledCsv(bigQuoteBar.Ask), bigQuoteBar.LastAskSize);
316 if (bigTradeBar !=
null)
318 return ToCsv(longTime, ToScaledCsv(bigTradeBar), bigTradeBar.Volume);
321 if (bigOpenInterest !=
null)
323 return ToCsv(longTime, bigOpenInterest.Value);
328 throw new ArgumentOutOfRangeException(nameof(resolution), resolution,
null);
336 var tick = (
Tick)data;
337 if (tick.TickType ==
TickType.Trade)
339 return ToCsv(milliseconds,
340 tick.LastPrice, tick.Quantity, tick.ExchangeCode, tick.SaleCondition, tick.Suspicious ?
"1" :
"0");
342 if (tick.TickType ==
TickType.Quote)
344 return ToCsv(milliseconds,
345 tick.BidPrice, tick.BidSize, tick.AskPrice, tick.AskSize, tick.ExchangeCode, tick.Suspicious ?
"1" :
"0");
347 if (tick.TickType ==
TickType.OpenInterest)
349 return ToCsv(milliseconds, tick.Value);
357 if (quoteBar !=
null)
359 return ToCsv(milliseconds,
360 ToNonScaledCsv(quoteBar.Bid), quoteBar.LastBidSize,
361 ToNonScaledCsv(quoteBar.Ask), quoteBar.LastAskSize);
364 if (tradeBar !=
null)
366 return ToCsv(milliseconds,
367 tradeBar.Open, tradeBar.High, tradeBar.Low, tradeBar.Close, tradeBar.Volume);
370 if (openInterest !=
null)
372 return ToCsv(milliseconds, openInterest.Value);
380 if (bigQuoteBar !=
null)
382 return ToCsv(longTime,
383 ToNonScaledCsv(bigQuoteBar.Bid), bigQuoteBar.LastBidSize,
384 ToNonScaledCsv(bigQuoteBar.Ask), bigQuoteBar.LastAskSize);
387 if (bigTradeBar !=
null)
389 return ToCsv(longTime, ToNonScaledCsv(bigTradeBar), bigTradeBar.Volume);
392 if (bigOpenInterest !=
null)
394 return ToCsv(longTime, bigOpenInterest.Value);
399 throw new ArgumentOutOfRangeException(nameof(resolution), resolution,
null);
407 var tick = (
Tick)data;
408 if (tick.TickType ==
TickType.Trade)
410 return ToCsv(milliseconds,
411 tick.LastPrice, tick.Quantity, tick.ExchangeCode, tick.SaleCondition, tick.Suspicious ?
"1" :
"0");
413 if (tick.TickType ==
TickType.Quote)
415 return ToCsv(milliseconds,
416 tick.BidPrice, tick.BidSize, tick.AskPrice, tick.AskSize, tick.ExchangeCode, tick.Suspicious ?
"1" :
"0");
418 if (tick.TickType ==
TickType.OpenInterest)
420 return ToCsv(milliseconds, tick.Value);
428 if (quoteBar !=
null)
430 return ToCsv(milliseconds,
431 ToNonScaledCsv(quoteBar.Bid), quoteBar.LastBidSize,
432 ToNonScaledCsv(quoteBar.Ask), quoteBar.LastAskSize);
435 if (tradeBar !=
null)
437 return ToCsv(milliseconds,
438 tradeBar.Open, tradeBar.High, tradeBar.Low, tradeBar.Close, tradeBar.Volume);
441 if (openInterest !=
null)
443 return ToCsv(milliseconds, openInterest.Value);
451 if (bigQuoteBar !=
null)
453 return ToCsv(longTime,
454 ToNonScaledCsv(bigQuoteBar.Bid), bigQuoteBar.LastBidSize,
455 ToNonScaledCsv(bigQuoteBar.Ask), bigQuoteBar.LastAskSize);
458 if (bigTradeBar !=
null)
460 return ToCsv(longTime, ToNonScaledCsv(bigTradeBar), bigTradeBar.Volume);
463 if (bigOpenInterest !=
null)
465 return ToCsv(longTime, bigOpenInterest.Value);
470 throw new ArgumentOutOfRangeException(nameof(resolution), resolution,
null);
475 throw new ArgumentOutOfRangeException(nameof(securityType), securityType,
null);
478 throw new NotImplementedException(Invariant(
479 $
"LeanData.GenerateLine has not yet been implemented for security type: {securityType} at resolution: {resolution}"
507 if (baseDataType == typeof(
Tick) ||
553 var securityType = symbol.
SecurityType.SecurityTypeToLower();
555 var market = symbol.
ID.
Market.ToLowerInvariant();
556 var res = resolution.ResolutionToLower();
557 var directory = Path.Combine(securityType, market, res);
566 return !isHourOrDaily ? Path.Combine(directory, symbol.
Value.ToLowerInvariant()) : directory;
570 return !isHourOrDaily ? Path.Combine(directory, symbol.
ID.
Symbol.ToLowerInvariant()) : directory;
574 return !isHourOrDaily ? Path.Combine(directory, symbol.
Underlying.
Value.ToLowerInvariant()) : directory;
583 return Path.Combine(directory, futureOptionPath);
587 return !isHourOrDaily ? Path.Combine(directory, symbol.
ID.
Symbol.ToLowerInvariant()) : directory;
591 throw new ArgumentOutOfRangeException();
604 symbol.
Value.ToLowerInvariant() +
".csv");
620 var directory = Path.Combine(securityType.SecurityTypeToLower(), market.ToLowerInvariant(), resolution.ResolutionToLower());
623 directory = Path.Combine(directory, symbol.ToLowerInvariant());
626 return Path.Combine(directory,
GenerateZipFileName(symbol, securityType, date, resolution));
634 var path = Path.Combine(symbol.
SecurityType.SecurityTypeToLower(), symbol.
ID.
Market,
"universes");
642 path = Path.Combine(path, symbol.
ID.
Symbol.ToLowerInvariant());
646 path = Path.Combine(path,
681 return Invariant($
"{formattedDate}_{symbol.Value.ToLowerInvariant()}_{tickType}_{resolution}.csv");
686 return $
"{symbol.Value.ToLowerInvariant()}.csv";
689 return Invariant($
"{formattedDate}_{symbol.Value.ToLowerInvariant()}_{resolution.ResolutionToLower()}_{tickType.TickTypeToLower()}.csv");
696 return string.Join(
"_",
698 tickType.TickTypeToLower(),
706 return string.Join(
"_",
709 resolution.ResolutionToLower(),
710 tickType.TickTypeToLower(),
720 var optionTickerBasedPath = symbol.
ID.
Symbol.ToLowerInvariant();
724 return string.Join(
"_",
725 optionTickerBasedPath,
726 tickType.TickTypeToLower(),
734 return string.Join(
"_",
736 optionTickerBasedPath,
737 resolution.ResolutionToLower(),
738 tickType.TickTypeToLower(),
752 string expirationTag;
753 var expiryDate = symbol.
ID.
Date;
757 var contractYearMonth = expiryDate.AddMonths(monthsToAdd).ToStringInvariant(
DateFormat.
YearMonth);
759 expirationTag = $
"{contractYearMonth}_{expiryDate.ToStringInvariant(DateFormat.EightCharacter)}";
763 expirationTag =
"perp";
768 return string.Join(
"_",
769 symbol.
ID.
Symbol.ToLowerInvariant(),
770 tickType.TickTypeToLower(),
775 return string.Join(
"_",
777 symbol.
ID.
Symbol.ToLowerInvariant(),
778 resolution.ResolutionToLower(),
779 tickType.TickTypeToLower(),
785 throw new ArgumentOutOfRangeException();
794 var tickTypeString = tickType.TickTypeToLower();
807 return $
"{symbol.Value.ToLowerInvariant()}.zip";
810 return $
"{formattedDate}_{tickTypeString}.zip";
814 return $
"{symbol.Value.ToLowerInvariant()}_{tickTypeString}.zip";
817 return $
"{formattedDate}_{tickTypeString}.zip";
823 return $
"{optionPath}_{date.Year}_{tickTypeString}_{symbol.ID.OptionStyle.OptionStyleToLower()}.zip";
826 return $
"{formattedDate}_{tickTypeString}_{symbol.ID.OptionStyle.OptionStyleToLower()}.zip";
833 var optionTickerBasedPath = symbol.
ID.
Symbol.ToLowerInvariant();
834 return $
"{optionTickerBasedPath}_{date.Year}_{tickTypeString}_{symbol.ID.OptionStyle.OptionStyleToLower()}.zip";
837 return $
"{formattedDate}_{tickTypeString}_{symbol.ID.OptionStyle.OptionStyleToLower()}.zip";
843 return $
"{symbol.ID.Symbol.ToLowerInvariant()}_{tickTypeString}.zip";
846 return $
"{formattedDate}_{tickTypeString}.zip";
850 throw new ArgumentOutOfRangeException();
861 return $
"{symbol.ToLowerInvariant()}.zip";
866 if (tickType ==
null)
877 var suffix = Invariant($
"_{tickType.Value.TickTypeToLower()}.zip");
878 return zipFileName + suffix;
905 var parts = zipEntryName.Replace(
".csv",
string.Empty).Split(
'_');
913 var style = parts[2].ParseOptionStyle();
914 var right = parts[3].ParseOptionRight();
921 var style = parts[4].ParseOptionStyle();
922 var right = parts[5].ParseOptionRight();
933 var futureExpiry = futureExpiryFunc(expiryYearMonth);
940 var futureExpiry = futureExpiryFunc(expiryYearMonth);
945 throw new NotImplementedException(Invariant(
946 $
"ReadSymbolFromZipEntry is not implemented for {symbol.ID.SecurityType} {symbol.ID.Market} {resolution}"
954 private static long Scale(decimal value)
956 return (
long)(value * 10000);
962 private static string ToCsv(params
object[] args)
965 for (var i = 0; i < args.Length; i++)
968 if (value is decimal)
970 args[i] = ((decimal)value).Normalize();
974 var argsFormatted = args.Select(x => Convert.ToString(x, CultureInfo.InvariantCulture));
975 return string.Join(
",", argsFormatted);
981 private static string ToScaledCsv(
IBar bar)
985 return ToCsv(
string.Empty,
string.Empty,
string.Empty,
string.Empty);
988 return ToCsv(Scale(bar.
Open), Scale(bar.
High), Scale(bar.
Low), Scale(bar.
Close));
995 private static string ToNonScaledCsv(
IBar bar)
999 return ToCsv(
string.Empty,
string.Empty,
string.Empty,
string.Empty);
1030 if (type == typeof(
Tick))
1046 if (securityType.Equals(
"alternative", StringComparison.InvariantCultureIgnoreCase))
1062 market =
string.Empty;
1066 var info = SplitDataPath(fileName);
1073 var foundMarket = info.Find(x => existingMarkets.Contains(x.ToLowerInvariant()));
1074 if (foundMarket !=
null)
1076 market = foundMarket;
1081 Log.
Error($
"LeanData.TryParsePath(): Error encountered while parsing the path {fileName}. Error: {e.GetBaseException()}");
1104 resolution =
default;
1108 if (!
TryParsePath(filePath, out symbol, out date, out resolution))
1114 var fileName = Path.GetFileNameWithoutExtension(filePath);
1115 if (fileName.Contains(
'_', StringComparison.InvariantCulture))
1118 var tickTypePosition = 1;
1119 if (resolution >=
Resolution.Hour && symbol.SecurityType.IsOption())
1123 tickTypePosition = 2;
1125 tickType = (
TickType)Enum.Parse(typeof(
TickType), fileName.Split(
'_')[tickTypePosition],
true);
1131 catch (Exception ex)
1133 Log.
Debug($
"LeanData.TryParsePath(): Error encountered while parsing the path {filePath}. Error: {ex.GetBaseException()}");
1149 date =
default(DateTime);
1153 var info = SplitDataPath(fileName);
1158 if (startIndex == -1)
1162 Log.
Debug($
"LeanData.TryParsePath(): Failed to parse '{fileName}' unexpected SecurityType");
1171 var isUniverses =
false;
1172 if (!Enum.TryParse(info[startIndex + 2],
true, out resolution))
1175 isUniverses = info[startIndex + 2].Equals(
"universes", StringComparison.InvariantCultureIgnoreCase);
1182 Log.
Debug($
"LeanData.TryParsePath(): Failed to parse '{fileName}' unexpected Resolution");
1194 var fileNameNoPath = info[info.Count - 1].Split(
'_').First();
1196 if (!DateTime.TryParseExact(fileNameNoPath,
1198 DateTimeFormatInfo.InvariantInfo,
1199 DateTimeStyles.None,
1203 ticker = fileNameNoPath;
1208 ticker = info[info.Count - 2];
1214 market = info[startIndex + 1];
1215 var components = info[startIndex + 3].Split(
'_');
1218 ticker = components[0];
1223 var dateIndex = securityType ==
SecurityType.FutureOption ? startIndex + 5 : startIndex + 4;
1229 var year =
int.Parse(components[1], CultureInfo.InvariantCulture);
1230 date =
new DateTime(year, 01, 01);
1242 var underlyingFuture =
Symbol.
CreateFuture(underlyingTicker, market, underlyingFutureExpiryDate);
1254 Type dataType =
null;
1255 if (isUniverses && info[startIndex + 3].
Equals(
"etf", StringComparison.InvariantCultureIgnoreCase))
1259 symbol = CreateSymbol(ticker, securityType, market, dataType, date);
1263 catch (Exception ex)
1265 Log.
Debug($
"LeanData.TryParsePath(): Error encountered while parsing the path {fileName}. Error: {ex.GetBaseException()}");
1290 private static Symbol CreateSymbol(
string ticker,
SecurityType securityType,
string market, Type dataType, DateTime mappingResolveDate =
default)
1299 return Symbol.
Create(ticker, securityType, market, baseDataType: dataType);
1303 private static List<string> SplitDataPath(
string fileName)
1305 var pathSeparators =
new[] {
'/',
'\\' };
1308 fileName = fileName.Replace(fileName.GetExtension(),
string.Empty);
1311 while (fileName.First() ==
'.' || pathSeparators.Any(x => x == fileName.First()))
1313 fileName = fileName.Remove(0, 1);
1317 return fileName.Split(pathSeparators, StringSplitOptions.RemoveEmptyEntries).ToList();
1396 if (potentialEnd.Date != endTime.Date)
1400 endTime = potentialEnd;
1403 var period = endTime - startTime;
1412 var nextMidnight = exchangeTimeZoneDate.Date.AddDays(1);
1415 return nextMidnight;
1418 var nextMarketClose = exchangeHours.
GetNextMarketClose(exchangeTimeZoneDate, extendedMarketHours:
false);
1419 if (nextMarketClose > nextMidnight)
1422 if (!exchangeHours.
IsOpen(exchangeTimeZoneDate, extendedMarketHours:
false))
1424 return nextMarketClose;
1426 return nextMidnight;
1428 return nextMarketClose;
1434 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1445 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1456 return dailyStrictEndTimeEnabled;
1480 return dataType !=
null && _strictDailyEndTimesDataTypes.Contains(dataType);
1489 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1492 if (baseData ==
null)
1497 var dataType = baseData.GetType();
1504 if (isZipEntryName && baseData.
Time.Hour == 0)
1513 if (!isZipEntryName && dailyCalendar.End < baseData.
Time)
1518 baseData.
Time = dailyCalendar.Start;
1519 baseData.
EndTime = dailyCalendar.End;
1529 public static void ParseKey(
string key, out
string fileName, out
string entryName)
1541 var hashIndex = key.LastIndexOf(
"#", StringComparison.Ordinal);
1542 if (hashIndex != -1)
1544 entryName = key.Substring(hashIndex + 1);
1545 fileName = key.Substring(0, hashIndex);
1562 var getConsolidatedBar = () =>
1564 if (lastAggregated != consolidator.Consolidated && consolidator.Consolidated !=
null)
1567 lastAggregated = consolidator.Consolidated;
1568 lastAggregated.
Symbol = symbol;
1569 return lastAggregated;
1574 foreach (var dataPoint
in dataPoints)
1576 consolidator.Update(dataPoint);
1577 var consolidated = getConsolidatedBar();
1578 if (consolidated !=
null)
1580 yield
return (T)consolidated;
1585 consolidator.Scan(Time.EndOfTime);
1586 var lastConsolidated = getConsolidatedBar();
1587 if (lastConsolidated !=
null)
1589 yield
return (T)lastConsolidated;
1593 consolidator.DisposeSafely();