How to Use Contingency Orders (OCO, OTO, OUO) in Nautilus Trader

Contingency orders in Nautilus Trader are controlled through the ContingencyType enumeration defined in nautilus_trader/model/enums.py, enabling OCO (One-Cancels-Other), OTO (One-Triggers-Other), and OUO (One-Updates-Other) patterns via OrderList and OrderFactory APIs.

Nautilus Trader provides native support for contingency orders, allowing traders to link multiple orders with specific execution dependencies. This functionality is implemented through the ContingencyType enum and the OrderList data structure, which manages relationships between parent and child orders. Understanding how to configure these contingency types is essential for implementing advanced order management strategies such as brackets and stop-loss attachments.

Understanding Contingency Types in Nautilus Trader

The ContingencyType enum in nautilus_trader/model/enums.py defines three distinct linking behaviors:

Type Meaning Typical Use
OCO One‑Cancels‑Other – two mutually exclusive orders; when one fills, the other is automatically cancelled. Separate take‑profit / stop‑loss orders that should not both execute.
OTO One‑Triggers‑Other – a primary "entry" order that, once executed, activates a secondary order (or list). Opening a position and immediately attaching a stop‑loss that should only appear after the entry fills.
OUO One‑Updates‑Other – a primary order that, after execution, spawns two or more child orders that are linked to the parent. A bracket where a filled entry order creates both a stop‑loss and a take‑profit order that are tied to the same parent.

The enum definition at lines 237-242:


# https://github.com/nautechsystems/nautilus_trader/blob/develop/nautilus_trader/model/enums.py#L237-L242

class ContingencyType(Enum):
    NO_CONTINGENCY = 0
    OCO = 1          # One‑Cancels‑Other

    OTO = 2          # One‑Triggers‑Other

    OUO = 3          # One‑Updates‑Other (bracket)

Where Contingency Logic Lives in the Source Code

Concern Primary source file(s)
ContingencyType enum nautilus_trader/model/enums.py
Order definitions (MarketOrder, LimitOrder, StopMarketOrder) nautilus_trader/model/orders/*.py
Grouping & linking (OrderList, contingency helpers) nautilus_trader/model/orders/list.py
Factory helpers (OrderFactory.bracket, generate_order_list_id) nautilus_trader/model/orders/factory.py
Execution‑engine handling of contingent orders (e.g., OCO cancellation) nautilus_trader/execution/reports.py (contingency conversion)
Tests that illustrate each pattern tests/unit_tests/model/test_orders.py, tests/unit_tests/trading/test_strategy.py

Implementing OCO (One-Cancels-Other) Orders

OCO contingency orders ensure that when one order fills, its counterpart is automatically cancelled. This is essential for stop-loss and take-profit pairs.

from nautilus_trader.model.enums import ContingencyType, OrderSide
from nautilus_trader.model.orders import LimitOrder, StopMarketOrder
from nautilus_trader.model.identifiers import OrderListId
from nautilus_trader.trading.commands import SubmitOrderList
from nautilus_trader.model.orders import OrderList

# Build two mutually exclusive orders

take_profit = LimitOrder(
    instrument_id=instrument.id,
    side=OrderSide.SELL,
    quantity=qty,
    price=take_profit_price,
    contingency_type=ContingencyType.OCO,
    order_list_id=OrderListId("OL-01"),
)

stop_loss = StopMarketOrder(
    instrument_id=instrument.id,
    side=OrderSide.SELL,
    quantity=qty,
    trigger_price=stop_price,
    contingency_type=ContingencyType.OCO,
    order_list_id=OrderListId("OL-01"),
)

# Submit together – the broker will cancel the other order when one fills.

submit = SubmitOrderList(order_list=OrderList(
    order_list_id=OrderListId("OL-01"),
    orders=[take_profit, stop_loss],
))
trader.submit(submit)

Both orders share the same order_list_id and have contingency_type=ContingencyType.OCO. The execution engine handles the cancellation logic when one fills.

Implementing OTO (One-Triggers-Other) Orders

OTO orders create a dependency where the secondary order only becomes active after the primary order fills. This is commonly used to attach stop-losses to entry orders.

from nautilus_trader.model.enums import ContingencyType, OrderSide
from nautilus_trader.model.orders import MarketOrder, StopMarketOrder
from nautilus_trader.model.identifiers import OrderListId
from nautilus_trader.trading.commands import SubmitOrderList
from nautilus_trader.model.orders import OrderList

# Entry order – will be placed first

entry = MarketOrder(
    instrument_id=instrument.id,
    side=OrderSide.BUY,
    quantity=qty,
    contingency_type=ContingencyType.OTO,
    order_list_id=OrderListId("OL-02"),
)

# Child order – becomes active only after entry fills

stop = StopMarketOrder(
    instrument_id=instrument.id,
    side=OrderSide.SELL,
    quantity=qty,
    trigger_price=stop_price,
    parent_order_id=entry.client_order_id,   # linked to entry

    contingency_type=ContingencyType.NO_CONTINGENCY,
    order_list_id=OrderListId("OL-02"),
)

submit = SubmitOrderList(order_list=OrderList(
    order_list_id=OrderListId("OL-02"),
    orders=[entry, stop],
))
trader.submit(submit)

The entry order uses ContingencyType.OTO, while the child references the entry via parent_order_id. The OrderList.is_oto() helper in nautilus_trader/model/orders/list.py validates this structure.

Implementing OUO (One-Updates-Other) Bracket Orders

OUO is the standard pattern for bracket orders, where a filled entry spawns multiple linked children (typically stop-loss and take-profit). The OrderFactory.bracket() method in nautilus_trader/model/orders/factory.py automates this construction.

from nautilus_trader.model.enums import OrderSide
from nautilus_trader.trading.commands import SubmitOrderList

# Using the built‑in OrderFactory (found in nautilus_trader/model/orders/factory.py)

bracket = strategy.order_factory.bracket(
    instrument_id=instrument.id,
    side=OrderSide.BUY,
    quantity=qty,
    sl_trigger_price=stop_price,   # stop‑loss trigger

    tp_price=take_profit_price,    # take‑profit limit

    entry_tags=["ENTRY"],
    sl_tags=["STOP_LOSS"],
    tp_tags=["TAKE_PROFIT"],
)

# `bracket` is an OrderList with the exact layout shown in the tests:

# - entry order: contingency_type = OTO

# - stop‑loss:   contingency_type = OUO, parent = entry

# - take‑profit: contingency_type = OUO, parent = entry

trader.submit(SubmitOrderList(order_list=bracket))

This factory method generates an OrderList where the entry uses ContingencyType.OTO and both children use ContingencyType.OUO with parent_order_id referencing the entry. This structure is validated in tests/unit_tests/model/test_orders.py lines 1868‑1881.

Summary

  • ContingencyType enum: Located in nautilus_trader/model/enums.py, defines OCO, OTO, and OUO behaviors.
  • OCO orders: Set contingency_type=ContingencyType.OCO on both orders with a shared order_list_id to ensure mutual cancellation.
  • OTO orders: Use ContingencyType.OTO on the primary order and set parent_order_id on the secondary order to delay activation until the primary fills.
  • OUO bracket orders: Leverage OrderFactory.bracket() in nautilus_trader/model/orders/factory.py to automatically create entry orders with linked stop-loss and take-profit children using ContingencyType.OUO.
  • Order linking: All contingent orders require proper order_list_id assignment and, where applicable, parent_order_id references to maintain the execution hierarchy.

Frequently Asked Questions

What is the difference between OTO and OUO in Nautilus Trader?

OTO (One-Triggers-Other) creates a simple dependency where a single secondary order activates after the primary order fills. OUO (One-Updates-Other) is designed for complex brackets where the primary order spawn multiple child orders (e.g., stop-loss and take-profit) that are all linked to the same parent. While OTO handles one-to-one relationships, OUO manages one-to-many relationships with synchronized lifecycle management.

To implement OCO (One-Cancels-Other), assign ContingencyType.OCO to both orders and ensure they share the same OrderListId. When submitting, wrap both orders in an OrderList and submit via SubmitOrderList. The execution engine monitors fills and automatically cancels the remaining order when either one executes, as handled in the reporting layer within nautilus_trader/execution/reports.py.

Can I create bracket orders without using OrderFactory?

Yes, you can manually construct OUO bracket orders by creating the entry order with ContingencyType.OTO, then creating separate stop-loss and take-profit orders with ContingencyType.OUO and their parent_order_id fields set to the entry order's client_order_id. Group all three orders under a single OrderList with a shared OrderListId and submit them together. However, using OrderFactory.bracket() in nautilus_trader/model/orders/factory.py reduces boilerplate and ensures correct contingency type assignment.

Where is the execution logic for contingency order cancellation handled?

The core definitions for contingency types reside in nautilus_trader/model/enums.py, while the grouping logic is managed in nautilus_trader/model/orders/list.py. When orders are executed, the conversion and reporting of contingency states occur in nautilus_trader/execution/reports.py, which handles the broker-side communication for OCO cancellations and OTO activations. The OrderList structure ensures that the execution engine maintains the proper hierarchy between parent and child orders throughout the order lifecycle.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →