/// <summary> /// Default option exercise model for the basic equity/index option security class. /// </summary> /// <param name="option">Option we're trading this order</param> /// <param name="order">Order to update</param> public IEnumerable <OrderEvent> OptionExercise(Option option, OptionExerciseOrder order) { var utcTime = option.LocalTime.ConvertToUtc(option.Exchange.TimeZone); var optionQuantity = order.Quantity; var assignment = order.Quantity < 0; var underlying = option.Underlying; var exercisePrice = order.Price; var fillQuantity = option.GetExerciseQuantity(order.Quantity); var exerciseQuantity = option.Symbol.ID.OptionRight == OptionRight.Call ? fillQuantity : -fillQuantity; var exerciseDirection = assignment? (option.Symbol.ID.OptionRight == OptionRight.Call ? OrderDirection.Sell : OrderDirection.Buy): (option.Symbol.ID.OptionRight == OptionRight.Call ? OrderDirection.Buy : OrderDirection.Sell); var orderFee = option.FeeModel.GetOrderFee(option, order); var cashQuote = option.QuoteCurrency; var addUnderlyingEvent = new OrderEvent(order.Id, underlying.Symbol, utcTime, OrderStatus.Filled, exerciseDirection, exercisePrice, exerciseQuantity, 0.0m, "Option Exercise/Assignment"); var optionRemoveEvent = new OrderEvent(order.Id, option.Symbol, utcTime, OrderStatus.Filled, assignment ? OrderDirection.Buy : OrderDirection.Sell, 0.0m, -optionQuantity, orderFee, "Adjusting(or removing) the exercised/assigned option"); if (optionRemoveEvent.FillQuantity > 0) { optionRemoveEvent.IsAssignment = true; } if (option.ExerciseSettlement == SettlementType.PhysicalDelivery && option.IsAutoExercised(underlying.Close)) { return(new[] { optionRemoveEvent, addUnderlyingEvent }); } return(new[] { optionRemoveEvent }); }
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); }
/// <summary> /// Default option exercise model for the basic equity/index option security class. /// </summary> /// <param name="option">Option we're trading this order</param> /// <param name="order">Order to update</param> public IEnumerable <OrderEvent> OptionExercise(Option option, OptionExerciseOrder order) { var underlying = option.Underlying; var isShort = order.Quantity < 0; var utcTime = option.LocalTime.ConvertToUtc(option.Exchange.TimeZone); var inTheMoney = option.IsAutoExercised(underlying.Close); // we're assigned only if we get exercised against, meaning we wrote the option and it was exercised by the buyer var isAssignment = inTheMoney && isShort; yield return(new OrderEvent( order.Id, option.Symbol, utcTime, OrderStatus.Filled, isShort ? OrderDirection.Buy : OrderDirection.Sell, 0.0m, -order.Quantity, OrderFee.Zero, // note whether or not we expired OTM/ITM and whether we were assigned or exercised inTheMoney ? isAssignment ? "Automatic Assignment" : "Automatic Exercise" : "OTM" ) { IsAssignment = isAssignment }); if (inTheMoney && option.ExerciseSettlement == SettlementType.PhysicalDelivery) { var right = option.Symbol.ID.OptionRight; // TODO : Why doesn't this method take into account the directionality of the quantity like other quantity properties/methods var fillQuantity = option.GetExerciseQuantity(order.Quantity); var exerciseQuantity = right == OptionRight.Call ? fillQuantity : -fillQuantity; yield return(new OrderEvent( order.Id, underlying.Symbol, utcTime, OrderStatus.Filled, right.GetExerciseDirection(isShort), order.Price, exerciseQuantity, OrderFee.Zero, isAssignment ? "Option Assignment" : "Option Exercise" )); } }
public void NonAccountCurrencyOption_Exercise() { var reference = new DateTime(2016, 02, 16, 11, 53, 30); SecurityPortfolioManager portfolio; var security = InitializeTest(reference, out portfolio); var cash = new Cash("EUR", 0, 10); portfolio.CashBook.Add("EUR", cash); var option = new Option( Symbols.SPY_C_192_Feb19_2016, SecurityExchangeHours.AlwaysOpen(DateTimeZone.Utc), cash, new OptionSymbolProperties(SymbolProperties.GetDefault("EUR")), portfolio.CashBook, RegisteredSecurityDataTypesProvider.Null, new SecurityCache() ); option.Underlying = security; security.SetMarketPrice(new Tick { Value = 1000 }); portfolio.Securities.Add(option); var fakeOrderProcessor = new FakeOrderProcessor(); portfolio.Transactions.SetOrderProcessor(fakeOrderProcessor); var fillPrice = 1000m; var fillQuantity = 1; option.ExerciseSettlement = SettlementType.Cash; var orderFee = new OrderFee(new CashAmount(1, "EUR")); var order = new OptionExerciseOrder(Symbols.SPY_C_192_Feb19_2016, fillQuantity, DateTime.UtcNow); fakeOrderProcessor.AddOrder(order); var orderDirection = fillQuantity > 0 ? OrderDirection.Buy : OrderDirection.Sell; var fill = new OrderEvent(order.Id, option.Symbol, reference, OrderStatus.Filled, orderDirection, fillPrice, fillQuantity, orderFee); portfolio.ProcessFill(fill); // (1000 (price) - 192 (call strike)) * 1 quantity => 808 EUR Assert.AreEqual(10, option.Holdings.TotalFees); // 1 * 10 (conversion rate to account currency) // 808 - 1000 (price) - 1 fee Assert.AreEqual(-193, portfolio.CashBook["EUR"].Amount); // 100000 initial amount, no fee deducted Assert.AreEqual(100000, portfolio.CashBook[Currencies.USD].Amount); }
public void DeserializesOptionExpireOrder() { var expected = new OptionExerciseOrder(Symbols.SPY_P_192_Feb19_2016, 100, new DateTime(2015, 11, 23, 17, 15, 37), "now") { Id = 12345, ContingentId = 123456, BrokerId = new List <string> { "727", "54970" } }; // Note: Order price equals strike price found in Symbol object // Price = Symbol.ID.StrikePrice TestOrderType(expected); }
/// <summary> /// Default option exercise model for the basic equity/index option security class. /// </summary> /// <param name="option">Option we're trading this order</param> /// <param name="order">Order to update</param> public OrderEvent OptionExercise(Option option, OptionExerciseOrder order) { //Default order event to return var utcTime = option.LocalTime.ConvertToUtc(option.Exchange.TimeZone); var deliveryEvent = new OrderEvent(order, utcTime, 0); // make sure the exchange is open before filling if (!IsExchangeOpen(option)) { return(deliveryEvent); } //Order price for a option exercise order model is the strike price deliveryEvent.FillPrice = order.Price; deliveryEvent.FillQuantity = option.GetExerciseQuantity(order.Quantity); deliveryEvent.OrderFee = option.FeeModel.GetOrderFee(option, order); deliveryEvent.Status = OrderStatus.Filled; return(deliveryEvent); }
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); }
/// <summary> /// Default option exercise model for the basic equity/index option security class. /// </summary> /// <param name="option">Option we're trading this order</param> /// <param name="order">Order to update</param> public IEnumerable <OrderEvent> OptionExercise(Option option, OptionExerciseOrder order) { var underlying = option.Underlying; var utcTime = option.LocalTime.ConvertToUtc(option.Exchange.TimeZone); var inTheMoney = option.IsAutoExercised(underlying.Close); var isAssignment = inTheMoney && option.Holdings.IsShort; yield return(new OrderEvent( order.Id, option.Symbol, utcTime, OrderStatus.Filled, GetOrderDirection(order.Quantity), 0.0m, order.Quantity, OrderFee.Zero, GetContractHoldingsAdjustmentFillTag(inTheMoney, isAssignment) ) { IsAssignment = isAssignment }); // TODO : Support Manual Exercise of OTM contracts [ inTheMoney = false ] if (inTheMoney && option.ExerciseSettlement == SettlementType.PhysicalDelivery) { var exerciseQuantity = option.GetExerciseQuantity(order.Quantity); yield return(new OrderEvent( order.Id, underlying.Symbol, utcTime, OrderStatus.Filled, GetOrderDirection(exerciseQuantity), order.Price, exerciseQuantity, OrderFee.Zero, isAssignment ? "Option Assignment" : "Option Exercise" )); } }