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