31 private readonly TimeSpan _priorExpiration;
34 private readonly decimal _requiredInTheMoneyPercent;
43 _priorExpiration = priorExpiration ??
new TimeSpan(4, 0, 0, 0);
44 _requiredInTheMoneyPercent = requiredInTheMoneyPercent;
54 var option = parameters.
Option;
58 if ((option.Symbol.ID.OptionStyle ==
OptionStyle.American && option.Symbol.ID.Date - option.LocalTime <= _priorExpiration ||
59 option.Symbol.ID.OptionStyle ==
OptionStyle.European && option.Symbol.ID.Date.Date == option.LocalTime.Date)
61 && IsDeepInTheMoney(option))
64 var potentialPnL = EstimateArbitragePnL(option, (
OptionHolding)option.Holdings, underlying);
67 return new OptionAssignmentResult(option.Holdings.AbsoluteQuantity,
"Simulated option assignment before expiration");
74 private bool IsDeepInTheMoney(
Option option)
76 var symbol = option.
Symbol;
85 ? (underlyingPrice - option.
ScaledStrikePrice) / underlyingPrice > _requiredInTheMoneyPercent
86 : (option.
ScaledStrikePrice - underlyingPrice) / underlyingPrice > _requiredInTheMoneyPercent;
91 private static decimal EstimateArbitragePnL(Option option, OptionHolding holding,
Security underlying)
99 var optionPrice = option.BidPrice;
103 var underlyingPrice = option.Symbol.ID.OptionRight ==
OptionRight.Call
109 var underlyingQuantity = option.GetExerciseQuantity(holding.Quantity);
112 var marketOrder1 =
new MarketOrder(option.Symbol, -holding.Quantity, option.LocalTime.ConvertToUtc(option.Exchange.TimeZone));
113 var orderFee1 = option.FeeModel.GetOrderFee(
114 new OrderFeeParameters(option, marketOrder1)).Value.Amount * option.QuoteCurrency.ConversionRate;
116 var basePnL = (optionPrice - holding.AveragePrice) * -holding.Quantity
117 * option.QuoteCurrency.ConversionRate
118 * option.SymbolProperties.ContractMultiplier
122 var optionExerciseOrder2 =
new OptionExerciseOrder(option.Symbol, (
int)holding.AbsoluteQuantity, option.LocalTime.ConvertToUtc(option.Exchange.TimeZone));
123 var optionOrderFee2 = option.FeeModel.GetOrderFee(
124 new OrderFeeParameters(option, optionExerciseOrder2)).Value.Amount * option.QuoteCurrency.ConversionRate;
126 var underlyingOrderFee2Amount = 0m;
132 var underlyingMarketOrder2 =
new MarketOrder(underlying.
Symbol, -underlyingQuantity,
136 underlyingOrderFee2Amount = underlyingOrderFee2;
140 var altPnL = (underlyingPrice - option.ScaledStrikePrice) * underlyingQuantity * underlying.
QuoteCurrency.
ConversionRate * option.ContractUnitOfTrade
141 - underlyingOrderFee2Amount
142 - holding.AveragePrice * holding.AbsoluteQuantity * option.SymbolProperties.ContractMultiplier * option.QuoteCurrency.ConversionRate
145 return altPnL - basePnL;