[BUG] Missing cap_queue_ahead in QuoteTick (L1) matching engine path
Summary
When backtesting with queue_position=True using QuoteTick data, cap_queue_ahead is never called during quote updates.
If the displayed size at the best bid or ask decreases without price movement (e.g., due to cancellations), queue_ahead_total remains frozen at its initial value. This leads to an invalid state where queue_ahead_total exceeds the total displayed size at that price level (ahead > displayed).
This logic is already present in the OrderBookDelta path, but missing in the QuoteTick path.
Code Reference
File: crates/execution/src/matching_engine/mod.rs
- Delta path (handles size updates properly): In adjust_queue_for_delta (line 880 in develop):
} else if delta.action == BookAction::Update {
if self.is_order_granular_delta(delta.flags) {
self.adjust_l3_queue_on_update(&delta.order);
} else {
self.cap_queue_ahead(
delta.order.price.raw(),
delta.order.size.raw(),
delta.order.side,
);
}
}- QuoteTick path (missing cap_queue_ahead): In decrement_l1_queue_on_quote (line 1023 in develop):
if self.tob_initialized {
if bid_price_raw < self.prev_bid_price_raw {
self.adjust_l1_queue_on_price_move(bid_price_raw, bid_size_raw, OrderSide::Buy);
}
if ask_price_raw > self.prev_ask_price_raw {
self.adjust_l1_queue_on_price_move(ask_price_raw, ask_size_raw, OrderSide::Sell);
}
}
self.resolve_pending_l1_snapshots(bid_price_raw, bid_size_raw, ask_price_raw, ask_size_raw);Because decrement_l1_queue_on_quote only triggers on price moves and never invokes cap_queue_ahead, resting orders do not reflect size decreases at the same price.
Steps to Reproduce
- Configure an engine with config.queue_position = true.
- Feed an initial QuoteTick (e.g., bid_price = 100.0, bid_size = 3000).
- Submit a resting limit buy order at 100.0.
- queue_ahead_total is set to 3000.
- Feed a subsequent QuoteTick at the same price with reduced size (bid_price = 100.0, bid_size = 500), with no trade ticks.
- Check order queue status:
- Current displayed book size is 500.
- queue_ahead_total is still 3000.
Expected Behavior
queue_ahead_total should be capped at the current displayed level size (500 in the example above), consistent with the behavior in adjust_queue_for_delta.
Actual Behavior
queue_ahead_total remains at 3000. The order remains unfilled until at least 3000 contracts trade at 100.0, even though only 500 contracts exist at that level.
Proposed Fix
Call cap_queue_ahead at the end of decrement_l1_queue_on_quote:
self.cap_queue_ahead(bid_price_raw, bid_size_raw, OrderSide::Buy);
self.cap_queue_ahead(ask_price_raw, ask_size_raw, OrderSide::Sell);Source: nautechsystems/nautilus_trader