---
title: "Exercise"
description: "If you exercise a long Option position or are assigned on your short Option position, LEAN processes an Option exercise order."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/reality-modeling/options-models/exercise
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/reality-modeling/options-models/exercise.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/24%20Reality%20Modeling/09%20Options%20Models/03%20Exercise
modified: 2026-10-29
index: https://www.quantconnect.com/llms.txt
---

# Exercise

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > Reality Modeling > Options Models > Exercise

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## 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](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-types/option-exercise-orders.md) into an [OrderEvent](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-events.md).

## 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](https://www.quantconnect.com/docs/v2/writing-algorithms/initialization.md#07-Set-Security-Initializer).

```python
class AddSecurityInitializerExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        # In the Initialize method, set the security initializer to set models of assets.
        self.add_security_initializer(self._custom_security_initializer)

    def _custom_security_initializer(self, security: Security) -> None:
        # 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.

For more information about this model, see the [class reference](https://www.lean.io/docs/v2/lean-engine/class-reference/py/QuantConnect/Orders/OptionExercise/DefaultExerciseModel/) and [implementation](https://github.com/QuantConnect/Lean/blob/master/Common/Orders/OptionExercise/DefaultExerciseModel.cs).

## 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.

```python
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 ]
```

`OptionExerciseOrder` objects have the following properties:

*Members of `QuantConnect.Orders.OptionExerciseOrder` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Orders.OptionExerciseOrder&language=python*

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:

*Members of `QuantConnect.Orders.OrderEvent` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Orders.OrderEvent&language=python*

## 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.

```python
class OptionExerciseModelAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)

        # 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.straddle(30))
        # Set custom option exercise model for disabling exercise through security initializer.
        self.settings.seed_initial_prices = True

    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 ]
```

### Example 2: Default Model Implementation

The following algorithm implements the `DefaultExerciseModel` so you can easily customize the parts you need.

```python
class CustomExerciseModelAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(100000)
        self.settings.seed_initial_prices = True
        self.add_security_initializer(self._custom_security_initializer)
        option = self.add_option("SPY")
        self.option_symbol = option.symbol

    def on_data(self, data: Slice):
        if self.portfolio.invested:
            return
        chain = data.option_chains.get(self.option_symbol)
        if chain:
            contract = sorted([x for x in chain if x.right == OptionRight.CALL],
                key=lambda x: x.strike)[0]
            self.market_order(contract.symbol, 1)
            self.exercise_option(contract.symbol, 1)
            self.quit()

    def _custom_security_initializer(self, security: Security) -> None:
        if security.type == SecurityType.OPTION:
            security.set_option_exercise_model(MyOptionExerciseModel(self))

class MyOptionExerciseModel(DefaultExerciseModel):

    def __init__(self, algorithm):
        self.algorithm = algorithm

    def option_exercise(self, option: Option, order: OptionExerciseOrder) -> List[OrderEvent]:
        order_events = []

        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)
        is_assignment = in_the_money and option.holdings.is_short
        messages = Messages.DefaultExerciseModel

        order_event = OrderEvent(
            order.id,
            option.symbol,
            utc_time,
            OrderStatus.FILLED,
            Extensions.get_order_direction(order.quantity),
            0,
            order.quantity,
            OrderFee.ZERO,
            messages.contract_holdings_adjustment_fill_tag(in_the_money, is_assignment, option)
        )
        order_event.is_assignment = is_assignment
        order_event.is_in_the_money = in_the_money
        order_events.append(order_event)

        if in_the_money and option.exercise_settlement == SettlementType.PHYSICAL_DELIVERY:
            exercise_quantity = option.get_exercise_quantity(order.quantity);
            order_event =  OrderEvent(
                order.id,
                underlying.symbol,
                utc_time,
                OrderStatus.FILLED,
                Extensions.get_order_direction(exercise_quantity),
                option.strike_price,
                exercise_quantity,
                OrderFee.ZERO,
                messages.option_assignment if is_assignment else messages.option_exercise
            )
            order_event.is_in_the_money = True
            order_events.append(order_event)

        return order_events
```

### Other Examples

For more examples, see the following algorithms:

**Demonstration Algorithms**

- [CustomOptionExerciseModelRegressionAlgorithm.py](https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/CustomOptionExerciseModelRegressionAlgorithm.py) (Python)
