private decimal EstimateArbitragePnL(Option option,
                                             OptionHolding holding,
                                             Security underlying,
                                             ICurrencyConverter currencyConverter)
        {
            // no-arb argument:
            // if our long deep ITM position has a large B/A spread and almost no time value, it may be interesting for us
            // to exercise the option and close the resulting position in underlying instrument, if we want to exit now.

            // User's short option position is our long one.
            // In order to sell ITM position we take option bid price as an input
            var optionPrice = option.BidPrice;

            // we are interested in underlying bid price if we exercise calls and want to sell the underlying immediately.
            // we are interested in underlying ask price if we exercise puts
            var underlyingPrice = option.Symbol.ID.OptionRight == OptionRight.Call
                ? underlying.BidPrice
                : underlying.AskPrice;

            // quantity is normally negative algo's holdings, but since we're modeling the contract holder (counter-party)
            // it's negative THEIR holdings. holding.Quantity is negative, so if counter-party exercises, they would reduce holdings
            var underlyingQuantity = option.GetExerciseQuantity(holding.Quantity);

            // Scenario 1 (base): we just close option position
            var marketOrder1 = new MarketOrder(option.Symbol, -holding.Quantity, option.LocalTime.ConvertToUtc(option.Exchange.TimeZone));
            var orderFee1    = currencyConverter.ConvertToAccountCurrency(option.FeeModel.GetOrderFee(
                                                                              new OrderFeeParameters(option, marketOrder1)).Value);

            var basePnL = (optionPrice - holding.AveragePrice) * -holding.Quantity
                          * option.QuoteCurrency.ConversionRate
                          * option.SymbolProperties.ContractMultiplier
                          - orderFee1.Amount;

            // Scenario 2 (alternative): we exercise option and then close underlying position
            var optionExerciseOrder2 = new OptionExerciseOrder(option.Symbol, (int)holding.AbsoluteQuantity, option.LocalTime.ConvertToUtc(option.Exchange.TimeZone));
            var optionOrderFee2      = currencyConverter.ConvertToAccountCurrency(option.FeeModel.GetOrderFee(
                                                                                      new OrderFeeParameters(option, optionExerciseOrder2)).Value);

            var undelyingMarketOrder2 = new MarketOrder(underlying.Symbol, -underlyingQuantity, underlying.LocalTime.ConvertToUtc(underlying.Exchange.TimeZone));
            var undelyingOrderFee2    = currencyConverter.ConvertToAccountCurrency(underlying.FeeModel.GetOrderFee(
                                                                                       new OrderFeeParameters(underlying, undelyingMarketOrder2)).Value);

            // calculating P/L of the two transactions (exercise option and then close underlying position)
            var altPnL = (underlyingPrice - option.StrikePrice) * underlyingQuantity * underlying.QuoteCurrency.ConversionRate * option.ContractUnitOfTrade
                         - undelyingOrderFee2.Amount
                         - holding.AveragePrice * holding.AbsoluteQuantity * option.SymbolProperties.ContractMultiplier * option.QuoteCurrency.ConversionRate
                         - optionOrderFee2.Amount;

            return(altPnL - basePnL);
        }
        private decimal EstimateArbitragePnL(Option option, OptionHolding holding, Security underlying)
        {
            // no-arb argument:
            // if our long deep ITM position has a large B/A spread and almost no time value, it may be interesting for us
            // to exercise the option and close the resulting position in underlying instrument, if we want to exit now.

            // User's short option position is our long one.
            // In order to sell ITM position we take option bid price as an input
            var optionPrice = option.BidPrice;

            // we are interested in underlying bid price if we exercise calls and want to sell the underlying immediately.
            // we are interested in underlying ask price if we exercise puts
            var underlyingPrice = option.Symbol.ID.OptionRight == OptionRight.Call ?
                                  underlying.BidPrice :
                                  underlying.AskPrice;

            var underlyingQuantity = option.Symbol.ID.OptionRight == OptionRight.Call ?
                                     option.GetExerciseQuantity((int)holding.AbsoluteQuantity) :
                                     -option.GetExerciseQuantity((int)holding.AbsoluteQuantity);

            // Scenario 1 (base): we just close option position
            var marketOrder1 = new MarketOrder(option.Symbol, -holding.Quantity, option.LocalTime.ConvertToUtc(option.Exchange.TimeZone));
            var orderFee1    = option.FeeModel.GetOrderFee(option, marketOrder1);

            var basePnL = (optionPrice - holding.AveragePrice) * -holding.Quantity * option.QuoteCurrency.ConversionRate * option.SymbolProperties.ContractMultiplier - orderFee1;

            // Scenario 2 (alternative): we exercise option and then close underlying position
            var optionExerciseOrder2 = new OptionExerciseOrder(option.Symbol, (int)holding.AbsoluteQuantity, option.LocalTime.ConvertToUtc(option.Exchange.TimeZone));
            var optionOrderFee2      = option.FeeModel.GetOrderFee(option, optionExerciseOrder2);

            var undelyingMarketOrder2 = new MarketOrder(underlying.Symbol, -underlyingQuantity, underlying.LocalTime.ConvertToUtc(underlying.Exchange.TimeZone));
            var undelyingOrderFee2    = underlying.FeeModel.GetOrderFee(underlying, undelyingMarketOrder2);

            // calculating P/L of the two transactions (exercise option and then close underlying position)
            var altPnL = (underlyingPrice - option.StrikePrice) * underlyingQuantity * underlying.QuoteCurrency.ConversionRate * option.ContractUnitOfTrade
                         - undelyingOrderFee2
                         - holding.AveragePrice * holding.AbsoluteQuantity * option.SymbolProperties.ContractMultiplier * option.QuoteCurrency.ConversionRate
                         - optionOrderFee2;

            return(altPnL - basePnL);
        }