Пример #1
0
        /// <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;

            // determine the unit price in terms of the account currency
            var unitPrice = new MarketOrder(symbol, 1, UtcTime).GetValue(security);

            // define lower and upper thresholds for the iteration
            var lowerThreshold = targetOrderValue - unitPrice / 2;
            var upperThreshold = targetOrderValue + unitPrice / 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 / unitPrice);

                orderQuantity += deltaQuantity;

                // recompute order fees
                var order = new MarketOrder(security.Symbol, orderQuantity, UtcTime);
                var fee   = security.FeeModel.GetOrderFee(security, order);

                orderValue = Math.Abs(order.GetValue(security)) + 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 currentHoldingsValue = security.Holdings.HoldingsValue;

            // 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);

            if (unitPrice == 0)
            {
                return(0);
            }

            // 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));

            //Rounding off Order Quantity to the nearest multiple of Lot Size
            if (orderQuantity % Convert.ToInt32(security.SymbolProperties.LotSize) != 0)
            {
                orderQuantity = orderQuantity - (orderQuantity % Convert.ToInt32(security.SymbolProperties.LotSize));
            }

            // add directionality back in
            return((direction == OrderDirection.Sell ? -1 : 1) * orderQuantity);
        }
Пример #3
0
        /// <summary>
        /// Get the maximum market order quantity to obtain a position with a given value in account currency
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio</param>
        /// <param name="security">The security to be traded</param>
        /// <param name="targetPortfolioValue">The value in account currency that we want our holding to have</param>
        /// <returns>Returns the maximum allowed market order quantity and if zero, also the reason</returns>
        public GetMaximumOrderQuantityForTargetValueResult GetMaximumOrderQuantityForTargetValue(SecurityPortfolioManager portfolio, Security security, decimal targetPortfolioValue)
        {
            // if targeting zero, simply return the negative of the quantity
            if (targetPortfolioValue == 0)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(-security.Holdings.Quantity, string.Empty, false));
            }

            var currentHoldingsValue = security.Holdings.HoldingsValue;

            // 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(security.Symbol, 1, DateTime.UtcNow).GetValue(security);

            if (unitPrice == 0)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The price of the {security.Symbol.Value} security is zero because it does not have any market data yet. When the security price is set this security will be ready for trading."));
            }

            // calculate the total margin available
            var marginRemaining = GetMarginRemaining(portfolio, security, direction);

            if (marginRemaining <= 0)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The portfolio does not have enough margin available."));
            }

            // continue iterating while we do not have enough margin for the order
            decimal marginRequired;
            decimal orderValue;
            decimal orderFees;
            var     feeToPriceRatio = 0m;

            // compute the initial order quantity
            var orderQuantity = targetOrderValue / unitPrice;

            // rounding off Order Quantity to the nearest multiple of Lot Size
            orderQuantity -= orderQuantity % security.SymbolProperties.LotSize;
            if (orderQuantity == 0)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The order quantity is less than the lot size of {security.SymbolProperties.LotSize} and has been rounded to zero.", false));
            }

            do
            {
                // reduce order quantity by feeToPriceRatio, since it is faster than by lot size
                // if it becomes nonpositive, return zero
                orderQuantity -= feeToPriceRatio;
                if (orderQuantity <= 0)
                {
                    return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The portfolio does not hold enough cash including the order fees."));
                }

                // generate the order
                var order = new MarketOrder(security.Symbol, orderQuantity, DateTime.UtcNow);
                orderValue = order.GetValue(security);
                orderFees  = security.FeeModel.GetOrderFee(security, order);

                // find an incremental delta value for the next iteration step
                feeToPriceRatio  = orderFees / unitPrice;
                feeToPriceRatio -= feeToPriceRatio % security.SymbolProperties.LotSize;
                if (feeToPriceRatio < security.SymbolProperties.LotSize)
                {
                    feeToPriceRatio = security.SymbolProperties.LotSize;
                }

                // calculate the margin required for the order
                marginRequired = GetInitialMarginRequiredForOrder(security, order);
            } while (marginRequired > marginRemaining || orderValue + orderFees > targetOrderValue);

            // add directionality back in
            return(new GetMaximumOrderQuantityForTargetValueResult((direction == OrderDirection.Sell ? -1 : 1) * orderQuantity));
        }
Пример #4
0
        /// <summary>
        /// Get the maximum market order quantity to obtain a position with a given value in account currency
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio</param>
        /// <param name="security">The security to be traded</param>
        /// <param name="targetPortfolioValue">The value in account currency that we want our holding to have</param>
        /// <returns>Returns the maximum allowed market order quantity</returns>
        public decimal GetMaximumOrderQuantityForTargetValue(SecurityPortfolioManager portfolio, Security security, decimal targetPortfolioValue)
        {
            var baseCurrency = security as IBaseCurrencySymbol;

            if (baseCurrency == null)
            {
                return(0);
            }

            // convert base currency cash to account currency
            var baseCurrencyPosition = portfolio.CashBook.ConvertToAccountCurrency(
                portfolio.CashBook[baseCurrency.BaseCurrencySymbol].Amount,
                baseCurrency.BaseCurrencySymbol);

            // convert quote currency cash to account currency
            var quoteCurrencyPosition = portfolio.CashBook.ConvertToAccountCurrency(
                portfolio.CashBook[security.QuoteCurrency.Symbol].Amount,
                security.QuoteCurrency.Symbol);

            // determine the unit price in terms of the account currency
            var unitPrice = new MarketOrder(security.Symbol, 1, DateTime.UtcNow).GetValue(security);

            if (unitPrice == 0)
            {
                return(0);
            }

            // remove directionality, we'll work in the land of absolutes
            var targetOrderValue = Math.Abs(targetPortfolioValue - baseCurrencyPosition);
            var direction        = targetPortfolioValue > baseCurrencyPosition ? OrderDirection.Buy : OrderDirection.Sell;

            // calculate the total cash available
            var cashRemaining = direction == OrderDirection.Buy ? quoteCurrencyPosition : baseCurrencyPosition;

            if (cashRemaining <= 0)
            {
                return(0);
            }

            // continue iterating while we do not have enough cash for the order
            decimal cashRequired;
            decimal orderValue;
            decimal orderFees;
            var     feeToPriceRatio = 0m;

            // compute the initial order quantity
            var orderQuantity = targetOrderValue / unitPrice;

            // rounding off Order Quantity to the nearest multiple of Lot Size
            orderQuantity -= orderQuantity % security.SymbolProperties.LotSize;

            do
            {
                // reduce order quantity by feeToPriceRatio, since it is faster than by lot size
                // if it becomes nonpositive, return zero
                orderQuantity -= feeToPriceRatio;
                if (orderQuantity <= 0)
                {
                    return(0);
                }

                // generate the order
                var order = new MarketOrder(security.Symbol, orderQuantity, DateTime.UtcNow);
                orderValue = order.GetValue(security);
                orderFees  = security.FeeModel.GetOrderFee(security, order);

                // find an incremental delta value for the next iteration step
                feeToPriceRatio  = orderFees / unitPrice;
                feeToPriceRatio -= feeToPriceRatio % security.SymbolProperties.LotSize;
                if (feeToPriceRatio < security.SymbolProperties.LotSize)
                {
                    feeToPriceRatio = security.SymbolProperties.LotSize;
                }

                // calculate the cash required for the order
                cashRequired = orderValue;
            } while (cashRequired > cashRemaining || orderValue + orderFees > targetOrderValue);

            // add directionality back in
            return((direction == OrderDirection.Sell ? -1 : 1) * orderQuantity);
        }
        public GetMaximumOrderQuantityResult GetMaximumOrderQuantityForTargetBuyingPower(
            GetMaximumOrderQuantityForTargetBuyingPowerParameters parameters
            )
        {
            EnsureSecurityExists(parameters.Security);
            var expected = SecurityModel.GetMaximumOrderQuantityForTargetBuyingPower(parameters);

            if (reentry)
            {
                return(expected);
            }

            reentry = true;
            var security      = parameters.Security;
            var positionGroup = Portfolio.Positions[new PositionGroupKey(PositionGroupModel, security)];
            var actual        = PositionGroupModel.GetMaximumLotsForTargetBuyingPower(
                new GetMaximumLotsForTargetBuyingPowerParameters(
                    parameters.Portfolio,
                    positionGroup,
                    parameters.TargetBuyingPower,
                    parameters.MinimumOrderMarginPortfolioPercentage,
                    parameters.SilenceNonErrorReasons
                    )
                );

            var lotSize = security.SymbolProperties.LotSize;

            Assert.AreEqual(expected.IsError, actual.IsError,
                            $"{PositionGroupModel.GetType().Name}:{nameof(GetMaximumOrderQuantityForTargetBuyingPower)}: " +
                            $"ExpectedQuantity: {expected.Quantity} ActualQuantity: {actual.NumberOfLots * lotSize} {Environment.NewLine}" +
                            $"ExpectedReason: {expected.Reason}{Environment.NewLine}" +
                            $"ActualReason: {actual.Reason}"
                            );

            // we're not comparing group quantities, which is the number of position lots, but rather the implied
            // position quantities resulting from having that many lots.
            var resizedPositionGroup = positionGroup.WithQuantity(actual.NumberOfLots);
            var position             = resizedPositionGroup.GetPosition(security.Symbol);

            var bpmOrder   = new MarketOrder(security.Symbol, expected.Quantity, parameters.Portfolio.Securities.UtcTime);
            var pgbpmOrder = new MarketOrder(security.Symbol, position.Quantity, parameters.Portfolio.Securities.UtcTime);

            var bpmOrderValue   = bpmOrder.GetValue(security);
            var pgbpmOrderValue = pgbpmOrder.GetValue(security);

            var bpmOrderFees   = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, bpmOrder)).Value.Amount;
            var pgbpmOrderFees = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, pgbpmOrder)).Value.Amount;

            var bpmMarginRequired   = bpmOrderValue + bpmOrderFees;
            var pgbpmMarginRequired = pgbpmOrderValue + pgbpmOrderFees;

            Assert.AreEqual(expected.Quantity, position.Quantity,
                            $"{PositionGroupModel.GetType().Name}:{nameof(GetMaximumOrderQuantityForTargetBuyingPower)}: " +
                            $"ExpectedReason: {expected.Reason}{Environment.NewLine}" +
                            $"ActualReason: {actual.Reason}"
                            );

            Assert.AreEqual(expected.Reason, actual.Reason,
                            $"{PositionGroupModel.GetType().Name}:{nameof(GetMaximumOrderQuantityForTargetBuyingPower)}: " +
                            $"ExpectedReason: {expected.Reason}{Environment.NewLine}" +
                            $"ActualReason: {actual.Reason}"
                            );

            reentry = false;
            return(expected);
        }
Пример #6
0
        /// <summary>
        /// Get the maximum market order quantity to obtain a position with a given value in account currency
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio</param>
        /// <param name="security">The security to be traded</param>
        /// <param name="targetPortfolioValue">The value in account currency that we want our holding to have</param>
        /// <returns>Returns the maximum allowed market order quantity and if zero, also the reason</returns>
        public GetMaximumOrderQuantityForTargetValueResult GetMaximumOrderQuantityForTargetValue(SecurityPortfolioManager portfolio, Security security, decimal targetPortfolioValue)
        {
            // no shorting allowed
            if (targetPortfolioValue < 0)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(0, "The cash model does not allow shorting."));
            }

            var baseCurrency = security as IBaseCurrencySymbol;

            if (baseCurrency == null)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(0, "The security type must be SecurityType.Crypto or SecurityType.Forex."));
            }

            // if target value is zero, return amount of base currency available to sell
            if (targetPortfolioValue == 0)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(-portfolio.CashBook[baseCurrency.BaseCurrencySymbol].Amount));
            }

            // convert base currency cash to account currency
            var baseCurrencyPosition = portfolio.CashBook.ConvertToAccountCurrency(
                portfolio.CashBook[baseCurrency.BaseCurrencySymbol].Amount,
                baseCurrency.BaseCurrencySymbol);

            // convert quote currency cash to account currency
            var quoteCurrencyPosition = portfolio.CashBook.ConvertToAccountCurrency(
                portfolio.CashBook[security.QuoteCurrency.Symbol].Amount,
                security.QuoteCurrency.Symbol);

            // determine the unit price in terms of the account currency
            var unitPrice = new MarketOrder(security.Symbol, 1, DateTime.UtcNow).GetValue(security);

            if (unitPrice == 0)
            {
                if (security.QuoteCurrency.ConversionRate == 0)
                {
                    return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The internal cash feed required for converting {security.QuoteCurrency.Symbol} to {CashBook.AccountCurrency} does not have any data yet (or market may be closed)."));
                }

                if (security.SymbolProperties.ContractMultiplier == 0)
                {
                    return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The contract multiplier for the {security.Symbol.Value} security is zero. The symbol properties database may be out of date."));
                }

                // security.Price == 0
                return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The price of the {security.Symbol.Value} security is zero because it does not have any market data yet. When the security price is set this security will be ready for trading."));
            }

            // remove directionality, we'll work in the land of absolutes
            var targetOrderValue = Math.Abs(targetPortfolioValue - baseCurrencyPosition);
            var direction        = targetPortfolioValue > baseCurrencyPosition ? OrderDirection.Buy : OrderDirection.Sell;

            // calculate the total cash available
            var cashRemaining = direction == OrderDirection.Buy ? quoteCurrencyPosition : baseCurrencyPosition;
            var currency      = direction == OrderDirection.Buy ? security.QuoteCurrency.Symbol : baseCurrency.BaseCurrencySymbol;

            if (cashRemaining <= 0)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The portfolio does not hold any {currency} for the order."));
            }

            // continue iterating while we do not have enough cash for the order
            decimal cashRequired;
            decimal orderValue;
            decimal orderFees;
            var     feeToPriceRatio = 0m;

            // compute the initial order quantity
            var orderQuantity = targetOrderValue / unitPrice;

            // rounding off Order Quantity to the nearest multiple of Lot Size
            orderQuantity -= orderQuantity % security.SymbolProperties.LotSize;
            if (orderQuantity == 0)
            {
                return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The order quantity is less than the lot size of {security.SymbolProperties.LotSize} and has been rounded to zero.", false));
            }

            do
            {
                // reduce order quantity by feeToPriceRatio, since it is faster than by lot size
                // if it becomes nonpositive, return zero
                orderQuantity -= feeToPriceRatio;
                if (orderQuantity <= 0)
                {
                    return(new GetMaximumOrderQuantityForTargetValueResult(0, $"The portfolio does not hold enough {currency} including the order fees."));
                }

                // generate the order
                var order = new MarketOrder(security.Symbol, orderQuantity, DateTime.UtcNow);
                orderValue = order.GetValue(security);
                orderFees  = security.FeeModel.GetOrderFee(security, order);

                // find an incremental delta value for the next iteration step
                feeToPriceRatio  = orderFees / unitPrice;
                feeToPriceRatio -= feeToPriceRatio % security.SymbolProperties.LotSize;
                if (feeToPriceRatio < security.SymbolProperties.LotSize)
                {
                    feeToPriceRatio = security.SymbolProperties.LotSize;
                }

                // calculate the cash required for the order
                cashRequired = orderValue;
            } while (cashRequired > cashRemaining || orderValue + orderFees > targetOrderValue);

            // add directionality back in
            return(new GetMaximumOrderQuantityForTargetValueResult((direction == OrderDirection.Sell ? -1 : 1) * orderQuantity));
        }