/// <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)
        {
            // no shorting allowed
            if (targetPortfolioValue < 0)
            {
                return(0);
            }

            var baseCurrency = security as IBaseCurrencySymbol;

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

            // if target value is zero, return amount of base currency available to sell
            if (targetPortfolioValue == 0)
            {
                return(-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)
            {
                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);
        }
Beispiel #2
0
        /// <summary>
        /// Get the maximum market order quantity to obtain a position with a given value in account currency. Will not take into account buying power.
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio</param>
        /// <param name="security">The security to be traded</param>
        /// <param name="target">Target percentage holdings</param>
        /// <returns>Returns the maximum allowed market order quantity and if zero, also the reason</returns>
        public GetMaximumOrderQuantityForTargetValueResult GetMaximumOrderQuantityForTargetValue(SecurityPortfolioManager portfolio, Security security, decimal target)
        {
            var targetPortfolioValue = target * portfolio.TotalPortfolioValue;

            // 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 orderValue = 0;
            decimal orderFees  = 0;
            // 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));
            }

            var loopCount = 0;
            // Just in case...
            var lastOrderQuantity = 0m;

            do
            {
                // Each loop will reduce the order quantity based on the difference between orderValue and targetOrderValue
                if (orderValue > targetOrderValue)
                {
                    var currentOrderValuePerUnit = orderValue / orderQuantity;
                    var amountOfOrdersToRemove   = (orderValue - targetOrderValue) / currentOrderValuePerUnit;
                    if (amountOfOrdersToRemove < security.SymbolProperties.LotSize)
                    {
                        // we will always substract at leat 1 LotSize
                        amountOfOrdersToRemove = security.SymbolProperties.LotSize;
                    }
                    orderQuantity -= amountOfOrdersToRemove;
                    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." +
                                                                           $"Target order value {targetOrderValue}. Order fees {orderFees}. Order quantity {orderQuantity}."));
                }

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

                // The TPV, take out the fees(unscaled) => yields available value for trading(less fees) -- then scale that by the target
                // finally remove currentHoldingsValue to get targetOrderValue
                targetOrderValue = Math.Abs(target * (portfolio.TotalPortfolioValue - orderFees) - currentHoldingsValue);

                // After the first loop we need to recalculate order quantity since now we have fees included
                if (loopCount == 0)
                {
                    // re compute the initial order quantity
                    orderQuantity  = targetOrderValue / unitPrice;
                    orderQuantity -= orderQuantity % security.SymbolProperties.LotSize;
                }
                else
                {
                    // Start safe check after first loop
                    if (lastOrderQuantity == orderQuantity)
                    {
                        throw new Exception($"GetMaximumOrderQuantityForTargetValue failed to converge to target order value {targetOrderValue}. " +
                                            $"Current order value is {orderValue}. Order quantity {orderQuantity}. Lot size is " +
                                            $"{security.SymbolProperties.LotSize}. Order fees {orderFees}. Security symbol {security.Symbol}");
                    }
                    lastOrderQuantity = orderQuantity;
                }

                orderValue = orderQuantity * unitPrice;
                loopCount++;
                // we always have to loop at least twice
            } while (loopCount < 2 || orderValue > targetOrderValue);

            // add directionality back in
            return(new GetMaximumOrderQuantityForTargetValueResult((direction == OrderDirection.Sell ? -1 : 1) * orderQuantity));
        }
Beispiel #3
0
 /// <summary>
 /// Gets the buying power available for a trade
 /// </summary>
 /// <param name="portfolio">The algorithm's portfolio</param>
 /// <param name="security">The security to be traded</param>
 /// <param name="direction">The direction of the trade</param>
 /// <returns>The buying power available for the trade</returns>
 public decimal GetBuyingPower(SecurityPortfolioManager portfolio, Security security, OrderDirection direction)
 {
     return(GetMarginRemaining(portfolio, security, direction));
 }
Beispiel #4
0
        /// <summary>
        /// Performs application of an OrderEvent to the portfolio
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio</param>
        /// <param name="security">The fill's security</param>
        /// <param name="fill">The order event fill object to be applied</param>
        public virtual void ProcessFill(SecurityPortfolioManager portfolio, Security security, OrderEvent fill)
        {
            var quoteCash = security.QuoteCurrency;

            //Get the required information from the vehicle this order will affect
            var isLong         = security.Holdings.IsLong;
            var isShort        = security.Holdings.IsShort;
            var closedPosition = false;
            //Make local decimals to avoid any rounding errors from int multiplication
            var quantityHoldings         = (decimal)security.Holdings.Quantity;
            var absoluteHoldingsQuantity = security.Holdings.AbsoluteQuantity;
            var averageHoldingsPrice     = security.Holdings.AveragePrice;

            try
            {
                // apply sales value to holdings in the account currency
                var saleValueInQuoteCurrency = fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity) * security.SymbolProperties.ContractMultiplier;
                var saleValue = saleValueInQuoteCurrency * quoteCash.ConversionRate;
                security.Holdings.AddNewSale(saleValue);

                // subtract transaction fees from the portfolio
                var feeInAccountCurrency = 0m;
                if (fill.OrderFee != OrderFee.Zero
                    // this is for user friendliness because some
                    // Security types default to use 0 USD ConstantFeeModel
                    && fill.OrderFee.Value.Amount != 0)
                {
                    var feeThisOrder = fill.OrderFee.Value;
                    feeInAccountCurrency = portfolio.CashBook.ConvertToAccountCurrency(feeThisOrder).Amount;
                    security.Holdings.AddNewFee(feeInAccountCurrency);

                    fill.OrderFee.ApplyToPortfolio(portfolio, fill);
                }

                // apply the funds using the current settlement model
                // we dont adjust funds for futures and CFDs: it is zero upfront payment derivative (margin applies though)
                // We do however apply funds for futures options, since they affect our cash balance the moment they are purchased/sold.
                if (security.Type != SecurityType.Future && security.Type != SecurityType.Cfd)
                {
                    security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, quoteCash.Symbol, -fill.FillQuantity * fill.FillPrice * security.SymbolProperties.ContractMultiplier);
                }
                if (security.Type == SecurityType.Forex || security.Type == SecurityType.Crypto)
                {
                    // model forex fills as currency swaps
                    var forex = (IBaseCurrencySymbol)security;
                    security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, forex.BaseCurrencySymbol, fill.FillQuantity);
                }

                // did we close or open a position further?
                closedPosition = isLong && fill.Direction == OrderDirection.Sell ||
                                 isShort && fill.Direction == OrderDirection.Buy;

                // calculate the last trade profit
                if (closedPosition)
                {
                    // profit = (closed sale value - cost)*conversion to account currency
                    // closed sale value = quantity closed * fill price       BUYs are deemed negative cash flow
                    // cost = quantity closed * average holdings price        SELLS are deemed positive cash flow
                    var absoluteQuantityClosed         = Math.Min(fill.AbsoluteFillQuantity, absoluteHoldingsQuantity);
                    var closedSaleValueInQuoteCurrency = Math.Sign(-fill.FillQuantity) * fill.FillPrice * absoluteQuantityClosed;
                    var closedCost      = Math.Sign(-fill.FillQuantity) * absoluteQuantityClosed * averageHoldingsPrice;
                    var lastTradeProfit = (closedSaleValueInQuoteCurrency - closedCost)
                                          * security.SymbolProperties.ContractMultiplier;
                    var lastTradeProfitInAccountCurrency = lastTradeProfit * security.QuoteCurrency.ConversionRate;

                    // Reflect account cash adjustment for futures/CFD position
                    if (security.Type == SecurityType.Future || security.Type == SecurityType.Cfd)
                    {
                        security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, quoteCash.Symbol, lastTradeProfit);
                    }

                    //Update Vehicle Profit Tracking:
                    security.Holdings.AddNewProfit(lastTradeProfitInAccountCurrency);
                    security.Holdings.SetLastTradeProfit(lastTradeProfitInAccountCurrency);
                    portfolio.AddTransactionRecord(security.LocalTime.ConvertToUtc(
                                                       security.Exchange.TimeZone),
                                                   lastTradeProfitInAccountCurrency - 2 * feeInAccountCurrency);
                }

                //UPDATE HOLDINGS QUANTITY, AVG PRICE:
                //Currently NO holdings. The order is ALL our holdings.
                if (quantityHoldings == 0)
                {
                    //First transaction just subtract order from cash and set our holdings:
                    averageHoldingsPrice = fill.FillPrice;
                    quantityHoldings     = fill.FillQuantity;
                }
                else if (isLong)
                {
                    //If we're currently LONG on the stock.
                    switch (fill.Direction)
                    {
                    case OrderDirection.Buy:
                        //Update the Holding Average Price: Total Value / Total Quantity:
                        averageHoldingsPrice = ((averageHoldingsPrice * quantityHoldings) + (fill.FillQuantity * fill.FillPrice)) / (quantityHoldings + fill.FillQuantity);
                        //Add the new quantity:
                        quantityHoldings += fill.FillQuantity;
                        break;

                    case OrderDirection.Sell:
                        quantityHoldings += fill.FillQuantity;     //+ a short = a subtraction
                        if (quantityHoldings < 0)
                        {
                            //If we've now passed through zero from selling stock: new avg price:
                            averageHoldingsPrice = fill.FillPrice;
                        }
                        else if (quantityHoldings == 0)
                        {
                            averageHoldingsPrice = 0;
                        }
                        break;
                    }
                }
                else if (isShort)
                {
                    //We're currently SHORTING the stock: What is the new position now?
                    switch (fill.Direction)
                    {
                    case OrderDirection.Buy:
                        //Buying when we're shorting moves to close position:
                        quantityHoldings += fill.FillQuantity;
                        if (quantityHoldings > 0)
                        {
                            //If we were short but passed through zero, new average price is what we paid. The short position was closed.
                            averageHoldingsPrice = fill.FillPrice;
                        }
                        else if (quantityHoldings == 0)
                        {
                            averageHoldingsPrice = 0;
                        }
                        break;

                    case OrderDirection.Sell:
                        //We are increasing a Short position:
                        //E.g.  -100 @ $5, adding -100 @ $10: Avg: $7.5
                        //      dAvg = (-500 + -1000) / -200 = 7.5
                        averageHoldingsPrice = ((averageHoldingsPrice * quantityHoldings) + (fill.FillQuantity * fill.FillPrice)) / (quantityHoldings + fill.FillQuantity);
                        quantityHoldings    += fill.FillQuantity;
                        break;
                    }
                }
            }
            catch (Exception err)
            {
                Log.Error(err);
            }

            //Set the results back to the vehicle.
            security.Holdings.SetHoldings(averageHoldingsPrice, quantityHoldings);
        }
Beispiel #5
0
        /// <summary>
        /// Check if there is sufficient buying power to execute this order.
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio</param>
        /// <param name="security">The security to be traded</param>
        /// <param name="order">The order to be checked</param>
        /// <returns>Returns buying power information for an order</returns>
        public HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(SecurityPortfolioManager portfolio, Security security, Order order)
        {
            // short circuit the div 0 case
            if (order.Quantity == 0)
            {
                return(new HasSufficientBuyingPowerForOrderResult(true));
            }

            var ticket = portfolio.Transactions.GetOrderTicket(order.Id);

            if (ticket == null)
            {
                var reason = $"Null order ticket for id: {order.Id}";
                Log.Error($"SecurityMarginModel.HasSufficientBuyingPowerForOrder(): {reason}");
                return(new HasSufficientBuyingPowerForOrderResult(false, reason));
            }

            if (order.Type == OrderType.OptionExercise)
            {
                // for option assignment and exercise orders we look into the requirements to process the underlying security transaction
                var option     = (Option.Option)security;
                var underlying = option.Underlying;

                if (option.IsAutoExercised(underlying.Close))
                {
                    var quantity = option.GetExerciseQuantity(order.Quantity);

                    var newOrder = new LimitOrder
                    {
                        Id         = order.Id,
                        Time       = order.Time,
                        LimitPrice = option.StrikePrice,
                        Symbol     = underlying.Symbol,
                        Quantity   = option.Symbol.ID.OptionRight == OptionRight.Call ? quantity : -quantity
                    };

                    // we continue with this call for underlying
                    return(underlying.BuyingPowerModel.HasSufficientBuyingPowerForOrder(portfolio, underlying, newOrder));
                }

                return(new HasSufficientBuyingPowerForOrderResult(true));
            }

            // When order only reduces or closes a security position, capital is always sufficient
            if (security.Holdings.Quantity * order.Quantity < 0 && Math.Abs(security.Holdings.Quantity) >= Math.Abs(order.Quantity))
            {
                return(new HasSufficientBuyingPowerForOrderResult(true));
            }

            var freeMargin = GetMarginRemaining(portfolio, security, order.Direction);
            var initialMarginRequiredForOrder = GetInitialMarginRequiredForOrder(security, order);

            // pro-rate the initial margin required for order based on how much has already been filled
            var percentUnfilled = (Math.Abs(order.Quantity) - Math.Abs(ticket.QuantityFilled)) / Math.Abs(order.Quantity);
            var initialMarginRequiredForRemainderOfOrder = percentUnfilled * initialMarginRequiredForOrder;

            if (Math.Abs(initialMarginRequiredForRemainderOfOrder) > freeMargin)
            {
                var reason = $"Id: {order.Id}, Initial Margin: {initialMarginRequiredForRemainderOfOrder.Normalize()}, Free Margin: {freeMargin.Normalize()}";
                Log.Error($"SecurityMarginModel.HasSufficientBuyingPowerForOrder(): {reason}");
                return(new HasSufficientBuyingPowerForOrderResult(false, reason));
            }

            return(new HasSufficientBuyingPowerForOrderResult(true));
        }
Beispiel #6
0
        /// <summary>
        /// Check if there is sufficient buying power to execute this order.
        /// </summary>
        /// <param name="portfolio">The algorithm's portfolio</param>
        /// <param name="security">The security to be traded</param>
        /// <param name="order">The order to be checked</param>
        /// <returns>Returns buying power information for an order</returns>
        public HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(SecurityPortfolioManager portfolio, Security security, Order order)
        {
            var baseCurrency = security as IBaseCurrencySymbol;

            if (baseCurrency == null)
            {
                return(new HasSufficientBuyingPowerForOrderResult(false, $"The '{security.Symbol.Value}' security is not supported by this cash model. Currently only SecurityType.Crypto and SecurityType.Forex are supported."));
            }

            decimal totalQuantity;
            decimal orderQuantity;

            if (order.Direction == OrderDirection.Buy)
            {
                // quantity available for buying in quote currency
                totalQuantity = portfolio.CashBook[security.QuoteCurrency.Symbol].Amount;
                orderQuantity = order.AbsoluteQuantity * GetOrderPrice(security, order);
            }
            else
            {
                // quantity available for selling in base currency
                totalQuantity = portfolio.CashBook[baseCurrency.BaseCurrencySymbol].Amount;
                orderQuantity = order.AbsoluteQuantity;
            }

            // calculate reserved quantity for open orders (in quote or base currency depending on direction)
            var openOrdersReservedQuantity = GetOpenOrdersReservedQuantity(portfolio, security, order);

            bool isSufficient;
            var  reason = string.Empty;

            if (order.Direction == OrderDirection.Sell)
            {
                // can sell available and non-reserved quantities
                isSufficient = orderQuantity <= totalQuantity - openOrdersReservedQuantity;
                if (!isSufficient)
                {
                    reason = $"Your portfolio holds {totalQuantity.Normalize()} {baseCurrency.BaseCurrencySymbol}, {openOrdersReservedQuantity.Normalize()} {baseCurrency.BaseCurrencySymbol} of which are reserved for open orders, but your Sell order is for {orderQuantity.Normalize()} {baseCurrency.BaseCurrencySymbol}. Cash Modeling trading does not permit short holdings so ensure you only sell what you have, including any additional open orders.";
                }

                return(new HasSufficientBuyingPowerForOrderResult(isSufficient, reason));
            }

            if (order.Type == OrderType.Market)
            {
                // include existing holdings (in quote currency)
                var holdingsValue =
                    portfolio.CashBook.Convert(
                        portfolio.CashBook[baseCurrency.BaseCurrencySymbol].Amount, baseCurrency.BaseCurrencySymbol, security.QuoteCurrency.Symbol);

                // find a target value in account currency for buy market orders
                var targetValue =
                    portfolio.CashBook.ConvertToAccountCurrency(totalQuantity - openOrdersReservedQuantity + holdingsValue,
                                                                security.QuoteCurrency.Symbol);

                // maximum quantity that can be bought (in quote currency)
                var maximumQuantity =
                    GetMaximumOrderQuantityForTargetValue(portfolio, security, targetValue).Quantity *GetOrderPrice(security, order);

                isSufficient = orderQuantity <= Math.Abs(maximumQuantity);
                if (!isSufficient)
                {
                    reason = $"Your portfolio holds {totalQuantity.Normalize()} {security.QuoteCurrency.Symbol}, {openOrdersReservedQuantity.Normalize()} {security.QuoteCurrency.Symbol} of which are reserved for open orders, but your Buy order is for {order.AbsoluteQuantity.Normalize()} {baseCurrency.BaseCurrencySymbol}. Your order requires a total value of {orderQuantity.Normalize()} {security.QuoteCurrency.Symbol}, but only a total value of {(Math.Abs(maximumQuantity) + holdingsValue).Normalize()} {security.QuoteCurrency.Symbol} is available.";
                }

                return(new HasSufficientBuyingPowerForOrderResult(isSufficient, reason));
            }

            // for limit orders, add fees to the order cost
            var orderFee = 0m;

            if (order.Type == OrderType.Limit)
            {
                orderFee = security.FeeModel.GetOrderFee(security, order);
                orderFee = portfolio.CashBook.Convert(orderFee, CashBook.AccountCurrency, security.QuoteCurrency.Symbol);
            }

            isSufficient = orderQuantity <= totalQuantity - openOrdersReservedQuantity - orderFee;
            if (!isSufficient)
            {
                reason = $"Your portfolio holds {totalQuantity.Normalize()} {security.QuoteCurrency.Symbol}, {openOrdersReservedQuantity.Normalize()} {security.QuoteCurrency.Symbol} of which are reserved for open orders, but your Buy order is for {order.AbsoluteQuantity.Normalize()} {baseCurrency.BaseCurrencySymbol}. Your order requires a total value of {orderQuantity.Normalize()} {security.QuoteCurrency.Symbol}, but only a total value of {(totalQuantity - openOrdersReservedQuantity - orderFee).Normalize()} {security.QuoteCurrency.Symbol} is available.";
            }

            return(new HasSufficientBuyingPowerForOrderResult(isSufficient, reason));
        }
Beispiel #7
0
        private static decimal GetOpenOrdersReservedQuantity(SecurityPortfolioManager portfolio, Security security, Order order)
        {
            var baseCurrency = security as IBaseCurrencySymbol;

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

            // find the target currency for the requested direction and the securities potentially involved
            var targetCurrency = order.Direction == OrderDirection.Buy
                ? security.QuoteCurrency.Symbol
                : baseCurrency.BaseCurrencySymbol;

            var symbolDirectionPairs = new Dictionary <Symbol, OrderDirection>();

            foreach (var portfolioSecurity in portfolio.Securities.Values)
            {
                var basePortfolioSecurity = portfolioSecurity as IBaseCurrencySymbol;
                if (basePortfolioSecurity == null)
                {
                    continue;
                }

                if (basePortfolioSecurity.BaseCurrencySymbol == targetCurrency)
                {
                    symbolDirectionPairs.Add(portfolioSecurity.Symbol, OrderDirection.Sell);
                }
                else if (portfolioSecurity.QuoteCurrency.Symbol == targetCurrency)
                {
                    symbolDirectionPairs.Add(portfolioSecurity.Symbol, OrderDirection.Buy);
                }
            }

            // fetch open orders with matching symbol/side
            var openOrders = portfolio.Transactions.GetOpenOrders(x =>
            {
                OrderDirection dir;
                return(symbolDirectionPairs.TryGetValue(x.Symbol, out dir) &&
                       // same direction of our order
                       dir == x.Direction &&
                       // don't count our current order
                       x.Id != order.Id &&
                       // only count working orders
                       (x.Type == OrderType.Limit || x.Type == OrderType.StopMarket));
            }
                                                                  );

            // calculate reserved quantity for selected orders
            var openOrdersReservedQuantity = 0m;

            foreach (var openOrder in openOrders)
            {
                var orderSecurity     = portfolio.Securities[openOrder.Symbol];
                var orderBaseCurrency = orderSecurity as IBaseCurrencySymbol;

                if (orderBaseCurrency != null)
                {
                    // convert order value to target currency
                    var quantityInTargetCurrency = openOrder.AbsoluteQuantity;
                    if (orderSecurity.QuoteCurrency.Symbol == targetCurrency)
                    {
                        quantityInTargetCurrency *= GetOrderPrice(security, openOrder);
                    }

                    openOrdersReservedQuantity += quantityInTargetCurrency;
                }
            }

            return(openOrdersReservedQuantity);
        }
Beispiel #8
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)
            {
                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));
        }
Beispiel #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MarginCallModel"/> class
 /// </summary>
 /// <param name="portfolio">The portfolio object to receive margin calls</param>
 public MarginCallModel(SecurityPortfolioManager portfolio)
 {
     Portfolio = portfolio;
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="HasSufficientBuyingPowerForOrderParameters"/> class
 /// </summary>
 /// <param name="portfolio">The algorithm's portfolio</param>
 /// <param name="security">The security</param>
 /// <param name="order">The order</param>
 public HasSufficientBuyingPowerForOrderParameters(SecurityPortfolioManager portfolio, Security security, Order order)
 {
     Portfolio = portfolio;
     Security  = security;
     Order     = order;
 }