docs/concepts/backtesting/trade-execution.md
Trade ticks trigger matching by default when a venue has trade_execution=True. A trade provides
evidence that liquidity traded at its price, so it can fill resting orders on the passive side.
Set trade_execution=False to use trades as strategy data without letting them trigger matching:
from nautilus_trader.backtest import BacktestVenueConfig
from nautilus_trader.model import AccountType
from nautilus_trader.model import BookType
from nautilus_trader.model import OmsType
venue = BacktestVenueConfig(
name="SIM",
oms_type=OmsType.NETTING,
account_type=AccountType.CASH,
book_type=BookType.L1_MBP,
starting_balances=["100_000 USD"],
trade_execution=False,
)
When trade execution is disabled, trade ticks do not run order matching or matching-engine maintenance such as GTD expiry, trailing-stop activation, and instrument-expiration checks. A later quote or executable bar can run that maintenance.
The engine temporarily moves its matching references to the trade price:
SELLER trade can match resting BUY orders.BUYER trade can match resting SELL orders.NO_AGGRESSOR trade can affect both sides because the passive side is unknown.The historical order book remains unchanged. Only the matching core's transient bid, ask, and last prices move for the iteration.
When a trade triggers a limit fill:
min(order.leaves_qty, trade.size).With liquidity_consumption=False, the same trade size can support more than one order during an
iteration. With liquidity_consumption=True, trade-driven fills share a consumption counter, so
their total cannot exceed the unconsumed trade size.
For example, a SELLER trade at 100.00 can fill a BUY LIMIT at 100.05. If no book level represents
that fill, the engine uses 100.05 rather than granting the better trade price.
After the iteration, the engine restores matching references from the available market baseline:
This distinction matters when interpreting a stream of trades without quotes. Repeated trades can move the simulated L1 state, but quote-backed L1 matching does not progressively discard the non-aggressor side of the latest quote.
The aggressor is the participant that crossed the spread:
SELLER: A seller hit the bid. The trade can fill a resting BUY order.BUYER: A buyer lifted the ask. The trade can fill a resting SELL order.NO_AGGRESSOR: The data does not identify the aggressor. The engine considers both sides where
the feature requires a side.A BUYER trade provides evidence for passive SELL orders, not BUY orders. A SELLER trade provides evidence for passive BUY orders, not SELL orders.
Book updates establish the spread and visible depth. Trade ticks provide execution evidence between those updates. This is useful when depth snapshots are throttled and a trade occurs at a price that the latest snapshot does not contain.
Use the two feeds with care:
Set queue_position=True with trade_execution=True to track displayed quantity ahead of each
LIMIT order:
venue = BacktestVenueConfig(
name="SIM",
oms_type=OmsType.NETTING,
account_type=AccountType.MARGIN,
book_type=BookType.L2_MBP,
starting_balances=["100_000 USD"],
trade_execution=True,
queue_position=True,
)
For example:
SELLER trade for 80 units reduces the queue ahead to 20.SELLER trade for 30 units clears the queue and leaves 10 units available to fill.For L2 books and aggregate L3 updates:
For L3 MBO books:
Changing a simulated order's price resets its queue position at the new level. A quantity-only change retains the progress already made.
With BookType.L1_MBP, trade ticks reduce quantity ahead while quotes provide price-move and
displayed-size evidence:
LIMIT orders.NO_AGGRESSOR trades reduce queues on both sides. This can clear a queue and fill an order
earlier than reality, so it is optimistic from the strategy's execution perspective.