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, definesOCO,OTO, andOUObehaviors. - OCO orders: Set
contingency_type=ContingencyType.OCOon both orders with a sharedorder_list_idto ensure mutual cancellation. - OTO orders: Use
ContingencyType.OTOon the primary order and setparent_order_idon the secondary order to delay activation until the primary fills. - OUO bracket orders: Leverage
OrderFactory.bracket()innautilus_trader/model/orders/factory.pyto automatically create entry orders with linked stop-loss and take-profit children usingContingencyType.OUO. - Order linking: All contingent orders require proper
order_list_idassignment and, where applicable,parent_order_idreferences 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.
How do I link orders for OCO contingency in Nautilus Trader?
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →