NautilusTrader
Developer Guide

Execution Testing Spec

This section defines a rigorous test matrix for validating adapter execution functionality using the ExecTester strategy. Both Python (nautilus_trader.test_kit.strategies.tester_exec) and Rust (nautilus_testkit::testers) provide the ExecTester. Each test case is identified by a prefixed ID (e.g. TC-E01) and grouped by functionality.

Each adapter must pass the subset of tests matching its supported capabilities.

Tests progress from simple (single market order) to complex (brackets, modification chains, rejection handling). An adapter that passes groups 1–5 is considered baseline compliant. Data connectivity should be verified first using the Data Testing Spec.

Document adapter-specific behavior (how a venue simulates market orders, handles TIF options, etc.) in the adapter's own guide, not here. Each adapter guide should include a capability matrix showing which order types, time-in-force options, actions, and flags it supports.

Prerequisites

Before running execution tests:

  • Demo/testnet account with valid API credentials (preferred, not required).
  • Account funded with sufficient margin for the test instrument and quantities.
  • Target instrument available and loadable via the instrument provider.
  • Environment variables set: {VENUE}_API_KEY, {VENUE}_API_SECRET (or sandbox variants).
  • If the venue offers a demo/testnet mode (e.g. is_demo=True), use credentials created for that environment. Demo and production API keys are typically separate and not interchangeable; using the wrong credentials produces authentication errors (e.g. HTTP 401).
  • Risk engine bypassed (LiveRiskEngineConfig(bypass=True)) to avoid interference.
  • Reconciliation enabled to verify state consistency.

Python node setup (reference: examples/live/{adapter}/{adapter}_exec_tester.py):

from nautilus_trader.live.node import TradingNode
from nautilus_trader.test_kit.strategies.tester_exec import ExecTester, ExecTesterConfig

node = TradingNode(config=config_node)
strategy = ExecTester(config=config_tester)
node.trader.add_strategy(strategy)
# Register adapter factories, build, and run

Rust node setup (reference: crates/adapters/{adapter}/examples/node_exec_tester.rs):

use nautilus_testkit::testers::{ExecTester, ExecTesterConfig};

let tester_config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, order_qty);
let tester = ExecTester::new(tester_config);
node.add_strategy(tester)?;
node.run().await?;

Basic smoke test

A quick sanity check that can run at any time, for example after adapter changes or between development iterations. The tester opens a position with a market order on start, places a buy and sell post-only limit order, waits 30 seconds, then stops (cancelling open orders and closing the position).

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.001"),
    open_position_on_start_qty=Decimal("0.001"),
    enable_limit_buys=True,
    enable_limit_sells=True,
    use_post_only=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, dec!(0.001))
    .with_open_position_on_start_qty(Some(dec!(0.001)))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(true)
    .with_use_post_only(true)

Expected behavior:

  1. On start: market order fills, opening a position.
  2. Two limit orders placed at tob_offset_ticks away from best bid/ask (default 500 ticks).
  3. Strategy idles for 30 seconds. Check logs for errors, rejected orders, or disconnections.
  4. On stop: open limit orders cancelled, position closed with a market order.

Pass criteria: No errors in logs, position opened and closed cleanly, limit orders acknowledged by the venue.


Each group below begins with a summary table, followed by detailed test cards. Test IDs use spaced numbering to allow insertion without renumbering.


Group 1: Market orders

Test market order submission and fills. Market orders should execute immediately.

TCNameDescriptionSkip when
TC-E01Market BUY - submit and fillOpen long position via market buy.No market orders.
TC-E02Market SELL - submit and fillOpen short position via market sell.No market orders.
TC-E03Market order with IOC TIFMarket order explicitly using IOC time in force.No IOC.
TC-E04Market order with FOK TIFMarket order explicitly using FOK time in force.No FOK.
TC-E05Market order with quote qtyMarket order using quote currency quantity.No quote quantity.
TC-E06Close position via marketClose an open position with a market order on stop.No market orders.

TC-E01: Market BUY - submit and fill

FieldValue
PrerequisiteAdapter connected, instrument loaded, market data flowing, no open position.
ActionExecTester opens a long position via open_position_on_start_qty.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaPosition opened with side=LONG, quantity matches config, fill price within market range, AccountState updated.
Skip whenAdapter does not support market orders.

Considerations:

  • Some adapters simulate market orders as aggressive limit IOC orders (check adapter guide).
  • The event sequence from the strategy's perspective should be identical regardless of the venue mechanism.
  • Fill price should be within the recent bid/ask spread.
  • Partial fills are valid; verify the cumulative filled quantity matches the order quantity.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    open_position_on_start_qty=Decimal("0.01"),
    enable_limit_buys=False,
    enable_limit_sells=False,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_open_position_on_start(Some(Decimal::new(1, 2)))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)

TC-E02: Market SELL - submit and fill

FieldValue
PrerequisiteAdapter connected, instrument loaded, market data flowing, no open position.
ActionExecTester opens a short position via negative open_position_on_start_qty.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaPosition opened with side=SHORT, quantity matches config, fill price within market range.
Skip whenAdapter does not support market orders or short selling.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    open_position_on_start_qty=Decimal("-0.01"),
    enable_limit_buys=False,
    enable_limit_sells=False,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_open_position_on_start(Some(Decimal::new(-1, 2)))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)

TC-E03: Market order with IOC TIF

FieldValue
PrerequisiteAdapter connected, instrument loaded, market data flowing.
ActionOpen position with open_position_time_in_force=IOC.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaSame as TC-E01; the IOC TIF is explicitly set on the order.
Skip whenNo IOC support.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    open_position_on_start_qty=Decimal("0.01"),
    open_position_time_in_force=TimeInForce.IOC,
    enable_limit_buys=False,
    enable_limit_sells=False,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_open_position_on_start(Some(Decimal::new(1, 2)))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false);
config.open_position_time_in_force = TimeInForce::Ioc;

TC-E04: Market order with FOK TIF

FieldValue
PrerequisiteAdapter connected, instrument loaded, market data flowing.
ActionOpen position with open_position_time_in_force=FOK.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaSame as TC-E01; the FOK TIF is explicitly set on the order.
Skip whenNo FOK support.

Considerations:

  • FOK requires the entire quantity to be fillable immediately or the order is canceled.
  • Use small test quantities so book depth is sufficient for a complete fill.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    open_position_on_start_qty=Decimal("0.01"),
    open_position_time_in_force=TimeInForce.FOK,
    enable_limit_buys=False,
    enable_limit_sells=False,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_open_position_on_start(Some(Decimal::new(1, 2)))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false);
config.open_position_time_in_force = TimeInForce::Fok;

TC-E05: Market order with quote quantity

FieldValue
PrerequisiteAdapter connected, instrument loaded, adapter supports quote quantity.
ActionOpen position with use_quote_quantity=True, quantity in quote currency.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaOrder submitted with quote currency quantity; fill quantity is in base currency.
Skip whenAdapter does not support quote quantity orders.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("100.0"),  # Quote currency amount
    open_position_on_start_qty=Decimal("100.0"),
    use_quote_quantity=True,
    enable_limit_buys=False,
    enable_limit_sells=False,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("100"))
    .with_open_position_on_start(Some(Decimal::from(100)))
    .with_use_quote_quantity(true)
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)

TC-E06: Close position via market order on stop

FieldValue
PrerequisiteOpen position from TC-E01 or TC-E02.
ActionStop the strategy; ExecTester closes position via market order.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled (closing order).
Pass criteriaPosition closed (net quantity = 0), no open orders remaining.
Skip whenAdapter does not support market orders.

Considerations:

  • This test naturally follows TC-E01 or TC-E02 as part of the same session.
  • close_positions_on_stop=True is the default.
  • The closing order should be on the opposite side of the position.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    open_position_on_start_qty=Decimal("0.01"),
    close_positions_on_stop=True,
    enable_limit_buys=False,
    enable_limit_sells=False,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_open_position_on_start(Some(Decimal::new(1, 2)))
    .with_close_positions_on_stop(true)
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)

Group 2: Limit orders

Test limit order submission, acceptance, and behavior across time-in-force options.

TCNameDescriptionSkip when
TC-E10Limit BUY GTCPlace GTC limit buy below TOB, verify accepted.Never.
TC-E11Limit SELL GTCPlace GTC limit sell above TOB, verify accepted.Never.
TC-E12Limit BUY and SELL pairBoth sides simultaneously, verify both accepted.Never.
TC-E13Limit IOC aggressive fillLimit IOC at aggressive price, expect fill.No IOC.
TC-E14Limit IOC passive no fillLimit IOC away from market, expect cancel.No IOC.
TC-E15Limit FOK fillLimit FOK at aggressive price, expect fill.No FOK.
TC-E16Limit FOK no fillLimit FOK away from market, expect cancel.No FOK.
TC-E17Limit GTDLimit with expiry time, verify accepted.No GTD.
TC-E18Limit GTD expiryWait for GTD expiry, verify OrderExpired.No GTD.
TC-E19Limit DAYLimit with DAY TIF, verify accepted.No DAY.

TC-E10: Limit BUY GTC - submit and accept

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places a limit buy at best_bid - tob_offset_ticks.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOrder is open on the venue with correct price, quantity, side=BUY, TIF=GTC.
Skip whenNever.

Considerations:

  • The tob_offset_ticks (default 500) places the order well away from the market to avoid accidental fills.
  • Verify the order appears in the cache with OrderStatus.ACCEPTED.
  • The order should remain open until explicitly canceled.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=False,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false)

TC-E11: Limit SELL GTC - submit and accept

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places a limit sell at best_ask + tob_offset_ticks.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOrder is open on the venue with correct price, quantity, side=SELL, TIF=GTC.
Skip whenNever.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=False,
    enable_limit_sells=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(true)

TC-E12: Limit BUY and SELL pair

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places both a limit buy and limit sell.
Event sequenceTwo independent sequences: each OrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaBoth orders open on venue, buy below bid, sell above ask.
Skip whenNever.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(true)

TC-E13: Limit IOC aggressive fill

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionSubmit a limit buy IOC at or above the best ask (aggressive price).
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaOrder fills immediately; position opened.
Skip whenAdapter does not support IOC TIF.

Considerations:

  • This test requires manual order creation or adapter-specific configuration, as the ExecTester's default limit order placement uses GTC TIF.
  • IOC orders that don't fill immediately are canceled by the venue.

TC-E14: Limit IOC passive - no fill

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionSubmit a limit buy IOC well below the market (passive price).
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderCanceled.
Pass criteriaOrder is immediately canceled by venue with no fill.
Skip whenAdapter does not support IOC TIF.

Considerations:

  • The venue should cancel the unfilled IOC order; verify OrderCanceled event (not OrderExpired).

TC-E15: Limit FOK fill

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing, sufficient book depth.
ActionSubmit a limit buy FOK at aggressive price with quantity within top-of-book depth.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaOrder fills completely in a single fill event.
Skip whenAdapter does not support FOK TIF.

Considerations:

  • FOK requires the entire quantity to be fillable; use small quantities so book depth is sufficient.

TC-E16: Limit FOK no fill

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionSubmit a limit buy FOK at passive price (well below market).
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderCanceled.
Pass criteriaOrder is immediately canceled by venue with no fill.
Skip whenAdapter does not support FOK TIF.

TC-E17: Limit GTD - submit and accept

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionPlace limit buy with order_expire_time_delta_mins set (e.g., 60 minutes).
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOrder accepted with GTD TIF and correct expiry timestamp.
Skip whenAdapter does not support GTD TIF.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    order_expire_time_delta_mins=60,
    enable_limit_buys=True,
    enable_limit_sells=False,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false);
config.order_expire_time_delta_mins = Some(60);

TC-E18: Limit GTD expiry

FieldValue
PrerequisiteOpen GTD limit order from TC-E17 (or use a very short expiry).
ActionWait for the GTD expiry time to elapse.
Event sequenceOrderExpired.
Pass criteriaOrder transitions to expired status; OrderExpired event received.
Skip whenAdapter does not support GTD TIF.

Considerations:

  • Use a short order_expire_time_delta_mins (e.g., 1–2 minutes) to avoid long waits.
  • Some venues may report expiry as a cancel; verify the adapter maps this to OrderExpired.

TC-E19: Limit DAY - submit and accept

FieldValue
PrerequisiteAdapter connected, instrument loaded, market is in trading hours.
ActionSubmit limit buy with DAY TIF.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOrder accepted with DAY TIF; will be automatically canceled at end of trading day.
Skip whenAdapter does not support DAY TIF.

Considerations:

  • DAY orders may behave differently on 24/7 crypto venues vs traditional markets.
  • Verify behavior when submitted outside trading hours (if applicable).

Group 3: Stop and conditional orders

Test stop and conditional order types. These orders rest on the venue until a trigger condition is met.

TCNameDescriptionSkip when
TC-E20StopMarket BUYStop buy above ask, verify accepted.No STOP_MARKET.
TC-E21StopMarket SELLStop sell below bid, verify accepted.No STOP_MARKET.
TC-E22StopLimit BUYStop-limit buy with trigger + limit price.No STOP_LIMIT.
TC-E23StopLimit SELLStop-limit sell with trigger + limit price.No STOP_LIMIT.
TC-E24MarketIfTouched BUYMIT buy below bid.No MIT.
TC-E25MarketIfTouched SELLMIT sell above ask.No MIT.
TC-E26LimitIfTouched BUYLIT buy with trigger + limit price.No LIT.
TC-E27LimitIfTouched SELLLIT sell with trigger + limit price.No LIT.

TC-E20: StopMarket BUY

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places a stop-market buy above the current ask.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaStop order accepted on venue with correct trigger price and side=BUY.
Skip whenAdapter does not support StopMarket orders.

Considerations:

  • The trigger price should be above the current ask by stop_offset_ticks.
  • The order should NOT trigger immediately (trigger price is above market).
  • Verifying trigger and fill requires the market to move, which may not happen during the test.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=False,
    enable_limit_sells=False,
    enable_stop_buys=True,
    enable_stop_sells=False,
    stop_order_type=OrderType.STOP_MARKET,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)
    .with_enable_stop_buys(true)
    .with_enable_stop_sells(false)
    .with_stop_order_type(OrderType::StopMarket)

TC-E21: StopMarket SELL

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places a stop-market sell below the current bid.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaStop order accepted on venue with correct trigger price and side=SELL.
Skip whenAdapter does not support StopMarket orders.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=False,
    enable_limit_sells=False,
    enable_stop_buys=False,
    enable_stop_sells=True,
    stop_order_type=OrderType.STOP_MARKET,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)
    .with_enable_stop_buys(false)
    .with_enable_stop_sells(true)
    .with_stop_order_type(OrderType::StopMarket)

TC-E22: StopLimit BUY

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places a stop-limit buy with trigger price above ask and limit offset.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaStop-limit order accepted with correct trigger price, limit price, and side=BUY.
Skip whenAdapter does not support StopLimit orders.

Considerations:

  • Requires stop_limit_offset_ticks to be set for the limit price offset from the trigger price.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=False,
    enable_limit_sells=False,
    enable_stop_buys=True,
    enable_stop_sells=False,
    stop_order_type=OrderType.STOP_LIMIT,
    stop_limit_offset_ticks=50,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)
    .with_enable_stop_buys(true)
    .with_enable_stop_sells(false)
    .with_stop_order_type(OrderType::StopLimit);
config.stop_limit_offset_ticks = Some(50);

TC-E23: StopLimit SELL

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places a stop-limit sell with trigger price below bid.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaStop-limit order accepted with correct trigger price, limit price, and side=SELL.
Skip whenAdapter does not support StopLimit orders.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=False,
    enable_limit_sells=False,
    enable_stop_buys=False,
    enable_stop_sells=True,
    stop_order_type=OrderType.STOP_LIMIT,
    stop_limit_offset_ticks=50,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)
    .with_enable_stop_buys(false)
    .with_enable_stop_sells(true)
    .with_stop_order_type(OrderType::StopLimit);
config.stop_limit_offset_ticks = Some(50);

TC-E24: MarketIfTouched BUY

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionPlace MIT buy with trigger below current bid (buy on dip).
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaMIT order accepted on venue with correct trigger price.
Skip whenAdapter does not support MarketIfTouched orders.

TC-E25: MarketIfTouched SELL

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionPlace MIT sell with trigger above current ask (sell on rally).
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaMIT order accepted on venue with correct trigger price.
Skip whenAdapter does not support MarketIfTouched orders.

TC-E26: LimitIfTouched BUY

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionPlace LIT buy with trigger below bid and limit price offset.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaLIT order accepted with correct trigger price and limit price.
Skip whenAdapter does not support LimitIfTouched orders.

TC-E27: LimitIfTouched SELL

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionPlace LIT sell with trigger above ask and limit price offset.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaLIT order accepted with correct trigger price and limit price.
Skip whenAdapter does not support LimitIfTouched orders.

Group 4: Order modification

Test order modification (amend) and cancel-replace workflows.

TCNameDescriptionSkip when
TC-E30Modify limit BUY priceAmend open limit buy to new price.No modify support.
TC-E31Modify limit SELL priceAmend open limit sell to new price.No modify support.
TC-E32Cancel-replace limit BUYCancel and resubmit limit buy at new price.Never.
TC-E33Cancel-replace limit SELLCancel and resubmit limit sell at new price.Never.
TC-E34Modify stop trigger priceAmend stop order trigger price.No modify or no stop.
TC-E35Cancel-replace stop orderCancel and resubmit stop at new trigger price.No stop orders.
TC-E36Modify rejectedModify on unsupported adapter.Adapter supports modify.

TC-E30: Modify limit BUY price

FieldValue
PrerequisiteOpen GTC limit buy from TC-E10.
ActionExecTester modifies limit buy to a new price as market moves (modify_orders_to_maintain_tob_offset=True).
Event sequenceOrderPendingUpdateOrderUpdated.
Pass criteriaOrder price updated on venue; OrderUpdated event contains new price.
Skip whenAdapter does not support order modification.

Considerations:

  • Requires market movement to trigger the ExecTester's order maintenance logic.
  • The modify is triggered when the order price drifts from the target TOB offset.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=False,
    modify_orders_to_maintain_tob_offset=True,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false);
config.modify_orders_to_maintain_tob_offset = true;

TC-E31: Modify limit SELL price

FieldValue
PrerequisiteOpen GTC limit sell from TC-E11.
ActionExecTester modifies limit sell to new price as market moves.
Event sequenceOrderPendingUpdateOrderUpdated.
Pass criteriaOrder price updated on venue; OrderUpdated event contains new price.
Skip whenAdapter does not support order modification.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=False,
    enable_limit_sells=True,
    modify_orders_to_maintain_tob_offset=True,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(true);
config.modify_orders_to_maintain_tob_offset = true;

TC-E32: Cancel-replace limit BUY

FieldValue
PrerequisiteOpen GTC limit buy.
ActionExecTester cancels and resubmits limit buy at new price as market moves.
Event sequenceOrderPendingCancelOrderCanceledOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOriginal order canceled, new order accepted at updated price.
Skip whenNever (cancel-replace is always available).

Considerations:

  • This is the universal alternative when the adapter does not support native modify.
  • Two distinct orders in the cache: the canceled original and the new replacement.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=False,
    cancel_replace_orders_to_maintain_tob_offset=True,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false);
config.cancel_replace_orders_to_maintain_tob_offset = true;

TC-E33: Cancel-replace limit SELL

FieldValue
PrerequisiteOpen GTC limit sell.
ActionExecTester cancels and resubmits limit sell at new price.
Event sequenceOrderPendingCancelOrderCanceledOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOriginal order canceled, new order accepted at updated price.
Skip whenNever.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=False,
    enable_limit_sells=True,
    cancel_replace_orders_to_maintain_tob_offset=True,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(true);
config.cancel_replace_orders_to_maintain_tob_offset = true;

TC-E34: Modify stop trigger price

FieldValue
PrerequisiteOpen stop order from TC-E20 or TC-E22.
ActionExecTester modifies stop trigger price as market moves (modify_stop_orders_to_maintain_offset=True).
Event sequenceOrderPendingUpdateOrderUpdated.
Pass criteriaStop order trigger price updated on venue.
Skip whenAdapter does not support modify, or no stop order support.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_stop_buys=True,
    modify_stop_orders_to_maintain_offset=True,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_stop_buys(true);
config.modify_stop_orders_to_maintain_offset = true;

TC-E35: Cancel-replace stop order

FieldValue
PrerequisiteOpen stop order.
ActionExecTester cancels and resubmits stop at new trigger price.
Event sequenceOrderPendingCancelOrderCanceledOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOriginal stop canceled, new stop accepted at updated trigger price.
Skip whenNo stop order support.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_stop_buys=True,
    cancel_replace_stop_orders_to_maintain_offset=True,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_stop_buys(true);
config.cancel_replace_stop_orders_to_maintain_offset = true;

TC-E36: Modify rejected

FieldValue
PrerequisiteOpen limit order, adapter does NOT support modify.
ActionAttempt to modify the order (programmatically, not via ExecTester auto-maintain).
Event sequenceOrderModifyRejected.
Pass criteriaModify attempt results in OrderModifyRejected event with reason; original order remains unchanged.
Skip whenAdapter supports order modification.

Considerations:

  • This tests the adapter's rejection path, not the ExecTester's cancel-replace logic.
  • The rejection reason should indicate that modification is not supported.

Group 5: Order cancellation

Test order cancellation workflows.

TCNameDescriptionSkip when
TC-E40Cancel single limit orderCancel an open limit order.Never.
TC-E41Cancel all on stopStrategy stop cancels all open orders (default).Never.
TC-E42Individual cancels on stopCancel orders one-by-one on stop.Never.
TC-E43Batch cancel on stopCancel orders via batch API on stop.No batch cancel.
TC-E44Cancel already-canceledCancel a non-open order.Never.

TC-E40: Cancel single limit order

FieldValue
PrerequisiteOpen GTC limit order from TC-E10 or TC-E11.
ActionStop the strategy; ExecTester cancels the open limit order.
Event sequenceOrderPendingCancelOrderCanceled.
Pass criteriaOrder status transitions to CANCELED; no open orders remaining.
Skip whenNever.

Considerations:

  • cancel_orders_on_stop=True (default) triggers cancellation when the strategy stops.
  • Verify the OrderCanceled event contains the correct venue_order_id.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=False,
    cancel_orders_on_stop=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false)
    .with_cancel_orders_on_stop(true)

TC-E41: Cancel all on stop

FieldValue
PrerequisiteMultiple open orders (limit buy + limit sell from TC-E12).
ActionStop the strategy with cancel_orders_on_stop=True (default).
Event sequenceFor each order: OrderPendingCancelOrderCanceled.
Pass criteriaAll open orders canceled; no open orders remaining.
Skip whenNever.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=True,
    cancel_orders_on_stop=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(true)
    .with_cancel_orders_on_stop(true)

TC-E42: Individual cancels on stop

FieldValue
PrerequisiteMultiple open orders.
ActionStop with use_individual_cancels_on_stop=True.
Event sequenceIndividual OrderPendingCancelOrderCanceled for each order.
Pass criteriaEach order canceled individually; all orders reach CANCELED status.
Skip whenNever.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=True,
    use_individual_cancels_on_stop=True,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(true);
config.use_individual_cancels_on_stop = true;

TC-E43: Batch cancel on stop

FieldValue
PrerequisiteMultiple open orders, adapter supports batch cancel.
ActionStop with use_batch_cancel_on_stop=True.
Event sequenceBatch OrderPendingCancelOrderCanceled for all orders.
Pass criteriaAll orders canceled via single batch request; all reach CANCELED status.
Skip whenAdapter does not support batch cancel.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=True,
    use_batch_cancel_on_stop=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(true)
    .with_use_batch_cancel_on_stop(true)

TC-E44: Cancel already-canceled order

FieldValue
PrerequisiteA previously canceled order (from TC-E40).
ActionAttempt to cancel the same order again.
Event sequenceOrderCancelRejected.
Pass criteriaCancel attempt is rejected; OrderCancelRejected event received with reason.
Skip whenNever.

Considerations:

  • This tests the adapter's error handling for invalid cancel requests.
  • The rejection reason should indicate the order is not in a cancelable state.

Group 6: Bracket orders

Test bracket order submission (entry + take-profit + stop-loss).

TCNameDescriptionSkip when
TC-E50Bracket BUYEntry limit buy + TP limit sell + SL stop sell.No bracket support.
TC-E51Bracket SELLEntry limit sell + TP limit buy + SL stop buy.No bracket support.
TC-E52Bracket entry fill activatesVerify TP/SL become active after entry fill.No bracket support.
TC-E53Bracket with post-only entryEntry order uses post-only flag.No bracket or PO.

TC-E50: Bracket BUY

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester submits a bracket order: limit buy entry + take-profit sell + stop-loss sell.
Event sequenceEntry: OrderInitializedOrderSubmittedOrderAccepted; TP and SL: OrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaThree orders created and accepted: entry below bid, TP above ask, SL below entry.
Skip whenAdapter does not support bracket orders.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_brackets=True,
    bracket_entry_order_type=OrderType.LIMIT,
    bracket_offset_ticks=500,
    enable_limit_buys=True,
    enable_limit_sells=False,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_brackets(true)
    .with_bracket_entry_order_type(OrderType::Limit)
    .with_bracket_offset_ticks(500)
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false)

TC-E51: Bracket SELL

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester submits bracket: limit sell entry + TP buy + SL buy.
Event sequenceSame pattern as TC-E50 but for sell side.
Pass criteriaThree orders created and accepted on sell side.
Skip whenAdapter does not support bracket orders.

TC-E52: Bracket entry fill activates TP/SL

FieldValue
PrerequisiteBracket order from TC-E50 where entry order fills.
ActionEntry order fills; verify contingent TP and SL orders activate.
Event sequenceEntry: OrderFilled; TP and SL transition from contingent to active.
Pass criteriaAfter entry fill, TP and SL orders are live on the venue.
Skip whenAdapter does not support bracket orders.

Considerations:

  • This requires the entry order to actually fill, which may need aggressive pricing.
  • The TP/SL activation mechanism varies by venue (some activate immediately, some are OCA groups).

TC-E53: Bracket with post-only entry

FieldValue
PrerequisiteAdapter supports brackets and post-only.
ActionSubmit bracket with use_post_only=True (applied to entry and TP).
Event sequenceSame as TC-E50 with post-only flag on entry.
Pass criteriaEntry and TP orders accepted as post-only (maker); SL is not post-only.
Skip whenNo bracket support or no post-only support.

Group 7: Order flags

Test order-level flags and special parameters.

TCNameDescriptionSkip when
TC-E60PostOnly acceptedLimit with post-only, placed away from TOB.No post-only.
TC-E61ReduceOnly on closeClose position with reduce-only flag.No reduce-only.
TC-E62Display quantityIceberg order with visible quantity < total.No display quantity.
TC-E63Custom order paramsAdapter-specific params via order_params.N/A.

TC-E60: PostOnly accepted

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places limit buy with use_post_only=True at passive price.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOrder accepted as a maker order; post-only flag acknowledged by venue.
Skip whenAdapter does not support post-only flag.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=False,
    use_post_only=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false)
    .with_use_post_only(true)

TC-E61: ReduceOnly on close

FieldValue
PrerequisiteOpen position (from TC-E01).
ActionStop strategy with reduce_only_on_stop=True; closing order uses reduce-only flag.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled (with reduce-only).
Pass criteriaClosing order has reduce-only flag; position fully closed.
Skip whenAdapter does not support reduce-only flag.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    open_position_on_start_qty=Decimal("0.01"),
    reduce_only_on_stop=True,
    close_positions_on_stop=True,
    enable_limit_buys=False,
    enable_limit_sells=False,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_open_position_on_start(Some(Decimal::new(1, 2)))
    .with_reduce_only_on_stop(true)
    .with_close_positions_on_stop(true)
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)

TC-E62: Display quantity (iceberg)

FieldValue
PrerequisiteAdapter connected, adapter supports display quantity.
ActionPlace limit order with order_display_qty < order_qty.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOrder accepted with display quantity set; only display qty visible on the book.
Skip whenAdapter does not support display quantity / iceberg orders.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("1.0"),
    order_display_qty=Decimal("0.1"),
    enable_limit_buys=True,
    enable_limit_sells=False,
)

Rust config:

let mut config = ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("1.0"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false);
config.order_display_qty = Some(Quantity::from("0.1"));

TC-E63: Custom order params

FieldValue
PrerequisiteAdapter connected, adapter accepts additional parameters.
ActionPlace order with order_params dict containing adapter-specific parameters.
Event sequenceOrderInitializedOrderSubmittedOrderAccepted.
Pass criteriaOrder accepted; adapter-specific parameters passed through to venue.
Skip whenN/A (adapter-specific).

Considerations:

  • The order_params dict is opaque to the ExecTester and passed through to the adapter.
  • Consult the adapter's guide for supported parameters.

Group 8: Rejection handling

Test that the adapter correctly handles and reports order rejections.

TCNameDescriptionSkip when
TC-E70PostOnly rejectionPost-only order that would cross the spread.No post-only.
TC-E71ReduceOnly rejectionReduce-only order with no position to reduce.No reduce-only.
TC-E72Unsupported order typeSubmit order type not supported by adapter.Never.
TC-E73Unsupported TIFSubmit order with unsupported time in force.Never.

TC-E70: PostOnly rejection

FieldValue
PrerequisiteAdapter connected, instrument loaded, quotes flowing.
ActionExecTester places post-only order on the wrong side of the book (test_reject_post_only=True), causing it to cross the spread.
Event sequenceOrderInitializedOrderSubmittedOrderRejected.
Pass criteriaOrder rejected by venue; OrderRejected event received with reason indicating post-only violation.
Skip whenAdapter does not support post-only flag.

Considerations:

  • The ExecTester's test_reject_post_only mode intentionally prices the order to cross.
  • Some venues may partially fill instead of rejecting; behavior is venue-specific.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    enable_limit_buys=True,
    enable_limit_sells=False,
    use_post_only=True,
    test_reject_post_only=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_enable_limit_buys(true)
    .with_enable_limit_sells(false)
    .with_use_post_only(true)
    .with_test_reject_post_only(true)

TC-E71: ReduceOnly rejection

FieldValue
PrerequisiteAdapter connected, no open position for the instrument.
ActionExecTester opens a market position with reduce_only=True via test_reject_reduce_only=True and open_position_on_start_qty, when no position exists to reduce.
Event sequenceOrderInitializedOrderSubmittedOrderRejected.
Pass criteriaOrder rejected; OrderRejected event with reason indicating reduce-only violation.
Skip whenAdapter does not support reduce-only flag.

Considerations:

  • The test_reject_reduce_only flag only applies to the opening market order submitted via open_position_on_start_qty.
  • Verify no prior position exists for the instrument before running this test.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    open_position_on_start_qty=Decimal("0.01"),
    test_reject_reduce_only=True,
    enable_limit_buys=False,
    enable_limit_sells=False,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_open_position_on_start(Some(Decimal::new(1, 2)))
    .with_test_reject_reduce_only(true)
    .with_enable_limit_buys(false)
    .with_enable_limit_sells(false)

TC-E72: Unsupported order type

FieldValue
PrerequisiteAdapter connected, order type not in adapter's supported set.
ActionSubmit an order type the adapter does not support.
Event sequenceOrderDenied (pre-submission rejection by adapter).
Pass criteriaOrder denied before reaching venue; OrderDenied event with reason.
Skip whenNever (every adapter has unsupported order types to test).

Considerations:

  • OrderDenied occurs at the adapter level before the order reaches the venue.
  • This differs from OrderRejected which comes from the venue.
  • Test by configuring a stop order type that the adapter does not support.

TC-E73: Unsupported TIF

FieldValue
PrerequisiteAdapter connected, TIF not in adapter's supported set.
ActionSubmit an order with a TIF the adapter does not support.
Event sequenceOrderDenied (pre-submission rejection by adapter).
Pass criteriaOrder denied before reaching venue; OrderDenied event with reason.
Skip whenNever (every adapter has unsupported TIF options to test).

Considerations:

  • Similar to TC-E72 but for time-in-force options.
  • Test with TIF values from the Nautilus enum that the adapter does not map.

Group 9: Lifecycle (start/stop)

Test strategy lifecycle behavior and state management on start and stop.

TCNameDescriptionSkip when
TC-E80Open position on startOpen a position immediately when strategy starts.No market orders.
TC-E81Cancel orders on stopCancel all open orders when strategy stops.Never.
TC-E82Close positions on stopClose open positions when strategy stops.No market orders.
TC-E83Unsubscribe on stopUnsubscribe from data feeds on strategy stop.No unsub support.
TC-E84Reconcile open ordersReconcile existing open orders from a prior session.Never.
TC-E85Reconcile filled ordersReconcile previously filled orders from a prior session.Never.
TC-E86Reconcile open longReconcile existing open long position.Never.
TC-E87Reconcile open shortReconcile existing open short position.Never.

TC-E80: Open position on start

FieldValue
PrerequisiteAdapter connected, instrument loaded, no existing position.
ActionStrategy starts with open_position_on_start_qty set.
Event sequenceOrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaPosition opened on start; market order submitted and filled before limit order maintenance begins.
Skip whenAdapter does not support market orders.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    open_position_on_start_qty=Decimal("0.01"),
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_open_position_on_start(Some(Decimal::new(1, 2)))

TC-E81: Cancel orders on stop

FieldValue
PrerequisiteOpen limit orders from the strategy session.
ActionStop the strategy with cancel_orders_on_stop=True (default).
Event sequenceFor each open order: OrderPendingCancelOrderCanceled.
Pass criteriaAll strategy-owned open orders canceled on stop.
Skip whenNever.

TC-E82: Close positions on stop

FieldValue
PrerequisiteOpen position from the strategy session.
ActionStop the strategy with close_positions_on_stop=True (default).
Event sequenceClosing order: OrderInitializedOrderSubmittedOrderAcceptedOrderFilled.
Pass criteriaAll strategy-owned positions closed; net position = 0.
Skip whenAdapter does not support market orders.

TC-E83: Unsubscribe on stop

FieldValue
PrerequisiteActive data subscriptions (quotes, trades, book).
ActionStop the strategy with can_unsubscribe=True (default).
Event sequenceData subscriptions removed.
Pass criteriaNo further data events received after stop; clean disconnection.
Skip whenAdapter does not support unsubscribe.

Python config:

ExecTesterConfig(
    instrument_id=instrument_id,
    order_qty=Decimal("0.01"),
    can_unsubscribe=True,
)

Rust config:

ExecTesterConfig::new(strategy_id, instrument_id, client_id, Quantity::from("0.01"))
    .with_can_unsubscribe(true)

TC-E84: Reconcile open orders

FieldValue
PrerequisiteOne or more open limit orders on the venue from a prior session.
ActionStart the node with reconciliation=True.
Event sequenceOrderStatusReport generated for each open order.
Pass criteriaEach open order is loaded into the cache with correct venue_order_id, status=ACCEPTED, price, quantity, side, and order type.
Skip whenNever.

Considerations:

  • Leave limit orders open from a prior test session (do not cancel on stop).
  • Use external_order_claims to claim the instrument so the adapter reconciles orders for it.
  • Verify that the reconciled order count matches the venue-reported count.

TC-E85: Reconcile filled orders

FieldValue
PrerequisiteOne or more filled orders on the venue from a prior session.
ActionStart the node with reconciliation=True.
Event sequenceFillReport generated for each historical fill.
Pass criteriaEach filled order is loaded into the cache with correct venue_order_id, status=FILLED, fill price, fill quantity, and commission.
Skip whenNever.

Considerations:

  • Requires orders that filled in a prior session.
  • Verify fill price, quantity, and commission match the venue's reported values.
  • Some adapters may only report fills within a lookback window.

TC-E86: Reconcile open long position

FieldValue
PrerequisiteAn open long position on the venue from a prior session.
ActionStart the node with reconciliation=True.
Event sequencePositionStatusReport generated for the long position.
Pass criteriaPosition loaded into cache with correct instrument, side=LONG, quantity, and entry price matching the venue.
Skip whenNever.

Considerations:

  • Open a long position in a prior session and stop the strategy without closing it (close_positions_on_stop=False).
  • Verify the reconciled position quantity and average entry price match the venue.
  • After reconciliation, the strategy should be able to manage or close this position.

TC-E87: Reconcile open short position

FieldValue
PrerequisiteAn open short position on the venue from a prior session.
ActionStart the node with reconciliation=True.
Event sequencePositionStatusReport generated for the short position.
Pass criteriaPosition loaded into cache with correct instrument, side=SHORT, quantity, and entry price matching the venue.
Skip whenNever.

Considerations:

  • Open a short position in a prior session and stop the strategy without closing it (close_positions_on_stop=False).
  • Verify the reconciled position quantity and average entry price match the venue.
  • After reconciliation, the strategy should be able to manage or close this position.

ExecTester configuration reference

Quick reference for all ExecTesterConfig parameters. Defaults shown are for the Python config; the Rust builder uses equivalent defaults.

ParameterTypeDefaultAffects groups
instrument_idInstrumentIdrequiredAll
order_qtyDecimalrequiredAll
order_display_qtyDecimal?None2, 7
order_expire_time_delta_minsPositiveInt?None2
order_paramsdict?None7
client_idClientId?NoneAll
subscribe_quotesboolTrue
subscribe_tradesboolTrue
subscribe_bookboolFalse
book_typeBookTypeL2_MBP
book_depthPositiveInt?None
book_interval_msPositiveInt1000
book_levels_to_printPositiveInt10
open_position_on_start_qtyDecimal?None1, 9
open_position_time_in_forceTimeInForceGTC1
enable_limit_buysboolTrue2, 4, 5, 6
enable_limit_sellsboolTrue2, 4, 5, 6
enable_stop_buysboolFalse3, 4
enable_stop_sellsboolFalse3, 4
limit_time_in_forceTimeInForce?None2, 6
tob_offset_ticksPositiveInt5002, 4
stop_order_typeOrderTypeSTOP_MARKET3
stop_offset_ticksPositiveInt1003
stop_limit_offset_ticksPositiveInt?None3
stop_time_in_forceTimeInForce?None3
stop_trigger_typeTriggerType?None3
enable_bracketsboolFalse6
bracket_entry_order_typeOrderTypeLIMIT6
bracket_offset_ticksPositiveInt5006
modify_orders_to_maintain_tob_offsetboolFalse4
modify_stop_orders_to_maintain_offsetboolFalse4
cancel_replace_orders_to_maintain_tob_offsetboolFalse4
cancel_replace_stop_orders_to_maintain_offsetboolFalse4
use_post_onlyboolFalse2, 6, 7, 8
use_quote_quantityboolFalse1, 7
emulation_triggerTriggerType?None2, 3
cancel_orders_on_stopboolTrue5, 9
close_positions_on_stopboolTrue9
close_positions_time_in_forceTimeInForce?None9
reduce_only_on_stopboolTrue7, 9
use_individual_cancels_on_stopboolFalse5
use_batch_cancel_on_stopboolFalse5
dry_runboolFalse
log_databoolTrue
test_reject_post_onlyboolFalse8
test_reject_reduce_onlyboolFalse8
can_unsubscribeboolTrue9

On this page

Execution Testing SpecPrerequisitesBasic smoke testGroup 1: Market ordersTC-E01: Market BUY - submit and fillTC-E02: Market SELL - submit and fillTC-E03: Market order with IOC TIFTC-E04: Market order with FOK TIFTC-E05: Market order with quote quantityTC-E06: Close position via market order on stopGroup 2: Limit ordersTC-E10: Limit BUY GTC - submit and acceptTC-E11: Limit SELL GTC - submit and acceptTC-E12: Limit BUY and SELL pairTC-E13: Limit IOC aggressive fillTC-E14: Limit IOC passive - no fillTC-E15: Limit FOK fillTC-E16: Limit FOK no fillTC-E17: Limit GTD - submit and acceptTC-E18: Limit GTD expiryTC-E19: Limit DAY - submit and acceptGroup 3: Stop and conditional ordersTC-E20: StopMarket BUYTC-E21: StopMarket SELLTC-E22: StopLimit BUYTC-E23: StopLimit SELLTC-E24: MarketIfTouched BUYTC-E25: MarketIfTouched SELLTC-E26: LimitIfTouched BUYTC-E27: LimitIfTouched SELLGroup 4: Order modificationTC-E30: Modify limit BUY priceTC-E31: Modify limit SELL priceTC-E32: Cancel-replace limit BUYTC-E33: Cancel-replace limit SELLTC-E34: Modify stop trigger priceTC-E35: Cancel-replace stop orderTC-E36: Modify rejectedGroup 5: Order cancellationTC-E40: Cancel single limit orderTC-E41: Cancel all on stopTC-E42: Individual cancels on stopTC-E43: Batch cancel on stopTC-E44: Cancel already-canceled orderGroup 6: Bracket ordersTC-E50: Bracket BUYTC-E51: Bracket SELLTC-E52: Bracket entry fill activates TP/SLTC-E53: Bracket with post-only entryGroup 7: Order flagsTC-E60: PostOnly acceptedTC-E61: ReduceOnly on closeTC-E62: Display quantity (iceberg)TC-E63: Custom order paramsGroup 8: Rejection handlingTC-E70: PostOnly rejectionTC-E71: ReduceOnly rejectionTC-E72: Unsupported order typeTC-E73: Unsupported TIFGroup 9: Lifecycle (start/stop)TC-E80: Open position on startTC-E81: Cancel orders on stopTC-E82: Close positions on stopTC-E83: Unsubscribe on stopTC-E84: Reconcile open ordersTC-E85: Reconcile filled ordersTC-E86: Reconcile open long positionTC-E87: Reconcile open short positionExecTester configuration reference