/// <summary> /// Perform preorder checks to ensure we have sufficient capital, /// the market is open, and we haven't exceeded maximum realistic orders per day. /// </summary> /// <returns>OrderResponse. If no error, order request is submitted.</returns> private OrderResponse PreOrderChecksImpl(SubmitOrderRequest request) { //Ordering 0 is useless. if (request.Quantity == 0 || request.Symbol == null || request.Symbol == Symbol.Empty) { return(OrderResponse.ZeroQuantity(request)); } //If we're not tracking this symbol: throw error: if (!Securities.ContainsKey(request.Symbol) && !_sentNoDataError) { _sentNoDataError = true; return(OrderResponse.Error(request, OrderResponseErrorCode.MissingSecurity, "You haven't requested " + request.Symbol.Permtick + " data. Add this with AddSecurity() in the Initialize() Method.")); } //Set a temporary price for validating order for market orders: var security = Securities[request.Symbol]; var price = security.Price; //Check the exchange is open before sending a market on close orders //Allow market orders, they'll just execute when the exchange reopens if (request.OrderType == OrderType.MarketOnClose && !security.Exchange.ExchangeOpen) { return(OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen, request.OrderType + " order and exchange not open.")); } if (price == 0) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityPriceZero, request.Symbol.Permtick + ": asset price is $0. If using custom data make sure you've set the 'Value' property.")); } if (security.Type == SecurityType.Forex) { // for forex pairs we need to verify that the conversions to USD have values as well string baseCurrency, quoteCurrency; Forex.DecomposeCurrencyPair(security.Symbol.Value, out baseCurrency, out quoteCurrency); // verify they're in the portfolio Cash baseCash, quoteCash; if (!Portfolio.CashBook.TryGetValue(baseCurrency, out baseCash) || !Portfolio.CashBook.TryGetValue(quoteCurrency, out quoteCash)) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexBaseAndQuoteCurrenciesRequired, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " in the cashbook to trade.")); } // verify we have conversion rates for each leg of the pair back into the account currency if (baseCash.ConversionRate == 0m || quoteCash.ConversionRate == 0m) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexConversionRateZero, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " to have non-zero conversion rates. This can be caused by lack of data.")); } } //Make sure the security has some data: if (!security.HasData) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityHasNoData, "There is no data for this symbol yet, please check the security.HasData flag to ensure there is at least one data point.")); } //We've already processed too many orders: max 100 per day or the memory usage explodes if (Transactions.OrdersCount > _maxOrders) { Status = AlgorithmStatus.Stopped; return(OrderResponse.Error(request, OrderResponseErrorCode.ExceededMaximumOrders, string.Format("You have exceeded maximum number of orders ({0}), for unlimited orders upgrade your account.", _maxOrders))); } if (request.OrderType == OrderType.MarketOnClose) { // must be submitted with at least 10 minutes in trading day, add buffer allow order submission var latestSubmissionTime = (Time.Date + security.Exchange.MarketClose).AddMinutes(-10.75); if (Time > latestSubmissionTime) { // tell the user we require an 11 minute buffer, on minute data in live a user will receive the 3:49->3:50 bar at 3:50, // this is already too late to submit one of these orders, so make the user do it at the 3:48->3:49 bar so it's submitted // to the brokerage before 3:50. return(OrderResponse.Error(request, OrderResponseErrorCode.MarketOnCloseOrderTooLate, "MarketOnClose orders must be placed with at least a 11 minute buffer before market close.")); } } // passes all initial order checks return(OrderResponse.Success(request)); }
/// <summary> /// Submit a new order for quantity of symbol using type order. /// </summary> /// <param name="type">Buy/Sell Limit or Market Order Type.</param> /// <param name="symbol">Symbol of the MarketType Required.</param> /// <param name="quantity">Number of shares to request.</param> public int Order(string symbol, int quantity, OrderType type = OrderType.Market, bool asynchronous = false) { //Add an order to the transacion manager class: int orderId = -1; decimal price = 0; //Ordering 0 is useless. if (quantity == 0 || symbol == null || symbol == "") { return(-1); } //Internals use upper case symbols. symbol = symbol.ToUpper(); //If we're not tracking this symbol: throw error: if (!Securities.ContainsKey(symbol) && !_sentNoDataError) { _sentNoDataError = true; Error("You haven't requested " + symbol + " data. Add this with AddSecurity() in the Initialize() Method."); } //Set a temporary price for validating order for market orders: price = Securities[symbol].Price; if (price == 0) { Error("Asset price is $0. If using custom data make sure you've set the 'Value' property."); return(-1); } //Check the exchange is open before sending a market order. if (type == OrderType.Market && !Securities[symbol].Exchange.ExchangeOpen) { return(-3); } //We've already processed too many orders: max 100 per day or the memory usage explodes if (Orders.Count > (_endDate - _startDate).TotalDays * 100) { return(-5); } //Add the order and create a new order Id. orderId = Transactions.AddOrder(new Order(symbol, quantity, type, Time, price)); //Wait for the order event to process: //Enqueue means send to order queue but don't wait for response: if (!asynchronous && type == OrderType.Market) { //Wait for the market order to fill. //This is processed in a parallel thread. while (!Transactions.Orders.ContainsKey(orderId) || (Transactions.Orders[orderId].Status != OrderStatus.Filled && Transactions.Orders[orderId].Status != OrderStatus.Invalid && Transactions.Orders[orderId].Status != OrderStatus.Canceled) || _processingOrder) { Thread.Sleep(1); } } return(orderId); }
public override void OnData(Slice data) { // verify expectations if (SubscriptionManager.Subscriptions.Count(x => x.Symbol == OptionChainSymbol) != (_expectUniverseSubscription ? 1 : 0)) { Log($"SubscriptionManager.Subscriptions: {string.Join(" -- ", SubscriptionManager.Subscriptions)}"); throw new Exception($"Unexpected {OptionChainSymbol} subscription presence"); } if (!data.ContainsKey(Underlying)) { // TODO : In fact, we're unable to properly detect whether or not we auto-added or it was manually added // this is because when we auto-add the underlying we don't mark it as an internal security like we do with other auto adds // so there's currently no good way to remove the underlying equity without invoking RemoveSecurity(underlying) manually // from the algorithm, otherwise we may remove it incorrectly. Now, we could track MORE state, but it would likely be a duplication // of the internal flag's purpose, so kicking this issue for now with a big fat note here about it :) to be considerd for any future // refactorings of how we manage subscription/security data and track various aspects about the security (thinking a flags enum with // things like manually added, auto added, internal, and any other boolean state we need to track against a single security) throw new Exception("The underlying equity data should NEVER be removed in this algorithm because it was manually added"); } if (_expectedSecurities.AreDifferent(LinqExtensions.ToHashSet(Securities.Keys))) { var expected = string.Join(Environment.NewLine, _expectedSecurities.OrderBy(s => s.ToString())); var actual = string.Join(Environment.NewLine, Securities.Keys.OrderBy(s => s.ToString())); throw new Exception($"{Time}:: Detected differences in expected and actual securities{Environment.NewLine}Expected:{Environment.NewLine}{expected}{Environment.NewLine}Actual:{Environment.NewLine}{actual}"); } if (_expectedUniverses.AreDifferent(LinqExtensions.ToHashSet(UniverseManager.Keys))) { var expected = string.Join(Environment.NewLine, _expectedUniverses.OrderBy(s => s.ToString())); var actual = string.Join(Environment.NewLine, UniverseManager.Keys.OrderBy(s => s.ToString())); throw new Exception($"{Time}:: Detected differences in expected and actual universes{Environment.NewLine}Expected:{Environment.NewLine}{expected}{Environment.NewLine}Actual:{Environment.NewLine}{actual}"); } if (_expectedData.AreDifferent(LinqExtensions.ToHashSet(data.Keys))) { var expected = string.Join(Environment.NewLine, _expectedData.OrderBy(s => s.ToString())); var actual = string.Join(Environment.NewLine, data.Keys.OrderBy(s => s.ToString())); throw new Exception($"{Time}:: Detected differences in expected and actual slice data keys{Environment.NewLine}Expected:{Environment.NewLine}{expected}{Environment.NewLine}Actual:{Environment.NewLine}{actual}"); } // 10AM add GOOG option chain if (Time.TimeOfDay.Hours == 10 && Time.TimeOfDay.Minutes == 0) { if (Securities.ContainsKey(OptionChainSymbol)) { throw new Exception("The option chain security should not have been added yet"); } var googOptionChain = AddOption(UnderlyingTicker); googOptionChain.SetFilter(u => { // find first put above market price return(u.IncludeWeeklys() .Strikes(+1, +1) .Expiration(TimeSpan.Zero, TimeSpan.FromDays(1)) .Contracts(c => c.Where(s => s.ID.OptionRight == OptionRight.Put))); }); _expectedSecurities.Add(OptionChainSymbol); _expectedUniverses.Add(OptionChainSymbol); _expectUniverseSubscription = true; } // 11:30AM remove GOOG option chain if (Time.TimeOfDay.Hours == 11 && Time.TimeOfDay.Minutes == 30) { RemoveSecurity(OptionChainSymbol); // remove contracts from expected data _expectedData.RemoveWhere(s => _expectedContracts.Contains(s)); // remove option chain universe from expected universes _expectedUniverses.Remove(OptionChainSymbol); // OptionChainSymbol universe subscription should not be present _expectUniverseSubscription = false; } }
/// <summary> /// Gets <see cref="OptionHistory"/> object for a given symbol, date and resolution /// </summary> /// <param name="symbol">The symbol to retrieve historical option data for</param> /// <param name="start">The history request start time</param> /// <param name="end">The history request end time. Defaults to 1 day if null</param> /// <param name="resolution">The resolution to request</param> /// <returns>A <see cref="OptionHistory"/> object that contains historical option data.</returns> public OptionHistory GetOptionHistory(Symbol symbol, DateTime start, DateTime?end = null, Resolution?resolution = null) { if (!end.HasValue || end.Value == start) { end = start.AddDays(1); } // Load a canonical option Symbol if the user provides us with an underlying Symbol if (!symbol.SecurityType.IsOption()) { var option = AddOption(symbol, resolution, symbol.ID.Market); // Allow 20 strikes from the money for futures. No expiry filter is applied // so that any future contract provided will have data returned. if (symbol.SecurityType == SecurityType.Future && symbol.IsCanonical()) { throw new ArgumentException("The Future Symbol provided is a canonical Symbol (i.e. a Symbol representing all Futures), which is not supported at this time. " + "If you are using the Symbol accessible from `AddFuture(...)`, use the Symbol from `AddFutureContract(...)` instead. " + "You can use `qb.FutureOptionChainProvider(canonicalFuture, datetime)` to get a list of futures contracts for a given date, and add them to your algorithm with `AddFutureContract(symbol, Resolution)`."); } if (symbol.SecurityType == SecurityType.Future && !symbol.IsCanonical()) { option.SetFilter(universe => universe.Strikes(-10, +10)); } symbol = option.Symbol; } IEnumerable <Symbol> symbols; if (symbol.IsCanonical()) { // canonical symbol, lets find the contracts var option = Securities[symbol] as Option; var resolutionToUseForUnderlying = resolution ?? SubscriptionManager.SubscriptionDataConfigService .GetSubscriptionDataConfigs(symbol) .GetHighestResolution(); if (!Securities.ContainsKey(symbol.Underlying)) { if (symbol.Underlying.SecurityType == SecurityType.Equity) { // only add underlying if not present AddEquity(symbol.Underlying.Value, resolutionToUseForUnderlying); } if (symbol.Underlying.SecurityType == SecurityType.Future && symbol.Underlying.IsCanonical()) { AddFuture(symbol.Underlying.ID.Symbol, resolutionToUseForUnderlying); } else if (symbol.Underlying.SecurityType == SecurityType.Future) { AddFutureContract(symbol.Underlying, resolutionToUseForUnderlying); } } var allSymbols = new List <Symbol>(); for (var date = start; date < end; date = date.AddDays(1)) { if (option.Exchange.DateIsOpen(date)) { allSymbols.AddRange(OptionChainProvider.GetOptionContractList(symbol.Underlying, date)); } } var optionFilterUniverse = new OptionFilterUniverse(); var distinctSymbols = allSymbols.Distinct(); symbols = base.History(symbol.Underlying, start, end.Value, resolution) .SelectMany(x => { // the option chain symbols wont change so we can set 'exchangeDateChange' to false always optionFilterUniverse.Refresh(distinctSymbols, x, exchangeDateChange: false); return(option.ContractFilter.Filter(optionFilterUniverse)); }) .Distinct().Concat(new[] { symbol.Underlying }); } else { // the symbol is a contract symbols = new List <Symbol> { symbol }; } return(new OptionHistory(History(symbols, start, end.Value, resolution))); }
/// <summary> /// Determines whether or not the specified symbol is currently a member of this universe /// </summary> /// <param name="symbol">The symbol whose membership is to be checked</param> /// <returns>True if the specified symbol is part of this universe, false otherwise</returns> public bool ContainsMember(Symbol symbol) { return(Securities.ContainsKey(symbol)); }
private IEnumerable <OrderTicket> GenerateOrders(OptionStrategy strategy, int strategyQuantity) { var orders = new List <OrderTicket>(); // setting up the tag text for all orders of one strategy var strategyTag = strategy.Name + " (" + strategyQuantity.ToString() + ")"; // walking through all option legs and issuing orders if (strategy.OptionLegs != null) { foreach (var optionLeg in strategy.OptionLegs) { var optionSeq = Securities.Where(kv => kv.Key.Underlying == strategy.Underlying && kv.Key.ID.OptionRight == optionLeg.Right && kv.Key.ID.Date == optionLeg.Expiration && kv.Key.ID.StrikePrice == optionLeg.Strike); if (optionSeq.Count() != 1) { var error = string.Format("Couldn't find the option contract in algorithm securities list. Underlying: {0}, option {1}, strike {2}, expiration: {3}", strategy.Underlying.ToString(), optionLeg.Right.ToString(), optionLeg.Strike.ToString(), optionLeg.Expiration.ToString()); throw new InvalidOperationException(error); } var option = optionSeq.First().Key; switch (optionLeg.OrderType) { case OrderType.Market: var marketOrder = MarketOrder(option, optionLeg.Quantity * strategyQuantity, tag: strategyTag); orders.Add(marketOrder); break; case OrderType.Limit: var limitOrder = LimitOrder(option, optionLeg.Quantity * strategyQuantity, optionLeg.OrderPrice, tag: strategyTag); orders.Add(limitOrder); break; default: throw new InvalidOperationException("Order type is not supported in option strategy: " + optionLeg.OrderType.ToString()); } } } // walking through all underlying legs and issuing orders if (strategy.UnderlyingLegs != null) { foreach (var underlyingLeg in strategy.UnderlyingLegs) { if (!Securities.ContainsKey(strategy.Underlying)) { var error = string.Format("Couldn't find the option contract underlying in algorithm securities list. Underlying: {0}", strategy.Underlying.ToString()); throw new InvalidOperationException(error); } switch (underlyingLeg.OrderType) { case OrderType.Market: var marketOrder = MarketOrder(strategy.Underlying, underlyingLeg.Quantity * strategyQuantity, tag: strategyTag); orders.Add(marketOrder); break; case OrderType.Limit: var limitOrder = LimitOrder(strategy.Underlying, underlyingLeg.Quantity * strategyQuantity, underlyingLeg.OrderPrice, tag: strategyTag); orders.Add(limitOrder); break; default: throw new InvalidOperationException("Order type is not supported in option strategy: " + underlyingLeg.OrderType.ToString()); } } } return(orders); }
/// <summary> /// Perform preorder checks to ensure we have sufficient capital, /// the market is open, and we haven't exceeded maximum realistic orders per day. /// </summary> /// <returns>OrderResponse. If no error, order request is submitted.</returns> private OrderResponse PreOrderChecksImpl(SubmitOrderRequest request) { //Ordering 0 is useless. if (request.Quantity == 0 || request.Symbol == null || request.Symbol == QuantConnect.Symbol.Empty) { return(OrderResponse.ZeroQuantity(request)); } //If we're not tracking this symbol: throw error: if (!Securities.ContainsKey(request.Symbol) && !_sentNoDataError) { _sentNoDataError = true; return(OrderResponse.Error(request, OrderResponseErrorCode.MissingSecurity, "You haven't requested " + request.Symbol.ToString() + " data. Add this with AddSecurity() in the Initialize() Method.")); } //Set a temporary price for validating order for market orders: var security = Securities[request.Symbol]; var price = security.Price; //Check the exchange is open before sending a market on close orders if (request.OrderType == OrderType.MarketOnClose && !security.Exchange.ExchangeOpen) { return(OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen, request.OrderType + " order and exchange not open.")); } if (price == 0) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityPriceZero, request.Symbol.ToString() + ": asset price is $0. If using custom data make sure you've set the 'Value' property.")); } // check quote currency existence/conversion rate on all orders Cash quoteCash; var quoteCurrency = security.QuoteCurrency.Symbol; if (!Portfolio.CashBook.TryGetValue(quoteCurrency, out quoteCash)) { return(OrderResponse.Error(request, OrderResponseErrorCode.QuoteCurrencyRequired, request.Symbol.Value + ": requires " + quoteCurrency + " in the cashbook to trade.")); } if (security.QuoteCurrency.ConversionRate == 0m) { return(OrderResponse.Error(request, OrderResponseErrorCode.ConversionRateZero, request.Symbol.Value + ": requires " + quoteCurrency + " to have a non-zero conversion rate. This can be caused by lack of data.")); } // need to also check base currency existence/conversion rate on forex orders if (security.Type == SecurityType.Forex) { Cash baseCash; var baseCurrency = ((Forex)security).BaseCurrencySymbol; if (!Portfolio.CashBook.TryGetValue(baseCurrency, out baseCash)) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexBaseAndQuoteCurrenciesRequired, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " in the cashbook to trade.")); } if (baseCash.ConversionRate == 0m) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexConversionRateZero, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " to have non-zero conversion rates. This can be caused by lack of data.")); } } //Make sure the security has some data: if (!security.HasData) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityHasNoData, "There is no data for this symbol yet, please check the security.HasData flag to ensure there is at least one data point.")); } //We've already processed too many orders: max 100 per day or the memory usage explodes if (Transactions.OrdersCount > _maxOrders) { Status = AlgorithmStatus.Stopped; return(OrderResponse.Error(request, OrderResponseErrorCode.ExceededMaximumOrders, string.Format("You have exceeded maximum number of orders ({0}), for unlimited orders upgrade your account.", _maxOrders))); } if (request.OrderType == OrderType.MarketOnClose) { var nextMarketClose = security.Exchange.Hours.GetNextMarketClose(security.LocalTime, false); // must be submitted with at least 10 minutes in trading day, add buffer allow order submission var latestSubmissionTime = nextMarketClose.AddMinutes(-15.50); if (!security.Exchange.ExchangeOpen || Time > latestSubmissionTime) { // tell the user we require a 16 minute buffer, on minute data in live a user will receive the 3:44->3:45 bar at 3:45, // this is already too late to submit one of these orders, so make the user do it at the 3:43->3:44 bar so it's submitted // to the brokerage before 3:45. return(OrderResponse.Error(request, OrderResponseErrorCode.MarketOnCloseOrderTooLate, "MarketOnClose orders must be placed with at least a 16 minute buffer before market close.")); } } // passes all initial order checks return(OrderResponse.Success(request)); }