---
title: "Assignment"
description: "If you sell an Option in a backtest, an assignment model can simulate an Option exercise order on behalf of the buyer and assign you."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/reality-modeling/options-models/assignment
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/reality-modeling/options-models/assignment.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/24%20Reality%20Modeling/09%20Options%20Models/04%20Assignment
modified: 2026-10-29
index: https://www.quantconnect.com/llms.txt
---

# Assignment

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

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

## Introduction

If you sell an Option in a backtest, an assignment model can simulate an [Option exercise order](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-types/option-exercise-orders.md) on behalf of the buyer and assign you to complete the requirements of the contract.

## Set Models

To set the assignment model of an Option, call the `set_option_assignment_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 assignment model
        if security.Type == SecurityType.OPTION: # Option type
            security.set_option_assignment_model(DefaultOptionAssignmentModel())
```

## Default Behavior

The default Option assignment model is the `DefaultOptionAssignmentModel`. The `DefaultOptionAssignmentModel` scans your portfolio every hour. It considers exercising American-style Options if they are within 4 days of their expiration and it considers exercising European-style Options on their day of expiration. If you have sold an Option that's 5% in-the-money and the Option exercise order is profitable after the cost of fees, this model exercises the Option.

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

## Model Structure

Option assignment models should extend the `NullOptionAssignmentModel` class. Extensions of the `NullOptionAssignmentModel` class must implement the `get_assignment` method, which automatically fires at the top of each hour and returns the Option assignments to generate.

```python
class CustomOptionAssignmentModelExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        security = self.add_option("SPY")
        # Set custom option assignment model for mimicking specific Brokerage most realistic actions
        security.set_option_assignment_model(MyOptionAssignmentModel())

# Define the custom Option assignment model outside of the algorithm
class MyOptionAssignmentModel(NullOptionAssignmentModel):

    def get_assignment(self, parameters: OptionAssignmentParameters) -> OptionAssignmentResult:
        option = parameters.option
        # Check if the contract is ITM
        if (option.right == OptionRight.CALL and option.underlying.price > option.strike_price or
            option.right == OptionRight.PUT and option.underlying.price < option.strike_price):
            return OptionAssignmentResult(option.holdings.absolute_quantity, "MyTag")
        return OptionAssignmentResult.NULL
```

The `OptionAssignmentParameters` object has the following members:

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

To exercise the Option, return an `OptionAssignmentResult` with a positive quantity. Otherwise, return `OptionAssignmentResult.Null`. The `OptionAssignmentResult` constructor accepts the following arguments:

| Argument | Data Type | Description | Default Value |
| --- | --- | --- | --- |
| `quantity` | `float` | The quantity to assign |  |
| `tag` | `str` | The [order tag](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-properties.md#17-Tags) to use |  |

## Disable Assignments

To disable early Option assignments, set the Option assignment model to the `NullOptionAssignmentModel`.

```python
security.set_option_assignment_model(NullOptionAssignmentModel())
```

## Examples

The following examples demonstrate some common practices for handling Option assignment.

### Example 1: Liquidate After Assignment

The following algorithm trades a [short straddle strategy](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/option-strategies/short-straddle.md) on ATM SPY Options that expire within seven days. To capitalize the cash profit, it liquidates the underlying assigned Equity position in the `on_assignment_order_event` method.

```python
class OptionAssignmentAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        # Add SPY Options data for trading.
        self._option = self.add_option("SPY")
        # Filter for the 2 ATM contracts that expire in 7 days to form a straddle.
        self._option.set_filter(lambda universe: universe.straddle(7))

    def on_data(self, slice: Slice) -> None:
        # Get the current Option chain.
        chain = slice.option_chains.get(self._option.symbol)
        # Open a new position if we're not already invested.
        if chain and not self.portfolio.invested:
            # Select the strike and expiry of the contracts.
            strike = list(chain)[0].strike
            expiry = list(chain)[0].expiry
            # Open the straddle position.
            option_strategy = OptionStrategies.short_straddle(self._option.symbol, strike, expiry)
            self.buy(option_strategy, 1)

    def on_assignment_order_event(self, assignment_event: OrderEvent) -> None:
        # Liquidate the assigned SPY position.
        self.market_order(
            assignment_event.symbol.underlying,
            -assignment_event.quantity * self._option.symbol_properties.contract_multiplier
        )
```

### Example 2: Wheel Strategy

The following algorithm demonstrates a [wheel Option strategy](https://www.quantconnect.com/research/17871/automating-the-wheel-strategy) . The strategy is split into separate parts to generate consistent income: selling puts and selling covered call.

```python
class WheelStrategyAlgorithm(QCAlgorithm):
    # Define the OTM threshold. A greater value means lower risk of assignment but lower premiums to collect.
    _otm_threshold = 0.05

    def initialize(self):
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(1000000)

        # Seed the initial price of the each new security so we can add and trade contracts in the same time step.
        self.settings.seed_initial_prices = True
        # Add SPY data for trading. Use raw data normalization mode to enable fair strike price comparison.
        self._equity = self.add_equity("SPY", data_normalization_mode=DataNormalizationMode.RAW)

    def _get_target_contract(self, right: OptionRight, target_price: float) -> Symbol:
        # Get the Option chain.
        chain = self.option_chain(self._equity.symbol, flatten=True).data_frame
        # Select the contracts that are first to expire after 30 days since they have greater premiums and theta
        # to collect.
        expiry_threshold = self.time + timedelta(30)
        expiry = chain[chain.expiry > expiry_threshold].expiry.min()
        # Select the OTM Option contract to start the wheel or covered call.
        symbol = chain[
            (chain.expiry == expiry) &
            (chain.right == right) &
            (chain.strike <= target_price if right == OptionRight.PUT else chain.strike >= target_price)
        ].sort_values('strike', ascending=right == OptionRight.CALL).index[0]
        # Add the Option contract to the algorithm.
        return self.add_option_contract(symbol).symbol

    def on_data(self, slice: Slice) -> None:
        # Start the wheel by selling the OTM put.
        if not self.portfolio.invested and self.is_market_open(self._equity.symbol):
            symbol = self._get_target_contract(OptionRight.PUT, self._equity.price * (1-self._otm_threshold))
            self.set_holdings(symbol, -0.2)

    def on_assignment_order_event(self, assignment_event: OrderEvent) -> None:
        # After the put is assigned, look for an OTM call to form a covered call with the assigned underlying
        # position.
        symbol = self._get_target_contract(OptionRight.CALL, self._equity.price * (1+self._otm_threshold))
        self.market_order(symbol, -self._equity.holdings.quantity / 100)
```

### Example 3: Assign ITM Contracts

The following algorithm assigns all in-the-money contracts at the top of each hour.

```python
class CustomOptionAssignmentAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(100000)

        self.add_security_initializer(self._initialize_security)

        option = self.add_option('GOOG')
        option.set_filter(
            lambda option_filter_universe: option_filter_universe.calls_only().strikes(-65, 0).expiration(0, 30)
        )

    def _initialize_security(self, security: Security) -> None:
        # Overwrite the assignment model.
        if security.type == SecurityType.OPTION: # Option type
            security.set_option_assignment_model(MyOptionAssignmentModel())

    def on_data(self, data) -> None:
        if self.portfolio.invested:
            return

        for canonical_symbol, chain in data.option_chains.items():
            min_expiry = min([contract.expiry for contract in chain])
            contracts = sorted(
                [contract for contract in chain if contract.expiry == min_expiry],
                key=lambda contract: contract.strike
            )
            if contracts:
                self.market_order(contracts[0].symbol, -1)

class MyOptionAssignmentModel(NullOptionAssignmentModel):

    def get_assignment(self, parameters: OptionAssignmentParameters) -> OptionAssignmentResult:
        option = parameters.option
        # Check if the contract is ITM.
        if (option.right == OptionRight.CALL and option.underlying.price > option.strike_price or
            option.right == OptionRight.PUT and option.underlying.price < option.strike_price):
            return OptionAssignmentResult(option.holdings.absolute_quantity, "MyTag")
        return OptionAssignmentResult.NULL
```

### Other Examples

For more examples, see the following algorithms:

**Demonstration Algorithms**

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