Nautilus Orders

Nautilus exposes nine concrete order types (MARKET, LIMIT, STOP_MARKET, STOP_LIMIT, MARKET_IF_TOUCHED, LIMIT_IF_TOUCHED, MARKET_TO_LIMIT, TRAILING_STOP_MARKET, TRAILING_STOP_LIMIT) plus a first-class bracket(...) primitive that wires entry + take-profit + stop-loss as a contingent group. Every conditional order type can be locally emulated by the OrderEmulator: when the strategy passes an emulation_trigger (BID_ASK, LAST_PRICE, MARK_PRICE, etc.), Nautilus subscribes to the trigger feed itself, holds the order in EMULATED state, and only transmits a basic MARKET or LIMIT to the venue at trigger time. Without emulation_trigger, the order is forwarded to the venue as-is and the venue is responsible for trigger logic. Emulation is opt-in, not automatic - the user picks. This is the load-bearing answer to spike question #6: emulated brackets give Cortana genuine software-fallback defense-in-depth (Nautilus owns the trigger), while non-emulated brackets on IBKR are venue-native bracket translation (single trigger). For feedback_dual_tp_defense_in_depth.md the choice is therefore deliberate: opt into emulation_trigger=MARK_PRICE (or BID_ASK) on the TP/SL legs and the framework runs the fallback we currently hand-roll.

Cody’s open question - answered

Q: When IBKR supports brackets natively, does Nautilus’s bracket give us software-fallback defense-in-depth, or is it just venue-native bracket translation?

A: Both, depending on whether you set emulation_trigger on the bracket legs. Default = venue-native (no SW fallback). Opt-in via emulation_trigger = real software fallback (the OrderEmulator subscribes to the trigger feed and owns trigger logic locally). For Cortana MK3, set emulation_trigger on the TP and SL legs.

Verbatim from the docs: “The only requirement to emulate an order is to pass a TriggerType to the emulation_trigger parameter.” And: “The platform makes it possible to emulate most order types locally, regardless of whether the type is supported on a trading venue.” And: NO_TRIGGER: disables local emulation completely and order is fully submitted to the venue.”

So the routing rule is mechanical:

emulation_triggerWhat happens
NO_TRIGGER (or omitted)Order forwarded to venue. IBKR handles the bracket natively. No SW fallback. Single trigger - if IBKR’s LMT misses, the system has nothing else watching.
BID_ASK / LAST_PRICE / MARK_PRICE / etc.OrderEmulator holds the order in EMULATED state, subscribes to the trigger feed, watches it locally. When triggered, it transforms to a basic MARKET (or LIMIT) and submits to IBKR. The trigger logic itself is owned by Nautilus, not the venue.

This means MK2’s hand-rolled position_manager.on_quote_tick software fallback (for when the broker LMT fails to fill) maps directly to a Nautilus OrderEmulator subscription. We get the same defense-in-depth pattern, but the framework owns the subscription, the cache-then-publish ordering, the trigger evaluation, and the reduce-only semantics. We delete the hand-rolled fallback loop.

The only nuance: for brackets specifically, you set emulation_trigger on the bracket builder, which propagates to the contingent legs. The parent (entry) is typically not emulated (Cortana enters at MARKET). The TP child (LIMIT) and SL child (STOP_MARKET) are the ones that benefit from emulation. This is the right shape - entry is fast-fire-and-forget, exits are where we want belt-and-suspenders.

Caveat - the docs do not enumerate what happens if emulation_trigger is omitted and the venue rejects the order type (e.g., a venue that doesn’t support trailing stops at all). Verify on Saturday’s spike. The implication for Cortana on IBKR: STOP_MARKET, LIMIT, MARKET, and bracket groups are all supported natively, so the rejection path is not the binding constraint - the SW-fallback choice is.

Reference: order type taxonomy

Nine concrete order types, grouped by execution semantics.

Foundational types

MARKET

  • Semantics: Execute immediately at best available price. Consumes liquidity (taker).
  • Cannot be emulated. The doc lists MARKET and MARKET_TO_LIMIT as the two types that pass through to the venue regardless of emulation_trigger.
  • Cortana use: V1 entry pattern. order_factory.market(...) for the bracket parent.

LIMIT

  • Semantics: Rest in order book at specified price. Provides liquidity until matched (maker). May not fill at all.
  • Emulatable: yes. Useful when the venue does not support reserve pricing or when you want trigger logic Nautilus controls.
  • Iceberg: Pass display_qty < quantity to expose only part of the order on the book. “Specifying a display_qty of zero is also equivalent to setting an order as hidden.” display_qty=None means full display.
  • Cortana use: V1 take-profit child of brackets, marketable-LMT exits.

Conditional / triggered types

STOP_MARKET

  • Semantics: Resting trigger. When the venue’s selected price feed hits trigger_price, the order becomes a MARKET order and races to fill.
  • Emulatable: yes. Without emulation, the venue’s matching engine watches the trigger.
  • Cortana use: V1 stop-loss child of brackets. -25% from entry.

STOP_LIMIT

  • Semantics: When trigger_price hits, the order becomes a LIMIT at price (not market). Protects against gap slippage at the cost of potential no-fill.
  • Emulatable: yes.
  • Cortana use: not currently. Could replace STOP_MARKET in V2 if slippage on stop-outs becomes a bigger problem than no-fill risk.

MARKET_IF_TOUCHED

  • Semantics: Conditional MARKET entry. When trigger_price is reached (from either side, depending on side), order fires as MARKET. Distinguished from STOP_MARKET by use case: STOP is exit/protection, MIT is entry/initiation.
  • Emulatable: yes.
  • Cortana use: V2 candidate for “enter on a confirming touch.”

LIMIT_IF_TOUCHED

  • Semantics: Conditional LIMIT entry. Trigger fires a LIMIT at price.
  • Emulatable: yes.
  • Cortana use: V2 candidate.

Hybrid / advanced

MARKET_TO_LIMIT

  • Semantics: Submit as MARKET. “If partial fill occurs, remainder resubmits as LIMIT at executed price.” Caps slippage on the partial-fill tail.
  • Cannot be emulated (passes through with MARKET).
  • Cortana use: candidate for the BULL/BEAR initial entry to bound slippage when the impulse engine fires into thin liquidity.

TRAILING_STOP_MARKET

  • Semantics: Stop-loss whose trigger price trails the market by a fixed offset. As price moves favorably, trigger ratchets in. When price reverses by the offset, the trailing stop fires as MARKET.
  • Configuration: trailing_offset (the offset value) + trailing_offset_type (PRICE, BASIS_POINTS, TICKS, PRICE_TIER, DEFAULT).
  • Emulatable: yes. Especially valuable when the venue’s trailing-stop semantics differ from what the strategy assumes.
  • Cortana use: NOT for V1. The mandate is feedback_no_hwm_trailing_language.md
    • single-shot +10% fixed TP, no trailing. This order type is here for completeness, but V1 strategy code should not call trailing_stop_market. V2+ might use it if the trend-day-detection work matures (project_runner_ladder.md).

TRAILING_STOP_LIMIT

  • Semantics: Same as TRAILING_STOP_MARKET but releases as a LIMIT at a configured limit_offset from the trigger.
  • Cortana use: same gating as TRAILING_STOP_MARKET - V2+ only.

Trigger types (for STOP_*, MIT, LIT, trailing stops)

Determine which price feed the trigger evaluates against:

  • LAST_PRICE - last trade
  • BID_ASK - best bid/ask (default for emulation)
  • DOUBLE_LAST - two consecutive last trades (slippage-resistant)
  • DOUBLE_BID_ASK - two consecutive bid/ask updates
  • LAST_OR_BID_ASK - either signal qualifies
  • MID_POINT - bid/ask mid
  • MARK_PRICE - venue mark
  • INDEX_PRICE - underlying index
  • DEFAULT - venue’s default (typically LAST_PRICE)

For options, MARK_PRICE is the saner choice - last-trade can be stale or one-sided in thin contracts (per nautilus-options.md).

Reference: bracket order primitive

Verbatim: “a parent order (entry order) and two child orders: a take-profit LIMIT order and a stop-loss STOP_MARKET order.” Bracket behavior is contingent: “When the parent order executes, the system places the child orders.”

Construction

bracket = self.order_factory.bracket(
    instrument_id=self.instrument_id,
    order_side=OrderSide.BUY,                  # parent side
    quantity=self.instrument.make_qty(qty),
    entry_order_type=OrderType.MARKET,         # or LIMIT, etc.
    tp_price=self.instrument.make_price(tp),   # take-profit limit price
    sl_trigger_price=self.instrument.make_price(sl),  # stop-loss trigger
    # Optional:
    emulation_trigger=TriggerType.MARK_PRICE,  # opt into local emulation
    time_in_force=TimeInForce.DAY,
    contingency_type=ContingencyType.OUO,      # or OCO
)
self.submit_order_list(bracket)

(Exact parameter names verified against OrderFactory.bracket(...) in the spike - the docs reference but do not enumerate the signature; see the python_api/common.html#nautilus_trader.common.factories.OrderFactory page.)

Contingency types

Brackets are an OTO+OCO (or OTO+OUO) construction.

ContingencyMeaning
OTO (One-Triggers-Other)Child orders activate only when parent fills. Two trigger modes: partial (children release pro-rata to each parent partial fill) or full (children release only after parent fully fills). The default backtest venue uses partial-trigger; pass oto_trigger_mode="FULL" for full-trigger.
OCO (One-Cancels-Other)“Two or more linked live orders where executing one cancels the remainder.” When TP fills, SL is canceled (and vice versa).
OUO (One-Updates-Other)“Two or more linked live orders where executing one reduces the open quantity of the remainder.” Useful for partial-fill bracket exits - TP fills 50% → SL is reduced to 50% rather than canceled.

For Cortana V1 (single-shot exits, no scaling out), OCO is sufficient. If V2 introduces scale-out (project_runner_ladder.md), OUO becomes the correct contingency.

Bracket fields shared via order_list_id and parent_order_id

The doc states OCO/OUO groups are “managed via shared order_list_id and parent-child relationships tracked through parent_order_id.” This is how the engine knows which child belongs to which parent for cancel-on-fill and reduce-on-partial logic.

Margin caveat

Verbatim: “You should be aware of the margin requirements of positions, as bracketing a position will consume more order margin.” For long-only 0DTE option buys this is not binding (premium is fully paid up front), but relevant if MK3 ever introduces short-premium structures.

Reference: time-in-force (TIF)

TIFMeaning
GTCGood Till Cancel - active until canceled
GTDGood Till Date - active until specified expiration; emits OrderExpired at expiry
DAYActive until end of trading session
IOCImmediate or Cancel - execute immediately, cancel any unfilled remainder
FOKFill or Kill - execute full quantity or cancel entirely (no partial)
AT_THE_OPENActive only at session open
AT_THE_CLOSEActive only at session close

For Cortana V1 single-shot 0DTE patterns, DAY is the primary TIF - the EOD-flatten invariant means we never want orders surviving past close. A runaway GTC after 16:00 ET would be a P0 invariant violation.

Emulation: the load-bearing concept

This is the section the spike most depends on. Quoting the doc:

“The platform makes it possible to emulate most order types locally, regardless of whether the type is supported on a trading venue.”

“The OrderEmulator will subscribe to any needed market data (if not already) to update the matching core.”

“The only requirement to emulate an order is to pass a TriggerType to the emulation_trigger parameter.”

“Emulation lets you use order types even when your trading venue does not natively support them. Nautilus locally mimics the behavior of these order types … while using only MARKET and LIMIT orders for actual execution on the venue.”

The emulation lifecycle

  1. Strategy submits order with emulation_trigger != NO_TRIGGER.
  2. Order routes to RiskEngine for pre-trade checks (size, notional, etc.).
  3. Order arrives at OrderEmulator; state becomes EMULATED.
  4. OrderEmulator subscribes to the trigger feed (e.g., BID_ASK quotes or MARK_PRICE updates) for that instrument.
  5. The emulator runs a local matching core that watches the feed against the order’s trigger price.
  6. When triggered, the order is transformed to a basic MARKET or LIMIT; emulation_trigger is cleared to NONE; state becomes RELEASED.
  7. The transformed order undergoes a second RiskEngine check.
  8. ExecutionEngine sends the basic order to the venue; normal lifecycle resumes (SUBMITTEDACCEPTEDFILLED).

Two RiskEngine checks (intake + release) is the framework’s belt-and- suspenders. A meta-prob veto rule placed in the RiskEngine fires at both checkpoints - even if conditions changed during the emulation hold.

What’s emulatable

TypeEmulatable?
MARKETNo (passes through)
LIMITYes
STOP_MARKETYes
STOP_LIMITYes
MARKET_IF_TOUCHEDYes
LIMIT_IF_TOUCHEDYes
MARKET_TO_LIMITNo (passes through with MARKET)
TRAILING_STOP_MARKETYes
TRAILING_STOP_LIMITYes

Brackets inherit emulation from their child legs - the LIMIT TP and STOP_MARKET SL are individually emulatable, and the bracket builder threads emulation_trigger through to them.

Emulated-order gotcha (cross-reference nautilus-strategies.md)

Verbatim: “the order object transforms when the emulated order is released.” Implication: the strategy must query emulated orders through the Cache by client_order_id, not by holding a reference. After release, the in-memory shape changes (it’s now a MARKET or LIMIT, not a STOP_LIMIT or trailing stop). Holding stale references leads to attribute errors or subtly wrong state reads.

# WRONG
order = self.order_factory.stop_market(..., emulation_trigger=...)
self.submit_order(order)
# ...later in on_quote_tick:
print(order.trigger_price)   # may not exist anymore!
 
# RIGHT
self._sl_id = order.client_order_id
self.submit_order(order)
# ...later:
order = self.cache.order(self._sl_id)
if order is not None and not order.is_closed:
    ...

Trigger-feed subscription cost

Per the doc, the OrderEmulator subscribes to “any needed market data (if not already).” Two implications:

  1. If the strategy already subscribes to BID_ASK quotes for an instrument, emulation is free (no incremental subscription).
  2. If you emulate against a feed nothing else uses (e.g., INDEX_PRICE), the emulator opens that subscription on demand.

For Cortana, MARK_PRICE emulation on the TP/SL legs of a 0DTE option position is essentially free - we already subscribe to the option chain (per nautilus-options.md), and Greeks updates carry mark.

Reference: order parameters

post_only

Verbatim: “Will only ever participate in providing liquidity to the limit order book, and never initiating a trade which takes liquidity as an aggressor.”

If a post_only LIMIT would cross the book at submission, the venue (or emulator) refuses it. Use to guarantee maker rebates and avoid taker fees. Not relevant to Cortana V1 (we do not chase rebates on options).

reduce_only

Verbatim: “Will only ever reduce an existing position on an instrument and never open a new position (if already flat).”

Critical for Cortana SL orders. A reduce_only=True STOP_MARKET cannot accidentally flip the position from long-call to short-call if a sizing bug or race condition double-submits. The market_exit() helper (see nautilus-strategies.md) automatically applies reduce_only=True to its exit orders, which is what makes the EOD-flatten invariant safe.

display_qty

Iceberg control. display_qty=None means full display. display_qty < quantity exposes only part. display_qty=0 is a hidden order (where the venue supports it). Not relevant to Cortana V1 (SPY 0DTE option spreads are too thin for icebergs to make sense).

exec_algorithm_id + exec_algorithm_params

Per nautilus-strategies.md, you can hand off execution to a custom ExecAlgorithm (e.g., TWAP) by passing exec_algorithm_id=ExecAlgorithmId("TWAP") and a parameter dict. The order routes to the algorithm’s matching core before reaching the venue. The orders page itself does not enumerate these - see the parallel Nautilus Execution page for details.

Not relevant to Cortana V1 (single-shot orders on liquid SPY options). Could become relevant in V2 if we ever want to TWAP a large entry.

emulation_trigger

See the Emulation section above. Values:

ValueMeaning
NO_TRIGGERDisable emulation; submit to venue as-is
DEFAULTSame as BID_ASK
BID_ASKTrigger on best bid/ask
LAST_PRICETrigger on last trade
DOUBLE_LASTTwo consecutive last trades
DOUBLE_BID_ASKTwo consecutive quote updates
LAST_OR_BID_ASKEither signal qualifies
MID_POINTBid/ask mid
MARK_PRICEVenue mark
INDEX_PRICEUnderlying index

contingency_type

OTO / OCO / OUO - see the Bracket section. For a bare order (non-bracket), this is None.

Reference: order modification

self.modify_order(order, new_quantity=...)
self.modify_order(order, new_price=...)
self.modify_order(order, new_trigger_price=...)

Modify request → state transitions to PENDING_UPDATE → venue acks → OrderUpdated event fires → state returns to its prior open state with new fields. If the venue rejects, OrderModifyRejected event fires and state returns to ACCEPTED (or wherever it was) with original fields.

Modifiable fields (the doc references but does not exhaustively enumerate; verify against IBKR adapter on spike):

  • quantity - common
  • price (LIMIT, STOP_LIMIT) - common
  • trigger_price (STOP_*, MIT, LIT) - common
  • expire_time (GTD) - venue-dependent

If the order is already closed (FILLED, CANCELED, REJECTED, EXPIRED, DENIED), modify is a logged no-op.

For a bracket parent modification, the engine cascades: modifying the parent quantity reduces or increases child qty proportionally (OUO) or refuses (OCO single-shot). Verify behavior on spike.

Reference: order cancellation

self.cancel_order(order)                # one order
self.cancel_orders(order_list)          # specific list
self.cancel_all_orders()                # all open + emulated
# Optional filters: instrument_id=..., side=...

Cancel request → state transitions to PENDING_CANCEL → venue acks → OrderCanceled event fires → terminal state CANCELED. If the venue rejects (already filled, unknown ID, etc.), OrderCancelRejected fires and state returns to its prior state.

Bracket cancellation cascades: canceling a parent cancels both children. Canceling one child of an OCO pair is venue-dependent - most venues will let you cancel a single OCO leg, leaving the other live; others reject. Verify on spike.

The market_exit() helper (see nautilus-strategies.md) cancels all open and in-flight orders before submitting reduce-only market closes. Verbatim from that doc: “During a market exit, non-reduce-only orders are automatically denied.” This is what makes EOD-flat safe - no in-flight on_data can sneak a new entry past the cancel sweep.

Reference: order state machine

Full state set (from the doc verbatim):

StatusClassDescription
INITIALIZEDLocalOrder instantiated within Nautilus
EMULATEDLocalHeld by OrderEmulator for local trigger
RELEASEDLocalReleased from emulator, awaiting submission
DENIEDTerminalRefused by Nautilus (RiskEngine, invariant)
SUBMITTEDIn-flightSent to venue, awaiting ack
ACCEPTEDOpenAcked by venue
REJECTEDTerminalRefused by venue
TRIGGEREDOpenSTOP price hit on venue (venue-side trigger)
PENDING_UPDATEOpenModify request in-flight
PENDING_CANCELOpenCancel request in-flight
PARTIALLY_FILLEDOpenPartial fill received
FILLEDTerminalFully filled
CANCELEDTerminalCancel confirmed
EXPIREDTerminalTIF (GTD/DAY/IOC/FOK) elapsed without fill

State classes:

  • Local (INITIALIZED, EMULATED, RELEASED) - order has not yet been transmitted to the venue.
  • In-flight (SUBMITTED, PENDING_UPDATE, PENDING_CANCEL) - request has been sent but ack not yet received.
  • Open (ACCEPTED, TRIGGERED, PARTIALLY_FILLED, plus the in-flight states) - order is live at the venue.
  • Terminal (DENIED, REJECTED, CANCELED, EXPIRED, FILLED) - order is finished; no further events possible.

Each state corresponds to an event

The full event-event-handler mapping is in nautilus-events.md. Key event ↔ state correspondences:

State entryEvent
INITIALIZEDOrderInitialized
DENIEDOrderDenied
EMULATEDOrderEmulated
RELEASEDOrderReleased
SUBMITTEDOrderSubmitted
ACCEPTEDOrderAccepted
REJECTEDOrderRejected
TRIGGEREDOrderTriggered
PENDING_UPDATEOrderPendingUpdate
PENDING_CANCELOrderPendingCancel
PARTIALLY_FILLED / FILLEDOrderFilled
CANCELEDOrderCanceled
EXPIREDOrderExpired

Plus modify/cancel-rejection events that don’t drive state transitions but emit telemetry: OrderModifyRejected, OrderCancelRejected, OrderUpdated.

Cortana MK3 implications

MK2 → Nautilus bracket-exit pattern

MK2 today: market entry (IBKR), then PM places bracket TP (LMT +10%) and SL (STP -25%) at IBKR after fill confirmation, then position_manager runs an on_quote_tick software fallback for both legs in case the broker LMT misses. This is the dual-TP defense-in-depth pattern from feedback_dual_tp_defense_in_depth.md.

MK3 in Nautilus terms - single line of code at entry decision:

bracket = self.order_factory.bracket(
    instrument_id=chosen_call_id,
    order_side=OrderSide.BUY,
    quantity=self.instrument.make_qty(contracts),
    entry_order_type=OrderType.MARKET,
    tp_price=self.instrument.make_price(entry * 1.10),
    sl_trigger_price=self.instrument.make_price(entry * 0.75),
    # The dual-TP defense-in-depth: emulate locally so Nautilus
    # owns the trigger logic, not just IBKR.
    emulation_trigger=TriggerType.MARK_PRICE,
    time_in_force=TimeInForce.DAY,
    contingency_type=ContingencyType.OCO,
)
self.submit_order_list(bracket)

What this gives us, mapped against the MK2 invariants:

MK2 invariant / failure modeMK3 Nautilus answer
feedback_dual_tp_defense_in_depth - TP must have software fallback when broker LMT failsemulation_trigger=MARK_PRICE makes Nautilus the fallback. The OrderEmulator subscribes to mark, watches it locally, fires a basic LIMIT (or MARKET) when triggered. Independent of IBKR’s bracket.
feedback_no_hwm_trailing_language - single-shot +10% fixed TP, no trailingtp_price=entry*1.10 as a LIMIT child. We do not use TRAILING_STOP_* types.
feedback_no_kill_with_open_positions - never kill engine while position openmarket_exit() helper cancels all open + in-flight, then submits reduce-only closes. The manage_stop=True config makes this automatic on Strategy.stop().
project_pm_ibkr_exit_invariant - PM exit intent → SELL at IBKR → position actually closesBelt-and-suspenders: bracket child placed at IBKR (OCO), AND emulator watches mark locally, AND LiveExecutionEngine.reconciliation continuously aligns cached state with broker truth. Three layers.
feedback_dual_tp_defense_in_depth - TP must have SW fallback(See above)
Stale-mark-price bug (2026-05-06)Cache-then-publish ordering for QuoteTick/TradeTick/Bar means the emulator’s read of mark is always the value that triggered the dispatch. No stale-cache window.

What we delete from MK2

  • The position_manager.on_quote_tick software-fallback loop for TP/SL. The OrderEmulator owns this.
  • The hand-rolled oca_group IBKR tagging. Nautilus’s OCO contingency is the framework primitive.
  • The retry-loop on cancel (when broker rejects a cancel, MK2 retries N times). OrderCancelRejected → strategy decides; the framework does not retry blindly.
  • The reduce-only flag bookkeeping. Pass reduce_only=True on the SL child once; the framework propagates.

What we keep / add

  • The +10% / -25% magnitudes. These are strategy decisions, not framework decisions.
  • A custom RiskEngine rule that vetoes any bracket whose (tp_price - entry_price) is not ≈ +10% (defensive sanity check against accidental config drift).
  • An AuditLogger Actor (per nautilus-events.md) that subscribes to events.* and writes Parquet - captures the bracket parent + both children + every state transition + the eventual fills, without Cortana code touching a database.

Open questions for Saturday’s spike

  1. Bracket emulation propagation: when emulation_trigger is set on order_factory.bracket(...), does it apply to (a) the parent only, (b) the children only, (c) all three? Verify in the OrderFactory.bracket(...) source. Docs do not explicitly say.
  2. OCO leg-by-leg cancel on IBKR: when the SL fires and IBKR is the venue, does Nautilus cancel the TP leg automatically (OCO contract honored), or do we need the emulator’s local OCO logic?
  3. Mid-emulation modify: can we modify the TP price while the bracket is in EMULATED state (e.g., to bump TP from +10% to +15% if the trend-day detector fires)? The docs say emulated orders “can be modified … and updated” - verify behavior.
  4. oto_trigger_mode default for IBKR: is it PARTIAL or FULL? For Cortana 0DTE single-fill entries this rarely matters (the parent either fills fully or rejects), but partial fills on illiquid strikes could leave half a SL active.
  5. Bracket parent modification: can we cancel just the bracket parent without cascading to children? Use case: change-of-mind on entry but still want children in a manual-entry path. Probably not, but verify.
  6. Reconciliation behavior for emulated orders: on engine restart mid-trade, does LiveExecutionEngine.reconcile_state() correctly resume an EMULATED-state order, or are emulated orders dropped on restart? Check ~/brain/concepts/nautilus-concepts.md Reconciliation section alongside.

Caveats and gotchas

  • Emulation is opt-in. A bracket without emulation_trigger is IBKR-bracket-only. No SW fallback. Make the choice deliberate.
  • Object reference invalidates on release. Always query emulated orders through self.cache.order(client_order_id), never hold a Python reference past OrderEmulated event.
  • Two RiskEngine checks for emulated orders (intake + release). A custom Risk rule that vetoes based on stale data could fail twice for the same order. Idempotency matters.
  • MARKET and MARKET_TO_LIMIT cannot be emulated. If you pass emulation_trigger on a MARKET, the framework either ignores it or rejects - verify.
  • Trailing stops are emulatable but Cortana V1 must not use them. The mandate is single-shot fixed TP. Keep the trailing_* order factory methods out of CortanaStrategy.
  • AT_THE_OPEN / AT_THE_CLOSE TIF is venue-dependent. IBKR supports MOC/MOO orders separately from these TIFs; verify the mapping.
  • Bracket OCO behavior on partial fills is contingency-type-specific. OCO with a partial fill leaves the unfilled remainder of the other leg active at full size - wrong for a Cortana exit. Use OUO for scale-out semantics; OCO is fine for single-shot all-or-nothing.

When this concept applies

  • Designing the MK3 entry → bracket pipeline.
  • Choosing emulated vs venue-native exits (load-bearing decision).
  • Implementing the dual-TP defense-in-depth pattern.
  • Enumerating order states for the audit-trail subscriber.
  • Authoring custom RiskEngine rules that gate specific order types.

When it breaks / does not apply

  • Combo orders / multi-leg spreads. OptionSpread is a separate primitive (see nautilus-options.md); the bracket primitive does not cover spread legs. V2+ structural plays will need a different shape.
  • OTC / RFQ orders. The order taxonomy is exchange-style; request-for-quote workflows are not modeled here.
  • Order types unique to a single venue (e.g., IBKR’s IBOrderTags.algoStrategy="Adaptive"). These flow through IBKR-adapter-specific tags parameters, not the standard order types. See nautilus-integrations.md.

See Also

  • Nautilus Strategies - submit_order / OrderFactory overview, market_exit helper, RiskEngine integration
  • Nautilus Events - 17 order-lifecycle events that pair with each state transition
  • Nautilus Execution (parallel) - ExecutionEngine, ExecutionClient, exec algorithms (TWAP)
  • Nautilus Options - option-instrument context for V1 single-leg flow; chain-driven strike selection feeds into the bracket order
  • Nautilus Value Types - Price.make_price, Quantity.make_qty, the precision contracts that order-factory parameters must honor
  • Nautilus Concepts - Kernel, MessageBus, Cache, ExecutionEngine, RiskEngine canon
  • Nautilus Integrations - IBKR adapter capabilities, IBOrderTags, native bracket support
  • 2026-05-09 Nautilus Spike Plan: ~/conductor/workspaces/cortanaroi-mk2/belo-horizonte/plans/2026-05-09-nautilus-spike.md
    • Step 4 / Step 5 implementation reference, spike question #6
  • ~/.claude/projects/.../memory/feedback_dual_tp_defense_in_depth.md
    • TP must have software fallback when broker LMT fails
  • ~/.claude/projects/.../memory/feedback_no_hwm_trailing_language.md
    • single-shot +10% fixed TP, no trailing
  • ~/.claude/projects/.../memory/project_pm_ibkr_exit_invariant.md
    • PM exit intent → SELL at IBKR → position actually closes

Timeline

  • 2026-05-07 | Cody - Filed during pre-spike concept mastery sweep batch 2.