public void TestSP500EMiniExpiryDateFunction() { var june2017 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Indices.SP500EMini, Market.USA, new DateTime(2017, 6, 15)); var june2017Func = FuturesExpiryFunctions.FuturesExpiryFunction(june2017.ID.Symbol); Assert.AreEqual(june2017Func(june2017.ID.Date), new DateTime(2017, 6, 16, 9, 30, 0)); var september2017 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Indices.SP500EMini, Market.USA, new DateTime(2017, 9, 1)); var september2017Func = FuturesExpiryFunctions.FuturesExpiryFunction(september2017.ID.Symbol); Assert.AreEqual(september2017Func(september2017.ID.Date), new DateTime(2017, 9, 15, 9, 30, 0)); var december2017 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Indices.SP500EMini, Market.USA, new DateTime(2017, 12, 31)); var december2017Func = FuturesExpiryFunctions.FuturesExpiryFunction(december2017.ID.Symbol); Assert.AreEqual(december2017Func(december2017.ID.Date), new DateTime(2017, 12, 15, 9, 30, 0)); var march2018 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Indices.SP500EMini, Market.USA, new DateTime(2018, 3, 31)); var march2018Func = FuturesExpiryFunctions.FuturesExpiryFunction(march2018.ID.Symbol); Assert.AreEqual(march2018Func(march2018.ID.Date), new DateTime(2018, 3, 16, 9, 30, 0)); var june2018 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Indices.SP500EMini, Market.USA, new DateTime(2018, 3, 31)); var june2018Func = FuturesExpiryFunctions.FuturesExpiryFunction(june2018.ID.Symbol); Assert.AreEqual(june2018Func(june2018.ID.Date), new DateTime(2018, 3, 16, 9, 30, 0)); }
public void HongKongFutureFee(bool canonical) { var symbol = Symbols.CreateFutureSymbol(Futures.Indices.HangSeng, SecurityIdentifier.DefaultDate); if (!canonical) { symbol = Symbols.CreateFutureSymbol(Futures.Indices.HangSeng, FuturesExpiryFunctions.FuturesExpiryFunction(symbol)(new DateTime(2021, 12, 1))); } var entry = MarketHoursDatabase.FromDataFolder().GetEntry(symbol.ID.Market, symbol, symbol.SecurityType); var properties = SymbolPropertiesDatabase.FromDataFolder() .GetSymbolProperties(symbol.ID.Market, symbol, symbol.SecurityType, null); var security = new Future(symbol, entry.ExchangeHours, new Cash(properties.QuoteCurrency, 0, 0), properties, ErrorCurrencyConverter.Instance, RegisteredSecurityDataTypesProvider.Null, new SecurityCache() ); security.SetMarketPrice(new Tick(DateTime.UtcNow, security.Symbol, 100, 100)); var fee = _feeModel.GetOrderFee( new OrderFeeParameters( security, new MarketOrder(security.Symbol, 1000, DateTime.UtcNow) ) ); Assert.AreEqual(Currencies.HKD, fee.Value.Currency); Assert.AreEqual(1000 * 40m, fee.Value.Amount); }
public void TestGoldExpiryDateFunction() { var april2017 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Metals.Gold, Market.USA, new DateTime(2017, 4, 1)); var april2017Func = FuturesExpiryFunctions.FuturesExpiryFunction(april2017.ID.Symbol); Assert.AreEqual(april2017Func(april2017.ID.Date), new DateTime(2017, 4, 26)); var may2017 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Metals.Gold, Market.USA, new DateTime(2017, 5, 31)); var may2017Func = FuturesExpiryFunctions.FuturesExpiryFunction(may2017.ID.Symbol); Assert.AreEqual(may2017Func(may2017.ID.Date), new DateTime(2017, 5, 26)); var june2017 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Metals.Gold, Market.USA, new DateTime(2017, 6, 15)); var june2017Func = FuturesExpiryFunctions.FuturesExpiryFunction(june2017.ID.Symbol); Assert.AreEqual(june2017Func(june2017.ID.Date), new DateTime(2017, 6, 28)); var july2017 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Metals.Gold, Market.USA, new DateTime(2017, 7, 15)); var july2017Func = FuturesExpiryFunctions.FuturesExpiryFunction(july2017.ID.Symbol); Assert.AreEqual(july2017Func(july2017.ID.Date), new DateTime(2017, 7, 27)); var october2018 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Metals.Gold, Market.USA, new DateTime(2018, 10, 15)); var october2018Func = FuturesExpiryFunctions.FuturesExpiryFunction(october2018.ID.Symbol); Assert.AreEqual(october2018Func(october2018.ID.Date), new DateTime(2018, 10, 29)); var december2021 = Symbol.CreateFuture(QuantConnect.Securities.Futures.Metals.Gold, Market.USA, new DateTime(2021, 12, 15)); var december2021Func = FuturesExpiryFunctions.FuturesExpiryFunction(december2021.ID.Symbol); Assert.AreEqual(december2021Func(december2021.ID.Date), new DateTime(2021, 12, 29)); }
/// <summary> /// Gets the FOP's underlying Future. The underlying Future's contract month might not match /// the contract month of the Future Option when providing CBOT or COMEX based FOPs contracts to this method. /// </summary> /// <param name="futureOptionTicker">Future option ticker</param> /// <param name="market">Market of the Future Option</param> /// <param name="futureOptionExpiration">Expiration date of the future option</param> /// <param name="date">Date to search the future chain provider with. Optional, but required for CBOT based contracts</param> /// <returns>Symbol if there is an underlying for the FOP, null if there's no underlying found for the Future Option</returns> public static Symbol GetUnderlyingFutureFromFutureOption(string futureOptionTicker, string market, DateTime futureOptionExpiration, DateTime?date = null) { var futureTicker = FuturesOptionsSymbolMappings.MapFromOption(futureOptionTicker); var canonicalFuture = Symbol.Create(futureTicker, SecurityType.Future, market); // Get the contract month of the FOP to use when searching for the underlying. // If the FOP and Future share the same contract month, this is reused as the future's // contract month so that we can resolve the Future's expiry. var contractMonth = GetFutureContractMonthNoRulesApplied(canonicalFuture, futureOptionExpiration); if (_underlyingFuturesOptionsRules.ContainsKey(futureTicker)) { // The provided ticker follows some sort of rule. Let's figure out the underlying's contract month. var newFutureContractMonth = _underlyingFuturesOptionsRules[futureTicker](contractMonth, date); if (newFutureContractMonth == null) { // This will only happen when we search the Futures chain for a given contract and no // closest match could be made, i.e. there are no futures in the chain that come after the FOP's // contract month. return(null); } contractMonth = newFutureContractMonth.Value; } var futureExpiry = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture)(contractMonth); return(Symbol.CreateFuture(futureTicker, market, futureExpiry)); }
public void FuturesExpiryFunction_MissingSymbol_ShouldThrowArgumentException() { const string badSymbol = "AAAAA"; Assert.Throws <ArgumentException>(() => { FuturesExpiryFunctions.FuturesExpiryFunction(badSymbol); }, $"Expiry function not implemented for {badSymbol} in FuturesExpiryFunctions.FuturesExpiryDictionary"); }
/// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="data">Slice object keyed by symbol containing the stock data</param> public override void OnData(Slice data) { if (IsWarmingUp) { // warm up data _warmedUp = true; if (!_continuousContract.HasData) { throw new Exception($"ContinuousContract did not get any data during warmup!"); } var backMonthExpiration = data.Keys.Single().Underlying.ID.Date; var frontMonthExpiration = FuturesExpiryFunctions.FuturesExpiryFunction(_continuousContract.Symbol)(Time.AddMonths(1)); if (backMonthExpiration <= frontMonthExpiration.Date) { throw new Exception($"Unexpected current mapped contract expiration {backMonthExpiration}" + $" @ {Time} it should be AFTER front month expiration {frontMonthExpiration}"); } } if (data.Keys.Count != 1) { throw new Exception($"We are getting data for more than one symbols! {string.Join(",", data.Keys.Select(symbol => symbol))}"); } if (!Portfolio.Invested) { Buy(_continuousContract.Symbol, 1); } }
/// <summary> /// Parses a contract for future with malformed data. /// Malformed data usually manifests itself by having "0" assigned to some values /// we expect, like the contract's expiry date. The contract is returned by IB /// like this, usually due to a high amount of data subscriptions that are active /// in an account, surpassing IB's imposed limit. Read more about this here: https://interactivebrokers.github.io/tws-api/rtd_fqa_errors.html#rtd_common_errors_maxmktdata /// /// We are provided a string in the Symbol in malformed contracts that can be /// parsed to construct the clean contract, which is done by this method. /// </summary> /// <param name="malformedContract">Malformed contract (for futures), i.e. a contract with invalid values ("0") in some of its fields</param> /// <param name="symbolPropertiesDatabase">The symbol properties database to use</param> /// <returns>Clean Contract for the future</returns> /// <remarks> /// The malformed contract returns data similar to the following when calling <see cref="InteractiveBrokersBrokerage.GetContractDetails"/>: ES MAR2021 /// </remarks> public Contract ParseMalformedContractFutureSymbol(Contract malformedContract, SymbolPropertiesDatabase symbolPropertiesDatabase) { Log.Trace($"InteractiveBrokersSymbolMapper.ParseMalformedContractFutureSymbol(): Parsing malformed contract: {InteractiveBrokersBrokerage.GetContractDescription(malformedContract)} with trading class: \"{malformedContract.TradingClass}\""); // capture any character except spaces, match spaces, capture any char except digits, capture digits var matches = Regex.Matches(malformedContract.Symbol, @"^(\S*)\s*(\D*)(\d*)"); var match = matches[0].Groups; var contractSymbol = match[1].Value; var contractMonthExpiration = DateTime.ParseExact(match[2].Value, "MMM", CultureInfo.CurrentCulture).Month; var contractYearExpiration = match[3].Value; var leanSymbol = GetLeanRootSymbol(contractSymbol); string market; if (!symbolPropertiesDatabase.TryGetMarket(leanSymbol, SecurityType.Future, out market)) { market = InteractiveBrokersBrokerageModel.DefaultMarketMap[SecurityType.Future]; } var canonicalSymbol = Symbol.Create(leanSymbol, SecurityType.Future, market); var contractMonthYear = new DateTime(int.Parse(contractYearExpiration, CultureInfo.InvariantCulture), contractMonthExpiration, 1); // we get the expiration date using the FuturesExpiryFunctions var contractExpirationDate = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalSymbol)(contractMonthYear); return(new Contract { Symbol = contractSymbol, Multiplier = malformedContract.Multiplier, LastTradeDateOrContractMonth = $"{contractExpirationDate:yyyyMMdd}", Exchange = malformedContract.Exchange, SecType = malformedContract.SecType, IncludeExpired = false, Currency = malformedContract.Currency }); }
/// <summary> /// Helper method to parse and generate a future symbol from a given user friendly representation /// </summary> /// <param name="ticker">The future ticker, for example 'ESZ1'</param> /// <param name="futureYear">Clarifies the year for the current future</param> /// <returns>The future symbol or null if failed</returns> public static Symbol ParseFutureSymbol(string ticker, int?futureYear = null) { var disambiguatedFutureYear = futureYear ?? TodayUtc.Year; var parsed = ParseFutureTicker(ticker); if (parsed == null) { return(null); } var underlying = parsed.Underlying; var expirationYearShort = parsed.ExpirationYearShort; var expirationMonth = parsed.ExpirationMonth; var expirationYear = GetExpirationYear(expirationYearShort, disambiguatedFutureYear); if (!SymbolPropertiesDatabase.FromDataFolder().TryGetMarket(underlying, SecurityType.Future, out var market)) { Log.Debug($"SymbolRepresentation.ParseFutureSymbol(): Failed to get market for future '{ticker}' and underlying '{underlying}'"); return(null); } var expiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(Symbol.Create(underlying, SecurityType.Future, market)); var expiryDate = expiryFunc(new DateTime(expirationYear, expirationMonth, 1)); return(Symbol.CreateFuture(underlying, market, expiryDate)); }
public void LiveOptionChainProviderReturnsFutureOptionData() { var now = DateTime.Now; var december = new DateTime(now.Year, 12, 1); var canonicalFuture = Symbol.Create("ES", SecurityType.Future, Market.CME); var expiry = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture)(december); // When the current year's december contract expires, the test starts failing. // This will happen around the last 10 days of December, but will start working // once we've crossed into the new year. // Let's try the next listed contract, which is in March of the next year if this is the case. if (now >= expiry) { expiry = now.AddMonths(-now.Month).AddYears(1).AddMonths(3); } var underlyingFuture = Symbol.CreateFuture("ES", Market.CME, expiry); var provider = new LiveOptionChainProvider(); var result = provider.GetOptionContractList(underlyingFuture, now).ToList(); Assert.AreNotEqual(0, result.Count); foreach (var symbol in result) { Assert.IsTrue(symbol.HasUnderlying); Assert.AreEqual(Market.CME, symbol.ID.Market); Assert.AreEqual(OptionStyle.American, symbol.ID.OptionStyle); Assert.GreaterOrEqual(symbol.ID.StrikePrice, 100m); Assert.Less(symbol.ID.StrikePrice, 30000m); } }
/// <summary> /// Converts an InteractiveBrokers symbol to a Lean symbol instance /// </summary> /// <param name="brokerageSymbol">The InteractiveBrokers symbol</param> /// <param name="securityType">The security type</param> /// <param name="market">The market</param> /// <param name="expirationDate">Expiration date of the security(if applicable)</param> /// <param name="strike">The strike of the security (if applicable)</param> /// <param name="optionRight">The option right of the security (if applicable)</param> /// <returns>A new Lean Symbol instance</returns> public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market, DateTime expirationDate = default(DateTime), decimal strike = 0, OptionRight optionRight = 0) { if (string.IsNullOrWhiteSpace(brokerageSymbol)) { throw new ArgumentException("Invalid symbol: " + brokerageSymbol); } if (securityType != SecurityType.Forex && securityType != SecurityType.Equity && securityType != SecurityType.Option && securityType != SecurityType.Future && securityType != SecurityType.FutureOption) { throw new ArgumentException("Invalid security type: " + securityType); } try { switch (securityType) { case SecurityType.Future: return(Symbol.CreateFuture(GetLeanRootSymbol(brokerageSymbol), market, expirationDate)); case SecurityType.Option: return(Symbol.CreateOption(brokerageSymbol, market, OptionStyle.American, optionRight, strike, expirationDate)); case SecurityType.FutureOption: var canonicalFutureSymbol = Symbol.Create(GetLeanRootSymbol(brokerageSymbol), SecurityType.Future, market); var futureContractMonth = FuturesOptionsExpiryFunctions.GetFutureContractMonth(canonicalFutureSymbol, expirationDate); var futureExpiry = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFutureSymbol)(futureContractMonth); return(Symbol.CreateOption( Symbol.CreateFuture( brokerageSymbol, market, futureExpiry), market, OptionStyle.American, optionRight, strike, expirationDate)); case SecurityType.Equity: brokerageSymbol = brokerageSymbol.Replace(" ", "."); break; } return(Symbol.Create(brokerageSymbol, securityType, market)); } catch (Exception) { throw new ArgumentException($"Invalid symbol: {brokerageSymbol}, security type: {securityType}, market: {market}."); } }
/// <summary> /// Creates a symbol from the specified zip entry name /// </summary> /// <param name="symbol">The root symbol of the output symbol</param> /// <param name="resolution">The resolution of the data source producing the zip entry name</param> /// <param name="zipEntryName">The zip entry name to be parsed</param> /// <returns>A new symbol representing the zip entry name</returns> public static Symbol ReadSymbolFromZipEntry(Symbol symbol, Resolution resolution, string zipEntryName) { var isHourlyOrDaily = resolution == Resolution.Hour || resolution == Resolution.Daily; var parts = zipEntryName.Replace(".csv", string.Empty).Split('_'); switch (symbol.ID.SecurityType) { case SecurityType.Option: case SecurityType.FutureOption: case SecurityType.IndexOption: if (isHourlyOrDaily) { var style = (OptionStyle)Enum.Parse(typeof(OptionStyle), parts[2], true); var right = (OptionRight)Enum.Parse(typeof(OptionRight), parts[3], true); var strike = Parse.Decimal(parts[4]) / 10000m; var expiry = Parse.DateTimeExact(parts[5], DateFormat.EightCharacter); return(Symbol.CreateOption(symbol.Underlying, symbol.ID.Market, style, right, strike, expiry)); } else { var style = (OptionStyle)Enum.Parse(typeof(OptionStyle), parts[4], true); var right = (OptionRight)Enum.Parse(typeof(OptionRight), parts[5], true); var strike = Parse.Decimal(parts[6]) / 10000m; var expiry = DateTime.ParseExact(parts[7], DateFormat.EightCharacter, CultureInfo.InvariantCulture); return(Symbol.CreateOption(symbol.Underlying, symbol.ID.Market, style, right, strike, expiry)); } case SecurityType.Future: if (isHourlyOrDaily) { var expiryYearMonth = Parse.DateTimeExact(parts[2], DateFormat.YearMonth); var futureExpiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(symbol); var futureExpiry = futureExpiryFunc(expiryYearMonth); return(Symbol.CreateFuture(parts[0], symbol.ID.Market, futureExpiry)); } else { var expiryYearMonth = Parse.DateTimeExact(parts[4], DateFormat.YearMonth); var futureExpiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(symbol); var futureExpiry = futureExpiryFunc(expiryYearMonth); return(Symbol.CreateFuture(parts[1], symbol.ID.Market, futureExpiry)); } default: throw new NotImplementedException(Invariant( $"ReadSymbolFromZipEntry is not implemented for {symbol.ID.SecurityType} {symbol.ID.Market} {resolution}" )); } }
/// <summary> /// Gets the theoretical (i.e. intermediate/naive) future contract month if we assumed a 1-1 mapping /// between FOPs contract months and Futures contract months, i.e. they share the same contract month. /// </summary> /// <param name="canonicalFutureSymbol">Canonical future Symbol</param> /// <param name="futureOptionExpirationDate">Future Option Expiration Date</param> /// <returns>Contract month assuming that the Future Option and Future share the same contract month</returns> private static DateTime GetFutureContractMonthNoRulesApplied(Symbol canonicalFutureSymbol, DateTime futureOptionExpirationDate) { var baseOptionExpiryMonthDate = new DateTime(futureOptionExpirationDate.Year, futureOptionExpirationDate.Month, 1); if (!_futuresOptionsExpiryDelta.ContainsKey(canonicalFutureSymbol.ID.Symbol)) { // For contracts like CL, they have no expiry delta between the Futures and FOPs, so we hit this path. // However, it does have a delta between its expiry and contract month, which we adjust here before // claiming that `baseOptionExpiryMonthDate` is the future's contract month. var futuresExpiry = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFutureSymbol)(baseOptionExpiryMonthDate); var futuresDelta = FuturesExpiryUtilityFunctions.GetDeltaBetweenContractMonthAndContractExpiry(canonicalFutureSymbol.ID.Symbol, futuresExpiry); return(baseOptionExpiryMonthDate.AddMonths(futuresDelta)); } return(baseOptionExpiryMonthDate.AddMonths(_futuresOptionsExpiryDelta[canonicalFutureSymbol.ID.Symbol])); }
public void SoftsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var futureSymbol = GetFutureSymbol(symbol, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(GetFutureSymbol(symbol)); //Act var actual = func(futureSymbol.ID.Date); var expected = date.LastTrade; //Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } }
public void DairyExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { // Arrange var security = Symbol.CreateFuture(symbol, "usa", date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(security.ID.Symbol); // Act var actual = func(security.ID.Date); var expected = date.LastTrade + Parse.TimeSpan(dayTime); // Assert Assert.AreEqual(expected, actual, "Failed for symbol: " + symbol); } }
public void GrainsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol) { Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data"); foreach (var date in _data[symbol]) { //Arrange var security = Symbol.CreateFuture(symbol, Market.USA, date.ContractMonth); var func = FuturesExpiryFunctions.FuturesExpiryFunction(security.ID.Symbol); //Act var calculated = func(security.ID.Date); var actual = date.LastTrade; //Assert Assert.AreEqual(calculated, actual, "Failed for symbol: " + symbol); } }
/// <summary> /// Gets the Futures Options' expiry for the given contract month. /// </summary> /// <param name="canonicalFutureOptionSymbol">Canonical Futures Options Symbol. Will be made canonical if not provided a canonical</param> /// <param name="futureContractMonth">Contract month of the underlying Future</param> /// <returns>Expiry date/time</returns> public static DateTime FuturesOptionExpiry(Symbol canonicalFutureOptionSymbol, DateTime futureContractMonth) { if (!canonicalFutureOptionSymbol.IsCanonical() || !canonicalFutureOptionSymbol.Underlying.IsCanonical()) { canonicalFutureOptionSymbol = Symbol.CreateOption( Symbol.Create(canonicalFutureOptionSymbol.Underlying.ID.Symbol, SecurityType.Future, canonicalFutureOptionSymbol.Underlying.ID.Market), canonicalFutureOptionSymbol.ID.Market, default(OptionStyle), default(OptionRight), default(decimal), SecurityIdentifier.DefaultDate); } Func <DateTime, DateTime> expiryFunction; if (!_futuresOptionExpiryFunctions.TryGetValue(canonicalFutureOptionSymbol, out expiryFunction)) { // No definition exists for this FOP. Let's default to futures expiry. return(FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFutureOptionSymbol.Underlying)(futureContractMonth)); } return(expiryFunction(futureContractMonth)); }
/// <summary> /// Creates a symbol from the specified zip entry name /// </summary> /// <param name="symbol">The root symbol of the output symbol</param> /// <param name="resolution">The resolution of the data source producing the zip entry name</param> /// <param name="zipEntryName">The zip entry name to be parsed</param> /// <returns>A new symbol representing the zip entry name</returns> public static Symbol ReadSymbolFromZipEntry(Symbol symbol, Resolution resolution, string zipEntryName) { var isHourlyOrDaily = resolution == Resolution.Hour || resolution == Resolution.Daily; var parts = zipEntryName.Replace(".csv", string.Empty).Split('_'); switch (symbol.ID.SecurityType) { case SecurityType.Option: if (isHourlyOrDaily) { var style = (OptionStyle)Enum.Parse(typeof(OptionStyle), parts[2], true); var right = (OptionRight)Enum.Parse(typeof(OptionRight), parts[3], true); var strike = decimal.Parse(parts[4]) / 10000m; var expiry = DateTime.ParseExact(parts[5], DateFormat.EightCharacter, null); return(Symbol.CreateOption(symbol.Underlying, Market.USA, style, right, strike, expiry)); } else { var style = (OptionStyle)Enum.Parse(typeof(OptionStyle), parts[4], true); var right = (OptionRight)Enum.Parse(typeof(OptionRight), parts[5], true); var strike = decimal.Parse(parts[6]) / 10000m; var expiry = DateTime.ParseExact(parts[7], DateFormat.EightCharacter, null); return(Symbol.CreateOption(symbol.Underlying, Market.USA, style, right, strike, expiry)); } break; case SecurityType.Future: var expiryYearMonth = DateTime.ParseExact(parts[4], DateFormat.YearMonth, null); var futureExpiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(parts[1]); var futureExpiry = futureExpiryFunc(expiryYearMonth); return(Symbol.CreateFuture(parts[1], Market.USA, futureExpiry)); default: throw new NotImplementedException("ReadSymbolFromZipEntry is not implemented for " + symbol.ID.SecurityType + " " + resolution); } }
public void FuturesExpiryFunctions_AllFutures_ShouldHaveExpiryFunction() { var missingFutures = new List <string>(); var futuresSymbols = typeof(QuantConnect.Securities.Futures).GetNestedTypes() .SelectMany(x => x.GetFields()) .Select(x => x.GetValue(null)) // null for obj in GetValue indicates static field .Cast <string>(); foreach (var futuresSymbol in futuresSymbols) { try { FuturesExpiryFunctions.FuturesExpiryFunction(futuresSymbol); } catch (ArgumentException) { missingFutures.Add(futuresSymbol); } } Assert.IsEmpty(missingFutures, $"The following symbols do not have an expiry function defined in FuturesExpiryFunction.FuturesExpiryDictionary: {string.Join(", ", missingFutures)}"); }
/// <summary> /// Creates a future option Symbol from the provided ticker /// </summary> /// <param name="ticker">The future option ticker, for example 'ESZ0 P3590'</param> /// <param name="strikeScale">Optional the future option strike scale factor</param> public static Symbol ParseFutureOptionSymbol(string ticker, int strikeScale = 1) { var split = ticker.Split(' '); if (split.Length != 2) { return(null); } var parsed = ParseFutureTicker(split[0]); if (parsed == null) { return(null); } ticker = parsed.Underlying; OptionRight right; if (split[1][0] == 'P' || split[1][0] == 'p') { right = OptionRight.Put; } else if (split[1][0] == 'C' || split[1][0] == 'c') { right = OptionRight.Call; } else { return(null); } var strike = split[1].Substring(1); if (parsed.ExpirationYearShort < 10) { parsed.ExpirationYearShort += 20; } var expirationYearParsed = 2000 + parsed.ExpirationYearShort; var expirationDate = new DateTime(expirationYearParsed, parsed.ExpirationMonth, 1); var strikePrice = decimal.Parse(strike, NumberStyles.Any, CultureInfo.InvariantCulture); var futureTicker = FuturesOptionsSymbolMappings.MapFromOption(ticker); if (!SymbolPropertiesDatabase.FromDataFolder().TryGetMarket(futureTicker, SecurityType.Future, out var market)) { Log.Debug($"SymbolRepresentation.ParseFutureOptionSymbol(): No market found for '{futureTicker}'"); return(null); } var canonicalFuture = Symbol.Create(futureTicker, SecurityType.Future, market); var futureExpiry = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture)(expirationDate); var future = Symbol.CreateFuture(futureTicker, market, futureExpiry); var futureOptionExpiry = FuturesOptionsExpiryFunctions.GetFutureOptionExpiryFromFutureExpiry(future); return(Symbol.CreateOption(future, market, OptionStyle.American, right, strikePrice / strikeScale, futureOptionExpiry)); }
private IEnumerable <Symbol> GetFutureOptionContractList(Symbol futureContractSymbol, DateTime date) { var symbols = new List <Symbol>(); var retries = 0; var maxRetries = 5; while (++retries <= maxRetries) { try { _rateGate.WaitToProceed(); var productResponse = _client.GetAsync(CMEProductSlateURL.Replace(CMESymbolReplace, futureContractSymbol.ID.Symbol)) .SynchronouslyAwaitTaskResult(); productResponse.EnsureSuccessStatusCode(); var productResults = JsonConvert.DeserializeObject <CMEProductSlateV2ListResponse>(productResponse.Content .ReadAsStringAsync() .SynchronouslyAwaitTaskResult()); productResponse.Dispose(); // We want to gather the future product to get the future options ID var futureProductId = productResults.Products.Where(p => p.Globex == futureContractSymbol.ID.Symbol && p.GlobexTraded && p.Cleared == "Futures") .Select(p => p.Id) .Single(); var optionsTradesAndExpiries = CMEOptionsTradeDateAndExpirations.Replace(CMEProductCodeReplace, futureProductId.ToStringInvariant()); _rateGate.WaitToProceed(); var optionsTradesAndExpiriesResponse = _client.GetAsync(optionsTradesAndExpiries).SynchronouslyAwaitTaskResult(); optionsTradesAndExpiriesResponse.EnsureSuccessStatusCode(); var tradesAndExpiriesResponse = JsonConvert.DeserializeObject <List <CMEOptionsTradeDatesAndExpiration> >(optionsTradesAndExpiriesResponse.Content .ReadAsStringAsync() .SynchronouslyAwaitTaskResult()); optionsTradesAndExpiriesResponse.Dispose(); // For now, only support American options on CME var selectedOption = tradesAndExpiriesResponse .FirstOrDefault(x => !x.Daily && !x.Weekly && !x.Sto && x.OptionType == "AME"); if (selectedOption == null) { Log.Error($"LiveOptionChainProvider.GetFutureOptionContractList(): Found no matching future options for contract {futureContractSymbol}"); yield break; } // Gather the month code and the year's last number to query the next API, which expects an expiration as `<MONTH_CODE><YEAR_LAST_NUMBER>` var canonicalFuture = Symbol.Create(futureContractSymbol.ID.Symbol, SecurityType.Future, futureContractSymbol.ID.Market); var expiryFunction = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture); var futureContractExpiration = selectedOption.Expirations .Select(x => new KeyValuePair <CMEOptionsExpiration, DateTime>(x, expiryFunction(new DateTime(x.Expiration.Year, x.Expiration.Month, 1)))) .FirstOrDefault(x => x.Value.Year == futureContractSymbol.ID.Date.Year && x.Value.Month == futureContractSymbol.ID.Date.Month) .Key; if (futureContractExpiration == null) { Log.Error($"LiveOptionChainProvider.GetFutureOptionContractList(): Found no future options with matching expiry year and month for contract {futureContractSymbol}"); yield break; } var futureContractMonthCode = futureContractExpiration.Expiration.Code; _rateGate.WaitToProceed(); // Subtract one day from now for settlement API since settlement may not be available for today yet var optionChainQuotesResponseResult = _client.GetAsync(CMEOptionChainQuotesURL .Replace(CMEProductCodeReplace, selectedOption.ProductId.ToStringInvariant()) .Replace(CMEProductExpirationReplace, futureContractMonthCode) + Math.Floor((DateTime.UtcNow - _epoch).TotalMilliseconds).ToStringInvariant()); optionChainQuotesResponseResult.Result.EnsureSuccessStatusCode(); var futureOptionChain = JsonConvert.DeserializeObject <CMEOptionChainQuotes>(optionChainQuotesResponseResult.Result.Content .ReadAsStringAsync() .SynchronouslyAwaitTaskResult()) .Quotes .DistinctBy(s => s.StrikePrice) .ToList(); optionChainQuotesResponseResult.Dispose(); // Each CME contract can have arbitrary scaling applied to the strike price, so we normalize it to the // underlying's price via static entries. var optionStrikePriceScaleFactor = CMEStrikePriceScalingFactors.GetScaleFactor(futureContractSymbol); var canonicalOption = Symbol.CreateOption( futureContractSymbol, futureContractSymbol.ID.Market, futureContractSymbol.SecurityType.DefaultOptionStyle(), default(OptionRight), default(decimal), SecurityIdentifier.DefaultDate); foreach (var optionChainEntry in futureOptionChain) { var futureOptionExpiry = FuturesOptionsExpiryFunctions.GetFutureOptionExpiryFromFutureExpiry(futureContractSymbol, canonicalOption); var scaledStrikePrice = optionChainEntry.StrikePrice / optionStrikePriceScaleFactor; // Calls and puts share the same strike, create two symbols per each to avoid iterating twice. symbols.Add(Symbol.CreateOption( futureContractSymbol, futureContractSymbol.ID.Market, OptionStyle.American, OptionRight.Call, scaledStrikePrice, futureOptionExpiry)); symbols.Add(Symbol.CreateOption( futureContractSymbol, futureContractSymbol.ID.Market, OptionStyle.American, OptionRight.Put, scaledStrikePrice, futureOptionExpiry)); } break; } catch (HttpRequestException err) { if (retries != maxRetries) { Log.Error(err, $"Failed to retrieve futures options chain from CME, retrying ({retries} / {maxRetries})"); continue; } Log.Error(err, $"Failed to retrieve futures options chain from CME, returning empty result ({retries} / {retries})"); } } foreach (var symbol in symbols) { yield return(symbol); } }
/// <summary> /// Parse a string line into a future tick. /// </summary> /// <param name="line"></param> /// <returns></returns> private Tick Parse(string line) { try { const int TradeMask = 2; const int QuoteMask = 1; const int OpenInterestMask = 11; const int MessageTypeMask = 15; // parse csv check column count var csv = line.ToCsv(); if (csv.Count - 1 < _columnsCount) { return(null); } var ticker = csv[_columnTicker]; // we filter out options and spreads if (ticker.IndexOfAny(new [] { ' ', '-' }) != -1) { return(null); } ticker = ticker.Trim(new char[] { '"' }); if (_symbolFilter != null && !_symbolFilter.Contains(ticker)) { return(null); } if (string.IsNullOrEmpty(ticker)) { return(null); } var parsed = SymbolRepresentation.ParseFutureTicker(ticker); if (parsed == null || !_symbolMultipliers.ContainsKey(parsed.Underlying)) { return(null); } // ignoring time zones completely -- this is all in the 'data-time-zone' var timeString = csv[_columnTimestamp]; var time = DateTime.ParseExact(timeString, "yyyyMMddHHmmssFFF", CultureInfo.InvariantCulture); var underlying = parsed.Underlying; var expirationYearShort = parsed.ExpirationYearShort; var expirationMonth = parsed.ExpirationMonth; var expirationYear = GetExpirationYear(time, expirationYearShort); var expiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(underlying); var expiryDate = expiryFunc(new DateTime(expirationYear, expirationMonth, 1)); var symbol = Symbol.CreateFuture(underlying, Market.USA, expiryDate); // detecting tick type (trade or quote) TickType tickType; bool isAsk = false; var type = csv[_columnType].ConvertInvariant <int>(); if ((type & MessageTypeMask) == TradeMask) { tickType = TickType.Trade; } else if ((type & MessageTypeMask) == OpenInterestMask) { tickType = TickType.OpenInterest; } else if ((type & MessageTypeMask) == QuoteMask) { tickType = TickType.Quote; switch (csv[_columnSide]) { case "B": isAsk = false; break; case "S": isAsk = true; break; default: { return(null); } } } else { return(null); } // All futures but VIX are delivered with a scale factor of 10000000000. var scaleFactor = symbol.ID.Symbol == "VX" ? decimal.One : 10000000000m; var price = csv[_columnPrice].ToDecimal() / scaleFactor; var quantity = csv[_columnQuantity].ToInt32(); price *= _symbolMultipliers[underlying]; switch (tickType) { case TickType.Quote: var tick = new Tick { Symbol = symbol, Time = time, TickType = tickType, Exchange = Market.USA, Value = price }; if (isAsk) { tick.AskPrice = price; tick.AskSize = quantity; } else { tick.BidPrice = price; tick.BidSize = quantity; } return(tick); case TickType.Trade: tick = new Tick { Symbol = symbol, Time = time, TickType = tickType, Exchange = Market.USA, Value = price, Quantity = quantity }; return(tick); case TickType.OpenInterest: tick = new Tick { Symbol = symbol, Time = time, TickType = tickType, Exchange = Market.USA, Value = quantity }; return(tick); } return(null); } catch (Exception err) { Log.Error(err); Log.Trace("Line: {0}", line); return(null); } }