19 using System.Globalization;
21 using System.Collections.Generic;
26 using System.Text.RegularExpressions;
37 private static readonly Regex _optionTickerRegex =
new Regex(
@"^([A-Z]+)\s*(\d{6})([CP])(\d{8})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
106 var doubleDigitYear =
char.IsDigit(ticker.Substring(ticker.Length - 2, 1)[0]);
107 var doubleDigitOffset = doubleDigitYear ? 1 : 0;
109 var expirationDayOffset = 0;
110 var expirationDay = 1;
111 if (ticker.Length > 4 + doubleDigitOffset)
113 var potentialExpirationDay = ticker.Substring(ticker.Length - 4 - doubleDigitOffset, 2);
114 var containsExpirationDay =
char.IsDigit(potentialExpirationDay[0]) &&
char.IsDigit(potentialExpirationDay[1]);
115 expirationDayOffset = containsExpirationDay ? 2 : 0;
116 if (containsExpirationDay && !
int.TryParse(potentialExpirationDay, out expirationDay))
122 var expirationYearString = ticker.Substring(ticker.Length - 1 - doubleDigitOffset, 1 + doubleDigitOffset);
123 var expirationMonthString = ticker.Substring(ticker.Length - 2 - doubleDigitOffset, 1);
124 var underlyingString = ticker.Substring(0, ticker.Length - 2 - doubleDigitOffset - expirationDayOffset);
126 int expirationYearShort;
128 if (!
int.TryParse(expirationYearString, out expirationYearShort))
142 Underlying = underlyingString,
143 ExpirationYearShort = expirationYearShort,
144 ExpirationYearShortLength = expirationYearString.Length,
145 ExpirationMonth = expirationMonth,
146 ExpirationDay = expirationDay
164 var underlying = parsed.Underlying;
165 var expirationMonth = parsed.ExpirationMonth;
166 var expirationYear = GetExpirationYear(futureYear, parsed);
170 Log.
Debug($
@"SymbolRepresentation.ParseFutureSymbol(): {Messages.SymbolRepresentation.FailedToGetMarketForTickerAndUnderlying(ticker, underlying)}");
175 var expiryDate = expiryFunc(
new DateTime(expirationYear, expirationMonth, 1));
187 var split = ticker.Split(
' ');
188 if (split.Length != 2)
198 ticker = parsed.Underlying;
201 if (split[1][0] ==
'P' || split[1][0] ==
'p')
205 else if (split[1][0] ==
'C' || split[1][0] ==
'c')
213 var strike = split[1].Substring(1);
215 if (parsed.ExpirationYearShort < 10)
217 parsed.ExpirationYearShort += 20;
219 var expirationYearParsed = 2000 + parsed.ExpirationYearShort;
221 var expirationDate =
new DateTime(expirationYearParsed, parsed.ExpirationMonth, 1);
223 var strikePrice = decimal.Parse(strike, NumberStyles.Any, CultureInfo.InvariantCulture);
228 Log.
Debug($
"SymbolRepresentation.ParseFutureOptionSymbol(): {Messages.SymbolRepresentation.NoMarketFound(futureTicker)}");
242 strikePrice / strikeScale,
255 public static string GenerateFutureTicker(
string underlying, DateTime expiration,
bool doubleDigitsYear =
true,
bool includeExpirationDate =
true)
257 var year = doubleDigitsYear ? expiration.Year % 100 : expiration.Year % 10;
258 var month = expiration.Month;
261 if (contractMonthDelta < 0)
265 var expirationMonth = expiration.AddDays(-(expiration.Day - 1))
266 .AddMonths(contractMonthDelta);
268 month = expirationMonth.Month;
269 year = doubleDigitsYear ? expirationMonth.Year % 100 : expirationMonth.Year % 10;
274 month += contractMonthDelta;
287 var expirationDay = includeExpirationDate ? $
"{expiration.Day:00}" :
string.Empty;
289 return $
"{underlying}{expirationDay}{FuturesMonthLookup[month]}{year}";
302 throw new ArgumentException(
320 if (underlying.Length > 5) underlying +=
" ";
321 return Invariant($
"{underlying,-6}{expiration.ToStringInvariant(DateFormat.SixCharacter)}{right.ToStringPerformance()[0]}{(strikePrice * 1000m):00000000}");
335 throw new ArgumentException(
352 return Invariant($
"{underlying}{expiration.ToStringInvariant(DateFormat.SixCharacter)}{right.ToStringPerformance()[0]}{(strikePrice * 1000m):00000000}");
383 string underlyingSymbolValue;
388 underlyingSymbolValue = optionTicker;
395 underlyingSymbolValue = underlyingSid.
Symbol;
399 throw new NotImplementedException($
"ParseOptionTickerOSI(): {Messages.SymbolRepresentation.SecurityTypeNotImplemented(securityType)}");
402 return new Symbol(sid, ticker,
new Symbol(underlyingSid, underlyingSymbolValue));
420 strike = decimal.Zero;
422 if (
string.IsNullOrEmpty(ticker))
427 var match = _optionTickerRegex.Match(ticker);
433 optionTicker = match.Groups[1].Value;
453 out
string underlyingTicker, out DateTime expiry, out
OptionRight right, out decimal strike)
456 underlyingTicker =
null;
459 strike = decimal.Zero;
461 if (!securityType.IsOption())
482 var letter = _optionSymbology.Where(x => x.Value.Item2 == symbol.
ID.
OptionRight && x.Value.Item1 == symbol.
ID.
Date.Month).Select(x => x.Key).Single();
483 var twoYearDigit = symbol.
ID.
Date.ToString(
"yy");
484 return $
"{symbolTicker}{twoYearDigit}{symbol.ID.Date.Day:00}{letter}{symbol.ID.StrikePrice.ToStringInvariant()}";
495 var letterRange = _optionSymbology.Keys
498 var optionTypeDelimiter = ticker.LastIndexOfAny(letterRange);
499 var strikePriceString = ticker.Substring(optionTypeDelimiter + 1, ticker.Length - optionTypeDelimiter - 1);
501 var lookupResult = _optionSymbology[ticker[optionTypeDelimiter].ToStringInvariant()];
502 var month = lookupResult.Item1;
503 var optionRight = lookupResult.Item2;
505 var dayString = ticker.Substring(optionTypeDelimiter - 2, 2);
506 var yearString = ticker.Substring(optionTypeDelimiter - 4, 2);
507 var underlying = ticker.Substring(0, optionTypeDelimiter - 4);
511 if (!Decimal.TryParse(strikePriceString, NumberStyles.Any, CultureInfo.InvariantCulture, out strikePrice))
518 if (!
int.TryParse(dayString, out day))
525 if (!
int.TryParse(yearString, out year))
530 var expirationDate =
new DateTime(2000 + year, month, day);
534 Underlying = underlying,
536 OptionStrike = strikePrice,
537 ExpirationDate = expirationDate
543 private static Dictionary<string, Tuple<int, OptionRight>> _optionSymbology =
new Dictionary<string, Tuple<int, OptionRight>>
592 private static int GetExpirationYear(
int? futureYear, FutureTickerProperties parsed)
594 if (futureYear.HasValue)
596 var referenceYear = 1900 + parsed.ExpirationYearShort;
597 while (referenceYear < futureYear.Value)
602 return referenceYear;
605 var currentYear = DateTime.UtcNow.Year;
606 if (parsed.ExpirationYearShortLength > 1)
609 return 2000 + parsed.ExpirationYearShort;
612 var baseYear = ((int)Math.Round(currentYear / 10.0)) * 10 + parsed.ExpirationYearShort;
613 while (baseYear < currentYear)