예제 #1
0
        /// <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));
        }
예제 #2
0
        /// <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;
            }
        }
예제 #4
0
        /// <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)));
        }
예제 #5
0
 /// <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));
 }
예제 #6
0
        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);
        }
예제 #7
0
        /// <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));
        }