/// <summary> /// Check if there is sufficient buying power to execute this order. /// </summary> /// <param name="parameters">An object containing the portfolio, the security and the order</param> /// <returns>Returns buying power information for an order</returns> public virtual HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(HasSufficientBuyingPowerForOrderParameters parameters) { // short circuit the div 0 case if (parameters.Order.Quantity == 0) { return(parameters.Sufficient()); } var ticket = parameters.Portfolio.Transactions.GetOrderTicket(parameters.Order.Id); if (ticket == null) { return(parameters.Insufficient( $"Null order ticket for id: {parameters.Order.Id}" )); } if (parameters.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)parameters.Security; var underlying = option.Underlying; if (option.IsAutoExercised(underlying.Close) && underlying.IsTradable) { var quantity = option.GetExerciseQuantity(parameters.Order.Quantity); var newOrder = new LimitOrder { Id = parameters.Order.Id, Time = parameters.Order.Time, LimitPrice = option.StrikePrice, Symbol = underlying.Symbol, Quantity = quantity }; // we continue with this call for underlying var parametersForUnderlying = parameters.ForUnderlying(newOrder); var freeMargin = underlying.BuyingPowerModel.GetBuyingPower(parametersForUnderlying.Portfolio, parametersForUnderlying.Security, parametersForUnderlying.Order.Direction); // we add the margin used by the option itself freeMargin += GetMaintenanceMargin(MaintenanceMarginParameters.ForQuantityAtCurrentPrice(option, -parameters.Order.Quantity)); var initialMarginRequired = underlying.BuyingPowerModel.GetInitialMarginRequiredForOrder( new InitialMarginRequiredForOrderParameters(parameters.Portfolio.CashBook, underlying, newOrder)); return(HasSufficientBuyingPowerForOrder(parametersForUnderlying, ticket, freeMargin, initialMarginRequired)); } return(parameters.Sufficient()); } return(HasSufficientBuyingPowerForOrder(parameters, ticket)); }
/// <summary> /// Check if there is sufficient buying power to execute this order. /// </summary> /// <param name="parameters">An object containing the portfolio, the security and the order</param> /// <returns>Returns buying power information for an order</returns> public virtual HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(HasSufficientBuyingPowerForOrderParameters parameters) { // short circuit the div 0 case if (parameters.Order.Quantity == 0) { return(parameters.Sufficient()); } var ticket = parameters.Portfolio.Transactions.GetOrderTicket(parameters.Order.Id); if (ticket == null) { return(parameters.Insufficient( $"Null order ticket for id: {parameters.Order.Id}" )); } if (parameters.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)parameters.Security; var underlying = option.Underlying; if (option.IsAutoExercised(underlying.Close) && underlying.IsTradable) { var quantity = option.GetExerciseQuantity(parameters.Order.Quantity); var newOrder = new LimitOrder { Id = parameters.Order.Id, Time = parameters.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( parameters.ForUnderlying(newOrder) )); } return(parameters.Sufficient()); } // When order only reduces or closes a security position, capital is always sufficient if (parameters.Security.Holdings.Quantity * parameters.Order.Quantity < 0 && Math.Abs(parameters.Security.Holdings.Quantity) >= Math.Abs(parameters.Order.Quantity)) { return(parameters.Sufficient()); } var freeMargin = GetMarginRemaining(parameters.Portfolio, parameters.Security, parameters.Order.Direction); var initialMarginRequiredForOrder = GetInitialMarginRequiredForOrder( new InitialMarginRequiredForOrderParameters( parameters.Portfolio.CashBook, parameters.Security, parameters.Order )); // pro-rate the initial margin required for order based on how much has already been filled var percentUnfilled = (Math.Abs(parameters.Order.Quantity) - Math.Abs(ticket.QuantityFilled)) / Math.Abs(parameters.Order.Quantity); var initialMarginRequiredForRemainderOfOrder = percentUnfilled * initialMarginRequiredForOrder; if (Math.Abs(initialMarginRequiredForRemainderOfOrder) > freeMargin) { return(parameters.Insufficient(Invariant($"Id: {parameters.Order.Id}, ") + Invariant($"Initial Margin: {initialMarginRequiredForRemainderOfOrder.Normalize()}, ") + Invariant($"Free Margin: {freeMargin.Normalize()}") )); } return(parameters.Sufficient()); }