nautechsystems/nautilus_trader · critical
Matching engine not found for instrument {order_instrument_i
Error message
Matching engine not found for instrument {order_instrument_id} What it means
While submitting an order list, the exchange could not find a `MatchingEngine` registered for the order's instrument ID and panicked. Each traded instrument in the backtest needs a matching engine that simulates order matching; submitting orders for an instrument with no engine means the venue cannot process them.
Source
Thrown at crates/backtest/src/exchange.rs:1886
let account_id = if let Some(exec_client) = &self.exec_client {
exec_client.account_id()
} else {
panic!("Execution client should be initialized");
};
if let TradingCommand::SubmitOrderList(ref command) = command {
let mut orders: Vec<OrderAny> = self
.cache
.borrow()
.orders_for_ids(&command.order_list.client_order_ids, command);
for order in &mut orders {
let order_instrument_id = order.instrument_id();
if let Some(matching_engine) = self.matching_engines.get_mut(&order_instrument_id) {
matching_engine.process_order(order, account_id);
} else {
panic!("Matching engine not found for instrument {order_instrument_id}");
}
}
return;
}
if let Some(matching_engine) = self.matching_engines.get_mut(&instrument_id) {
match command {
TradingCommand::SubmitOrder(command) => {
let mut order = self
.cache
.borrow()
.order(&command.client_order_id)
.map(|o| o.clone())
.expect("Order must exist in cache");
matching_engine.process_order(&mut order, account_id);
}
TradingCommand::ModifyOrder(ref command) => {View on GitHub (pinned to 18893faf8b)
Solutions
- Register the instrument in the venue configuration (`add_venue` with the instrument) so a matching engine is created.
- Verify every order's `instrument_id` exactly matches a registered instrument.
- Validate instrument coverage before the run (compare cache instruments to venue-registered instruments).
Example fix
// before
engine.add_data(quote_data); // instrument on data only
engine.run(); // panic: no matching engine
// after
engine.add_venue(SimulatedExchangeConfig {
instruments: vec![instrument],
..Default::default()
});
engine.add_data(quote_data);
engine.run(); Defensive patterns
Strategy: validation
Validate before calling
// before submitting
let registered: HashSet<InstrumentId> = venue.instruments().iter().map(|i| i.id()).collect();
for order in orders {
assert!(registered.contains(&order.instrument_id()), "instrument not registered: {}", order.instrument_id());
} Prevention
- Derive order instrument IDs from registered instruments, never hardcoded strings
- Cross-check strategy instrument lists against venue config at startup
- Add a startup assertion comparing cache instruments to venue instruments
When it happens
Trigger: `SubmitOrderList` command where one of the orders' `instrument_id` has no entry in `exchange.matching_engines` (never registered via the venue config).
Common situations: Orders created for an instrument not declared in the backtest venue configuration; typo'd or synthesized instrument IDs; running a backtest against data from an instrument that was not added to the exchange config.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Matching engine not found for instrument {instrument_id}
- Cannot handle command: {command:?}
- Latency model should be initialized
- Execution client should be initialized
- Position id should be generated. Hedging Oms type order matc
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/86f7844f4d566180.
Report an issue: GitHub.