book
Checkout our new book! Hands on AI Trading with Python, QuantConnect, and AWS Learn More arrow

Options Models

Exercise

Introduction

If you exercise a long Option position or are assigned on your short Option position, LEAN processes an Option exercise order. The Option exercise model converts the Option exercise order into an OrderEvent.

Set Models

To set the exercise model of an Option, call the set_option_exercise_model method of the Option object inside a security initializer.

Select Language:
class BrokerageModelExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        # In the Initialize method, set the security initializer to seed initial the prices and models of assets.
        self.set_security_initializer(MySecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))

# Outside of the algorithm class
class MySecurityInitializer(BrokerageModelSecurityInitializer):

    def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder) -> None:
        super().__init__(brokerage_model, security_seeder)    
    def initialize(self, security: Security) -> None:
        # First, call the superclass definition.
        # This method sets the reality models of each security using the default reality models of the brokerage model.
        super().initialize(security)

        # Next, overwrite the Option exercise model
        if security.Type == SecurityType.OPTION: # Option type
            security.set_option_exercise_model(DefaultExerciseModel())

Default Behavior

The default Option exercise model is the DefaultExerciseModel. The DefaultExerciseModel fills exercise orders to the full quantity with zero fees and applies an order tag to represent if the order is an exercise or assignment. To view the implementation of this model, see the LEAN GitHub repository.

Model Structure

Option exercise models should extend the DefaultExerciseModel class. Extensions of the DefaultExerciseModel must implement the option_exercise method, which receives Option and OptionExerciseOrder objects and then returns a list of OrderEvent objects that contain the order fill information.

Select Language:
class CustomOptionExerciseModelExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        security = self.add_option("SPY")
        # Set custom option exercise model for mimicking specific Brokerage most realistic actions
        security.set_option_exercise_model(MyOptionExerciseModel())

# Define the custom Option exercise model outside of the algorithm
class MyOptionExerciseModel(DefaultExerciseModel):
    def option_exercise(self, option: Option, order: OptionExerciseOrder) -> List[OrderEvent]:
        in_the_money = option.is_auto_exercised(option.underlying.close)
        is_assignment = in_the_money and option.holdings.is_short

        order_event = OrderEvent(
            order.id,
            option.symbol,
            Extensions.convert_to_utc(option.local_time, option.exchange.time_zone),
            OrderStatus.FILLED,
            Extensions.get_order_direction(order.quantity),
            0.0,
            order.quantity,
            OrderFee.zero,
            "Tag"
        )
        order_event.is_assignment = is_assignment
        return [ order_event ]

For a full example algorithm, see this backtest.

OptionExerciseOrder objects have the following properties:

The following table describes the arguments of the OrderEvent constructor:

Argument Details

Argument: order_id

Id of the parent order

Data Type: int | Default Value: -

Argument: symbol

Asset Symbol

Data Type: Symbol | Default Value: -

Argument: utc_time

Date/time of this event

Data Type: datetime | Default Value: -

Argument: direction

The direction of the order. The OrderDirection enumeration has the following members:

Data Type: OrderDirection | Default Value: Hold

Argument: fill_price

Fill price information if applicable

Data Type: float | Default Value: 0

Argument: fill_quantity

Fill quantity

Data Type: float | Default Value: 0

Argument: order_fee

The order fee. You can use OrderFee.Zero or create an OrderFee object with a custom fee.

OrderFee(CashAmount(0.5, 'USD'))

Data Type: OrderFee | Default Value: -

Argument: message

Message from the exchange

Data Type: str | Default Value: ""

OrderEvent objects have the following attributes:

Examples

The following examples demonstrate some common practices for implementing a custom option exercise model.

Example 1: Cash Settlement

The following algorithm trades GOOG 30-day expiring straddle. Yet, instead of settling with the underlying stock, some brokerages will settle with cash for ITM options. To simulate this behavior, we can create a custom option exercise model.

Select Language:
class OptionExerciseModelAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2017, 4, 1)
        self.set_end_date(2017, 6, 30)

        # Request GOOG option data for trading.
        security = self.add_option("GOOG")
        self.goog = security.symbol
        # Filter for the 2 ATM contracts expiring in 30 days to form a straddle strategy.
        security.set_filter(lambda universe: universe.include_weeklys().straddle(30))
        # Set custom option exercise model for disabling exercise through security initializer.
        self.set_security_initializer(MySecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))

    def on_data(self, slice: Slice) -> None:
        # Open position on updated option chain data.
        chain = slice.option_chains.get(self.goog)
        if chain and not self.portfolio.invested:
            # Only one strike and expiry for the straddle universe.
            strike = min(x.strike for x in chain)
            expiry = min(x.expiry for x in chain)
            # Open the straddle position.
            option_straddle = OptionStrategies.straddle(self.goog, strike, expiry)
            self.buy(option_straddle, 5)

class MySecurityInitializer(BrokerageModelSecurityInitializer):
    def __init__(self, brokerageModel, securitySeeder):
        super().__init__(brokerageModel, securitySeeder)

    def initialize(self, security: Security) -> None:
        super().initialize(security)

        # Set the custom Option exercise model for Option securities
        if security.type == SecurityType.OPTION:
            security.set_option_exercise_model(MyOptionExerciseModel())

# Define the custom Option exercise model outside of the algorithm
class MyOptionExerciseModel(DefaultExerciseModel):
    def option_exercise(self, option: Option, order: OptionExerciseOrder) -> List[OrderEvent]:
        underlying = option.underlying
        utc_time = Extensions.convert_to_utc(option.local_time, option.exchange.time_zone)
        in_the_money = option.is_auto_exercised(underlying.close)
        # Cash settle: using payoff.
        payoff = option.get_intrinsic_value(underlying.close)

        # Only liquidate option positions, but do not add equity positions.
        order_event = OrderEvent(
            order.id,
            option.symbol,
            utc_time,
            OrderStatus.FILLED,
            Extensions.get_order_direction(order.quantity),
            payoff,
            order.quantity,
            OrderFee.ZERO,
            "Option Settlement"
        )
        order_event.is_in_the_money = in_the_money
        return [ order_event ]

Other Examples

For more examples, see the following algorithms:

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: