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(): {
171 Messages.SymbolRepresentation.FailedToGetMarketForTickerAndUnderlying(ticker, underlying)}");
176 var expiryDate = expiryFunc(
new DateTime(expirationYear, expirationMonth, 1));
188 var split = ticker.Split(
' ');
189 if (split.Length != 2)
199 ticker = parsed.Underlying;
202 if (split[1][0] ==
'P' || split[1][0] ==
'p')
206 else if (split[1][0] ==
'C' || split[1][0] ==
'c')
214 var strike = split[1].Substring(1);
216 if (parsed.ExpirationYearShort < 10)
218 parsed.ExpirationYearShort += 20;
220 var expirationYearParsed = 2000 + parsed.ExpirationYearShort;
222 var expirationDate =
new DateTime(expirationYearParsed, parsed.ExpirationMonth, 1);
224 var strikePrice = decimal.Parse(strike, NumberStyles.Any, CultureInfo.InvariantCulture);
229 Log.
Debug($
"SymbolRepresentation.ParseFutureOptionSymbol(): {Messages.SymbolRepresentation.NoMarketFound(futureTicker)}");
243 strikePrice / strikeScale,
256 public static string GenerateFutureTicker(
string underlying, DateTime expiration,
bool doubleDigitsYear =
true,
bool includeExpirationDate =
true)
258 var year = doubleDigitsYear ? expiration.Year % 100 : expiration.Year % 10;
259 var month = expiration.Month;
262 if (contractMonthDelta < 0)
266 var expirationMonth = expiration.AddDays(-(expiration.Day - 1))
267 .AddMonths(contractMonthDelta);
269 month = expirationMonth.Month;
270 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())
481 var letter = _optionSymbology.Where(x => x.Value.Item2 == symbol.
ID.
OptionRight && x.Value.Item1 == symbol.
ID.
Date.Month).Select(x => x.Key).Single();
482 var twoYearDigit = symbol.
ID.
Date.ToString(
"yy");
483 return $
"{SecurityIdentifier.Ticker(symbol.Underlying, symbol.ID.Date)}{twoYearDigit}{symbol.ID.Date.Day:00}{letter}{symbol.ID.StrikePrice.ToStringInvariant()}";
494 var letterRange = _optionSymbology.Keys
497 var optionTypeDelimiter = ticker.LastIndexOfAny(letterRange);
498 var strikePriceString = ticker.Substring(optionTypeDelimiter + 1, ticker.Length - optionTypeDelimiter - 1);
500 var lookupResult = _optionSymbology[ticker[optionTypeDelimiter].ToStringInvariant()];
501 var month = lookupResult.Item1;
502 var optionRight = lookupResult.Item2;
504 var dayString = ticker.Substring(optionTypeDelimiter - 2, 2);
505 var yearString = ticker.Substring(optionTypeDelimiter - 4, 2);
506 var underlying = ticker.Substring(0, optionTypeDelimiter - 4);
510 if (!Decimal.TryParse(strikePriceString, NumberStyles.Any, CultureInfo.InvariantCulture, out strikePrice))
517 if (!
int.TryParse(dayString, out day))
524 if (!
int.TryParse(yearString, out year))
529 var expirationDate =
new DateTime(2000 + year, month, day);
533 Underlying = underlying,
535 OptionStrike = strikePrice,
536 ExpirationDate = expirationDate
542 private static Dictionary<string, Tuple<int, OptionRight>> _optionSymbology =
new Dictionary<string, Tuple<int, OptionRight>>
591 private static int GetExpirationYear(
int? futureYear, FutureTickerProperties parsed)
593 if(futureYear.HasValue)
595 var referenceYear = 1900 + parsed.ExpirationYearShort;
596 while(referenceYear < futureYear.Value)
601 return referenceYear;
604 var currentYear = DateTime.UtcNow.Year;
605 if (parsed.ExpirationYearShortLength > 1)
608 return 2000 + parsed.ExpirationYearShort;
611 var baseYear = ((int)Math.Round(currentYear / 10.0)) * 10 + parsed.ExpirationYearShort;
612 while (baseYear < currentYear)