/// <summary> /// Calculate the order quantity to achieve target-percent holdings. /// </summary> /// <param name="symbol">Security object we're asking for</param> /// <param name="target">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</param> /// <returns>Order quantity to achieve this percentage</returns> 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; }
/// <summary> /// Calculate the order quantity to achieve target-percent holdings. /// </summary> /// <param name="symbol">Security object we're asking for</param> /// <param name="target">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</param> /// <returns>Order quantity to achieve this percentage</returns> 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; // if targeting zero, simply return the negative of the quantity if (target == 0) return -security.Holdings.Quantity; // 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; // determine the unit price in terms of the account currency var unitPrice = new MarketOrder(symbol, 1, UtcTime).GetValue(security); // calculate the total margin available var marginRemaining = Portfolio.GetMarginRemaining(symbol, direction); if (marginRemaining <= 0) return 0; // continue iterating while we do not have enough margin for the order decimal marginRequired; decimal orderValue; decimal orderFees; var feeToPriceRatio = 0; // compute the initial order quantity var orderQuantity = (int)(targetOrderValue / unitPrice); var iterations = 0; do { // decrease the order quantity if (iterations > 0) { // if fees are high relative to price, we reduce the order quantity faster if (feeToPriceRatio > 0) orderQuantity -= feeToPriceRatio; else orderQuantity--; } // generate the order var order = new MarketOrder(security.Symbol, orderQuantity, UtcTime); orderValue = order.GetValue(security); orderFees = security.FeeModel.GetOrderFee(security, order); feeToPriceRatio = (int)(orderFees / unitPrice); // calculate the margin required for the order marginRequired = security.MarginModel.GetInitialMarginRequiredForOrder(security, order); iterations++; } while (orderQuantity > 0 && (marginRequired > marginRemaining || orderValue + orderFees > targetOrderValue)); // add directionality back in return (direction == OrderDirection.Sell ? -1 : 1) * orderQuantity; }