NautilusTrader
ConceptsOrders
These docs track the unreleased nightly build and may change without notice. Switch to the latest stable docs.

Advanced orders

Order lists group related orders, while contingency metadata describes how fills, cancellations, or updates should affect linked orders. The component that handles the list determines the behavior: the backtest matching engine, local order emulator, live adapter and venue, or strategy code.

An OrderList or ContingencyType does not guarantee that every live adapter or venue implements the relationship. Check the target integration before relying on native contingency behavior.

Order lists

An order list groups contingent orders or a larger batch under one order_list_id. Orders in the list do not need a contingency relationship; their own metadata defines any relationship.

Production constructors require every order in a list to use the same venue. Orders may target different instruments at that venue, such as pairs, calendar spreads, or multi-leg strategies. The list takes its representative instrument_id from the first order; consumers that need the actual instrument must resolve each order individually.

Caveats for mixed-instrument lists:

  • Pre-trade checks: Price precision, quantity precision, and GTD expiry use each order's own instrument.
  • Cumulative risk check: Free balance, notional bounds, position-reducing exposure, and market data use the list's representative instrument. For a mixed list, this produces a single-instrument bound rather than per-instrument accuracy.
  • Cache lookups: cache.order_lists(instrument_id=...) filters against the representative instrument_id; lists containing other instruments will not match queries for those other instruments.
  • Position IDs: The execution engine denies mixed-instrument lists when a position_id is supplied (a position belongs to a single instrument, regardless of OMS).
  • Adapter batching: submit_order_list implementations vary. Some iterate orders per leg and resolve each order's own instrument_id against the venue API; others still build the batch request around the list's representative instrument_id and will misroute non-first orders. Treat mixed-instrument lists as adapter-specific; verify the target adapter's behavior before relying on it. Backtesting and strategy-managed routing avoid relying on an adapter's mixed-instrument batch behavior.

Contingency types

  • OTO (One-Triggers-Other): A parent order releases one or more child orders after a configured fill condition.
  • OCO (One-Cancels-Other): A fill in one linked order requests cancellation of the others.
  • OUO (One-Updates-Other): A fill in one linked order requests a quantity update for the others.

These types correspond to FIX ContingencyType <1385>.

Strategy-managed contingencies

Enable StrategyConfig.manage_contingent_orders to manage open OTO, OCO, and OUO relationships for orders that are not active local. The strategy sends the resulting cancel and quantity-update commands through the normal execution path before it calls the specific and aggregate user order-event handlers.

The OrderEmulator always owns active-local orders. Enabling strategy management therefore does not make the strategy and emulator manage the same order. The option does not add native venue support or submit a non-active-local OTO child: it manages non-active-local orders that are already open.

One-Triggers-Other (OTO)

An OTO relationship has two parts:

  1. The parent order enters its execution path.
  2. One or more child orders reference the parent and wait for the configured release condition.

The handler determines where the children wait. The backtest engine can hold them locally, while a live adapter may send native venue instructions, submit all legs, reject the list, or require the strategy to manage the relationship.

Child sizing

Before the parent's first fill, strategy management propagates parent quantity updates to open, non-active-local OTO children.

After filling starts, each parent event starts the child target at the parent's cumulative filled quantity. For an execution spawn, this quantity includes fills from every order in the spawn.

For a parent linked to a position, the manager then adjusts the target in order:

  1. For a non-spread parent with a reduce-only child, cap the total target at the child's filled quantity plus the current commission-adjusted position quantity. This keeps the child's remaining quantity within the open position.
  2. Round the total target down to a multiple of the child instrument's size increment.
  3. When configured, treat a rounded target below the child instrument's minimum quantity as zero.

The calculation does not round position or account state. A remaining position too small to meet the child instrument's size increment and optional minimum quantity stays open without reduce-only child coverage. Spread parents skip the position cap because the execution engine does not create positions for them. Non-reduce-only children also skip the cap. Both still use the child instrument's size rules.

Required sizing state

When a fill event, cached parent, or filled execution-spawn sibling identifies a position, sizing requires:

  • The parent and child instruments in the cache.
  • A positive size increment for the child instrument.
  • The linked position in the cache for a non-spread parent with a reduce-only child.
  • Matching fill-event and cached position IDs when both are present.

A fill-event position ID that conflicts with cached ownership stops processing for that parent event. Other missing sizing state leaves the affected child unchanged, and processing continues with the remaining linked children.

Child lifecycle

Parent events apply the validated target according to the child and parent state:

ConditionAction
Managed child has a different positive targetUpdate its total quantity.
Target is zero; parent or execution spawn remains activeKeep the child unchanged and wait for executable quantity.
Target is zero; parent or execution spawn closesCancel the child.
Child fills meet or exceed the positive targetCancel any remaining quantity.
Active-local child reaches an executable positive targetThe active-local emulator submits it once.

A child fill or update does not recalculate the target immediately. The next parent event refreshes it.

Trigger models

Trigger modelBacktest release condition
PartialRelease children after the parent's first partial fill.
FullRelease children after the parent's cumulative fill reaches its quantity.

The default BacktestVenueConfig mode is OtoTriggerMode.PARTIAL. Set oto_trigger_mode to OtoTriggerMode.FULL to wait for a complete fill. This setting controls release timing; it does not promise pro rata child sizing. Verify child quantities when the parent fills partially.

Enforcing a full-fill trigger in strategy code

If the execution context does not provide the required full-fill behavior:

  1. Submit the parent order without contingent children.
  2. Handle OrderFilled events for the parent.
  3. Confirm the parent has reached FILLED status.
  4. Submit the stop-loss, take-profit, or other child orders.

Full-fill release leaves a partially filled position without its contingent exits until the parent finishes. Partial release reduces that delay, but the current backtest mode does not guarantee that child quantities track each partial fill. Check quantities and adapter behavior before treating a child as complete protection.

One-Cancels-Other (OCO)

In backtest local matching, a full or partial fill in one OCO order causes a best-effort request to cancel its open siblings. The local order manager applies this behavior only while a sibling remains active local. With strategy management enabled, the strategy requests cancellation for open, non-active-local siblings. Otherwise, the adapter or venue determines cancellation behavior. Another sibling can fill before cancellation completes.

One-Updates-Other (OUO)

Updates after a sibling fill

In backtest local matching, a fill in one OUO order uses that order's remaining quantity as the target for each open sibling:

  • If the target is zero or the sibling's filled quantity already meets the target, cancel the sibling.
  • Otherwise, update the sibling's quantity when needed.

This behavior suits equal-sized peers and does not preserve a ratio between unequal starting quantities. With strategy management enabled, the strategy applies the same update or cancellation behavior to open, non-active-local siblings. Otherwise, live behavior depends on adapter and venue support.

Backtest reduce-only resizing

With reduce-only enforcement enabled, a fill can resize resting reduce-only orders to the available position quantity, subject to parent caps. When contingent-order support is also enabled, a resized OUO order propagates its remaining quantity to siblings that are:

  • Open and not active local.
  • Passive orders resting on the same instrument's book.

Siblings do not need to be reduce_only. Each sibling's quantity update follows these rules:

  • Add the sibling's prior fills to the propagated remaining quantity to obtain its total quantity.
  • Apply the sibling's own cached parent's filled-quantity cap, when available.
  • Never reduce the total below the sibling's prior fills.

The order already being filled retains its active fill loop's quantity rules. This propagation does not trigger matching itself.

Backtest cancellation at zero capacity

With reduce-only enforcement and contingent-order support enabled:

  • When the reduce-only order has no remaining capacity, cancel it and its eligible siblings without resizing the siblings. This also covers siblings whose acceptance event is still awaiting delivery.
  • When a sibling exhausts only its own parent allowance, resize it to its filled quantity, then cancel it.

Constructing contingent orders

Use OrderFactory.bracket to construct a bracket's contingency metadata. In Rust, self.order().create_list(...) assigns a fresh order_list_id to an existing group of orders. Python code instead passes a plain list to self.submit_order_list(...), which creates an OrderList when needed. These grouping paths do not create parent or linked-order relationships. The current model enforces only part of the remaining consistency:

  • A contingent order must have at least one linked_order_id.
  • A child identifies its parent through parent_order_id.
  • Rust create_list requires a non-empty list whose orders use one venue.
  • OrderList.validate checks for non-empty, unique client order IDs when a strategy submits the list.
  • OrderList.validate does not verify shared order_list_id values, parent references, or other cross-field relationships.

Modification, cancellation, and rejection behavior depends on the component managing the contingency. Do not assume a parent update or cancellation cascades in every live integration.

Handle OrderDenied and OrderRejected events for every leg. Adapter or venue failures can affect legs independently and leave a position without its intended protection.

Bracket orders

Bracket orders combine an entry with take-profit and stop-loss children. By default, OrderFactory.bracket creates a MARKET entry, a LIMIT take-profit, and a STOP_MARKET stop-loss. It marks the entry with an OTO contingency, marks both exits reduce_only, and links the exits with an OUO contingency. The default LIMIT take-profit is also post_only.

The factory creates the orders and their relationship metadata. The execution context determines whether children wait locally, use a native venue instruction, enter the venue with the parent, or require manual strategy handling.

Create brackets with OrderFactory, which also supports different entry and exit types, trigger settings, and execution instructions.

In the following example we bracket a Market entry to BUY 10 ETHUSDT-PERP contracts with a take-profit Limit at 3,300 USDT and a stop-loss Stop-Market triggering at 2,800 USDT. The entry defaults to MARKET, the take-profit to LIMIT, and the stop-loss to STOP_MARKET; the take-profit and stop-loss legs are reduce_only and linked with the OUO contingency:

use nautilus_model::{
    enums::OrderSide,
    identifiers::InstrumentId,
    types::{Price, Quantity},
};

// `bracket()` returns a `bon` builder; finalize with `.call()`.
// The result is a `Vec<OrderAny>` ordered as [entry, stop-loss, take-profit].
let orders = self
    .order()
    .bracket()
    .instrument_id(InstrumentId::from("ETHUSDT-PERP.BINANCE"))
    .order_side(OrderSide::Buy)
    .quantity(Quantity::from(10))
    .tp_price(Price::from("3300.00"))         // take-profit LIMIT (default)
    .sl_trigger_price(Price::from("2800.00")) // stop-loss STOP_MARKET (default)
    .call();

Some venues reserve margin for bracket legs. Check the venue's margin rules and handle a child rejection after the entry fills.

  • Orders - Order concepts, execution instructions, and the order factory.
  • Emulated orders - Emulating order types on venues without native support.
  • Execution - Order execution and fill handling.

On this page