/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using QuantConnect.Orders; using QuantConnect.Securities; using QuantConnect.Securities.Forex; namespace QuantConnect.Algorithm { public partial class QCAlgorithm { private int _maxOrders = 10000; /// /// Transaction Manager - Process transaction fills and order management. /// public SecurityTransactionManager Transactions { get; set; } /// /// Buy Stock (Alias of Order) /// /// string Symbol of the asset to trade /// int Quantity of the asset to trade /// public OrderTicket Buy(Symbol symbol, int quantity) { return Order(symbol, Math.Abs(quantity)); } /// /// Buy Stock (Alias of Order) /// /// string Symbol of the asset to trade /// double Quantity of the asset to trade /// public OrderTicket Buy(Symbol symbol, double quantity) { return Order(symbol, Math.Abs(quantity)); } /// /// Buy Stock (Alias of Order) /// /// string Symbol of the asset to trade /// decimal Quantity of the asset to trade /// public OrderTicket Buy(Symbol symbol, decimal quantity) { return Order(symbol, Math.Abs(quantity)); } /// /// Buy Stock (Alias of Order) /// /// string Symbol of the asset to trade /// float Quantity of the asset to trade /// public OrderTicket Buy(Symbol symbol, float quantity) { return Order(symbol, Math.Abs(quantity)); } /// /// Sell stock (alias of Order) /// /// string Symbol of the asset to trade /// int Quantity of the asset to trade /// public OrderTicket Sell(Symbol symbol, int quantity) { return Order(symbol, Math.Abs(quantity) * -1); } /// /// Sell stock (alias of Order) /// /// String symbol to sell /// Quantity to order /// int Order Id. public OrderTicket Sell(Symbol symbol, double quantity) { return Order(symbol, Math.Abs(quantity) * -1); } /// /// Sell stock (alias of Order) /// /// String symbol /// Quantity to sell /// int order id /// public OrderTicket Sell(Symbol symbol, float quantity) { return Order(symbol, Math.Abs(quantity) * -1); } /// /// Sell stock (alias of Order) /// /// String symbol to sell /// Quantity to sell /// Int Order Id. public OrderTicket Sell(Symbol symbol, decimal quantity) { return Order(symbol, Math.Abs(quantity) * -1); } /// /// Issue an order/trade for asset: Alias wrapper for Order(string, int); /// /// public OrderTicket Order(Symbol symbol, double quantity) { return Order(symbol, (int) quantity); } /// /// Issue an order/trade for asset: Alias wrapper for Order(string, int); /// /// /// public OrderTicket Order(Symbol symbol, decimal quantity) { return Order(symbol, (int) quantity); } /// /// Wrapper for market order method: submit a new order for quantity of symbol using type order. /// /// Symbol of the MarketType Required. /// Number of shares to request. /// Send the order asynchrously (false). Otherwise we'll block until it fills /// Place a custom order property or tag (e.g. indicator data). /// public OrderTicket Order(Symbol symbol, int quantity, bool asynchronous = false, string tag = "") { return MarketOrder(symbol, quantity, asynchronous, tag); } /// /// Market order implementation: Send a market order and wait for it to be filled. /// /// Symbol of the MarketType Required. /// Number of shares to request. /// Send the order asynchrously (false). Otherwise we'll block until it fills /// Place a custom order property or tag (e.g. indicator data). /// int Order id public OrderTicket MarketOrder(Symbol symbol, int quantity, bool asynchronous = false, string tag = "") { var security = Securities[symbol]; // check the exchange is open before sending a market order, if it's not open // then convert it into a market on open order if (!security.Exchange.ExchangeOpen) { var mooTicket = MarketOnOpenOrder(symbol, quantity, tag); if (mooTicket.SubmitRequest.Response.IsSuccess && security.SubscriptionDataConfig.Resolution != Resolution.Daily) { Debug("Converted OrderID: " + mooTicket.OrderId + " into a MarketOnOpen order."); } return mooTicket; } var request = CreateSubmitOrderRequest(OrderType.Market, security, quantity, tag); //Initalize the Market order parameters: var preOrderCheckResponse = PreOrderChecks(request); if (preOrderCheckResponse.IsError) { return OrderTicket.InvalidSubmitRequest(Transactions, request, preOrderCheckResponse); } //Add the order and create a new order Id. var ticket = Transactions.AddOrder(request); //Wait for the order event to process, only if the exchange is open if (!asynchronous) { Transactions.WaitForOrder(ticket.OrderId); } return ticket; } /// /// Market on open order implementation: Send a market order when the exchange opens /// /// The symbol to be ordered /// The number of shares to required /// Place a custom order property or tag (e.g. indicator data). /// The order ID public OrderTicket MarketOnOpenOrder(Symbol symbol, int quantity, string tag = "") { var security = Securities[symbol]; var request = CreateSubmitOrderRequest(OrderType.MarketOnOpen, security, quantity, tag); var response = PreOrderChecks(request); if (response.IsError) { return OrderTicket.InvalidSubmitRequest(Transactions, request, response); } return Transactions.AddOrder(request); } /// /// Market on close order implementation: Send a market order when the exchange closes /// /// The symbol to be ordered /// The number of shares to required /// Place a custom order property or tag (e.g. indicator data). /// The order ID public OrderTicket MarketOnCloseOrder(Symbol symbol, int quantity, string tag = "") { var security = Securities[symbol]; var request = CreateSubmitOrderRequest(OrderType.MarketOnClose, security, quantity, tag); var response = PreOrderChecks(request); if (response.IsError) { return OrderTicket.InvalidSubmitRequest(Transactions, request, response); } return Transactions.AddOrder(request); } /// /// Send a limit order to the transaction handler: /// /// String symbol for the asset /// Quantity of shares for limit order /// Limit price to fill this order /// String tag for the order (optional) /// Order id public OrderTicket LimitOrder(Symbol symbol, int quantity, decimal limitPrice, string tag = "") { var security = Securities[symbol]; var request = CreateSubmitOrderRequest(OrderType.Limit, security, quantity, tag, limitPrice: limitPrice); var response = PreOrderChecks(request); if (response.IsError) { return OrderTicket.InvalidSubmitRequest(Transactions, request, response); } return Transactions.AddOrder(request); } /// /// Create a stop market order and return the newly created order id; or negative if the order is invalid /// /// String symbol for the asset we're trading /// Quantity to be traded /// Price to fill the stop order /// Optional string data tag for the order /// Int orderId for the new order. public OrderTicket StopMarketOrder(Symbol symbol, int quantity, decimal stopPrice, string tag = "") { var security = Securities[symbol]; var request = CreateSubmitOrderRequest(OrderType.StopMarket, security, quantity, tag, stopPrice: stopPrice); var response = PreOrderChecks(request); if (response.IsError) { return OrderTicket.InvalidSubmitRequest(Transactions, request, response); } return Transactions.AddOrder(request); } /// /// Send a stop limit order to the transaction handler: /// /// String symbol for the asset /// Quantity of shares for limit order /// Stop price for this order /// Limit price to fill this order /// String tag for the order (optional) /// Order id public OrderTicket StopLimitOrder(Symbol symbol, int quantity, decimal stopPrice, decimal limitPrice, string tag = "") { var security = Securities[symbol]; var request = CreateSubmitOrderRequest(OrderType.StopLimit, security, quantity, tag, stopPrice: stopPrice, limitPrice: limitPrice); var response = PreOrderChecks(request); if (response.IsError) { return OrderTicket.InvalidSubmitRequest(Transactions, request, response); } //Add the order and create a new order Id. return Transactions.AddOrder(request); } /// /// Perform preorder checks to ensure we have sufficient capital, /// the market is open, and we haven't exceeded maximum realistic orders per day. /// /// OrderResponse. If no error, order request is submitted. private OrderResponse PreOrderChecks(SubmitOrderRequest request) { var response = PreOrderChecksImpl(request); if (response.IsError) { Error(response.ErrorMessage); } return response; } /// /// Perform preorder checks to ensure we have sufficient capital, /// the market is open, and we haven't exceeded maximum realistic orders per day. /// /// OrderResponse. If no error, order request is submitted. 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) { _quit = true; 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); } /// /// Liquidate all holdings. Called at the end of day for tick-strategies. /// /// Symbols we wish to liquidate /// Array of order ids for liquidated symbols /// public List Liquidate(Symbol symbolToLiquidate = null) { var orderIdList = new List(); symbolToLiquidate = symbolToLiquidate ?? Symbol.Empty; foreach (var symbol in Securities.Keys) { //Send market order to liquidate if 1, we have stock, 2, symbol matches. if (!Portfolio[symbol].HoldStock || (symbol != symbolToLiquidate && symbolToLiquidate != Symbol.Empty)) continue; var quantity = 0; if (Portfolio[symbol].IsLong) { quantity = -Portfolio[symbol].Quantity; } else { quantity = Math.Abs(Portfolio[symbol].Quantity); } //Liquidate at market price. var ticket = Order(symbol, quantity); if (ticket.Status == OrderStatus.Filled) { orderIdList.Add(ticket.OrderId); } } return orderIdList; } /// /// Maximum number of orders for the algorithm /// /// public void SetMaximumOrders(int max) { if (!_locked) { _maxOrders = max; } } /// /// Alias for SetHoldings to avoid the M-decimal errors. /// /// string symbol we wish to hold /// double percentage of holdings desired /// liquidate existing holdings if neccessary to hold this stock /// public void SetHoldings(Symbol symbol, double percentage, bool liquidateExistingHoldings = false) { SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings); } /// /// Alias for SetHoldings to avoid the M-decimal errors. /// /// string symbol we wish to hold /// float percentage of holdings desired /// bool liquidate existing holdings if neccessary to hold this stock /// Tag the order with a short string. /// public void SetHoldings(Symbol symbol, float percentage, bool liquidateExistingHoldings = false, string tag = "") { SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings); } /// /// Alias for SetHoldings to avoid the M-decimal errors. /// /// string symbol we wish to hold /// float percentage of holdings desired /// bool liquidate existing holdings if neccessary to hold this stock /// Tag the order with a short string. /// public void SetHoldings(Symbol symbol, int percentage, bool liquidateExistingHoldings = false, string tag = "") { SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings); } /// /// Automatically place an order which will set the holdings to between 100% or -100% of *PORTFOLIO VALUE*. /// E.g. SetHoldings("AAPL", 0.1); SetHoldings("IBM", -0.2); -> Sets portfolio as long 10% APPL and short 20% IBM /// E.g. SetHoldings("AAPL", 2); -> Sets apple to 2x leveraged with all our cash. /// /// Symbol indexer /// decimal fraction of portfolio to set stock /// bool flag to clean all existing holdings before setting new faction. /// Tag the order with a short string. /// public void SetHoldings(Symbol symbol, decimal percentage, bool liquidateExistingHoldings = false, string tag = "") { //Initialize Requirements: Security security; if (!Securities.TryGetValue(symbol, out security)) { Error(symbol.Permtick + " not found in portfolio. Request this data when initializing the algorithm."); return; } //If they triggered a liquidate if (liquidateExistingHoldings) { foreach (var holdingSymbol in Portfolio.Keys) { if (holdingSymbol != symbol && security.Holdings.AbsoluteQuantity > 0) { //Go through all existing holdings [synchronously], market order the inverse quantity: Order(holdingSymbol, -security.Holdings.Quantity); } } } //Only place trade if we've got > 1 share to order. var quantity = CalculateOrderQuantity(symbol, percentage); if (Math.Abs(quantity) > 0) { MarketOrder(symbol, quantity, false, tag); } } /// /// Calculate the order quantity to achieve target-percent holdings. /// /// Security object we're asking for /// Target percentag holdings /// Order quantity to achieve this percentage public int CalculateOrderQuantity(Symbol symbol, double target) { return CalculateOrderQuantity(symbol, (decimal)target); } /// /// Calculate the order quantity to achieve target-percent holdings. /// /// Security object we're asking for /// Target percentag holdings, this is an unlevered value, so /// if you have 2x leverage and request 100% holdings, it will utilize half of the /// available margin /// Order quantity to achieve this percentage public int CalculateOrderQuantity(Symbol symbol, decimal target) { var security = Securities[symbol]; var price = security.Price; // can't order it if we don't have data if (price == 0) return 0; // this is the value in dollars that we want our holdings to have var targetPortfolioValue = target*Portfolio.TotalPortfolioValue; var quantity = security.Holdings.Quantity; var currentHoldingsValue = price*quantity; // remove directionality, we'll work in the land of absolutes var targetOrderValue = Math.Abs(targetPortfolioValue - currentHoldingsValue); var direction = targetPortfolioValue > currentHoldingsValue ? OrderDirection.Buy : OrderDirection.Sell; // define lower and upper thresholds for the iteration var lowerThreshold = targetOrderValue - price/2; var upperThreshold = targetOrderValue + price/2; // continue iterating while we're still not within the specified thresholds var iterations = 0; var orderQuantity = 0; decimal orderValue = 0; while ((orderValue < lowerThreshold || orderValue > upperThreshold) && iterations < 10) { // find delta from where we are to where we want to be var delta = targetOrderValue - orderValue; // use delta value to compute a change in quantity required var deltaQuantity = (int)(delta / price); orderQuantity += deltaQuantity; // recompute order fees var order = new MarketOrder(security.Symbol, orderQuantity, UtcTime, type: security.Type); var fee = security.TransactionModel.GetOrderFee(security, order); orderValue = Math.Abs(order.GetValue(price)) + fee; // we need to add the fee in as well, even though it's not value, it's still a cost for the transaction // and we need to account for it to be sure we can make the trade produced by this method, imagine // set holdings 100% with 1x leverage, but a high fee structure, it quickly becomes necessary to include // otherwise the result of this function will be inactionable. iterations++; } // add directionality back in return (direction == OrderDirection.Sell ? -1 : 1) * orderQuantity; } /// /// Obsolete implementation of Order method accepting a OrderType. This was deprecated since it /// was impossible to generate other orders via this method. Any calls to this method will always default to a Market Order. /// /// Symbol we want to purchase /// Quantity to buy, + is long, - short. /// Order Type /// Don't wait for the response, just submit order and move on. /// Custom data for this order /// Integer Order ID. [Obsolete("This Order method has been made obsolete, use Order(string, int, bool, string) method instead. Calls to the obsolete method will only generate market orders.")] public OrderTicket Order(Symbol symbol, int quantity, OrderType type, bool asynchronous = false, string tag = "") { return Order(symbol, quantity, asynchronous, tag); } /// /// Obsolete method for placing orders. /// /// /// /// [Obsolete("This Order method has been made obsolete, use the specialized Order helper methods instead. Calls to the obsolete method will only generate market orders.")] public OrderTicket Order(Symbol symbol, decimal quantity, OrderType type) { return Order(symbol, (int)quantity); } /// /// Obsolete method for placing orders. /// /// /// /// [Obsolete("This Order method has been made obsolete, use the specialized Order helper methods instead. Calls to the obsolete method will only generate market orders.")] public OrderTicket Order(Symbol symbol, int quantity, OrderType type) { return Order(symbol, quantity); } private SubmitOrderRequest CreateSubmitOrderRequest(OrderType orderType, Security security, int quantity, string tag, decimal stopPrice = 0m, decimal limitPrice = 0m) { return new SubmitOrderRequest(orderType, security.Type, security.Symbol, quantity, stopPrice, limitPrice, UtcTime, tag); } } }