Expand description
A common order matching core for the OrderMatchingEngine and other components.
§Book layout
Each side has two separate books, mirroring real-venue architecture:
- Limit book:
BTreeMap<Price, OrderBucket>keyed by limit price. Holds plainLIMITorders. - Stop book:
BTreeMap<Price, OrderBucket>keyed by trigger price. HoldsSTOP_*,*_IF_TOUCHED, andTRAILING_STOP_*orders that need trigger checking before matching.
Plus a per-side pending SmallVec for orders without a key (e.g.
MARKET_TO_LIMIT before conversion).
§Ordering invariant
Orders are matched in price-time priority, with limits processed before stops on each side:
- Bid limits: best (highest) price first via
iter().rev(). - Ask limits: best (lowest) price first via
iter(). - Bid stops: closest trigger first via
iter()(lowest trigger crosses first as ask climbs through resting buy stops). - Ask stops: closest trigger first via
iter().rev()(highest trigger crosses first as bid drops through resting sell stops).
Within a price level orders are stored in a SmallVec in insertion order,
preserving time priority (FIFO at the same price). No active sorting
happens; the BTreeMap’s tree shape gives price ordering for free.
§Modify semantics
The core does not expose an in-place modify API. Any change to a resting
order must call OrderMatchingCore::delete_order followed by
OrderMatchingCore::add_order, which lands the order at the back of
its (new or unchanged) price level. This matches real-venue behavior for
price-changing modifies but loses queue position on quantity-only
modifies. An in-place quantity-update API could be added later if the
engine wants to preserve queue position on those.
§Known limitation: limits-then-stops emission
On each side, OrderMatchingCore::iterate_bids and
OrderMatchingCore::iterate_asks emit all matchable limits before any
triggered stops. In real venues stops trigger as the price crosses them
and only then aggress against the limit book, so a snapshot iteration that
sees both kinds matchable simultaneously cannot perfectly reconstruct the
temporal order. The matching engine drives the snapshot, so a future
engine change that feeds the previous bid/ask to the core could replay
the price path and emit triggers/fills in cross-time order. Until then,
callers that depend on price-path ordering (e.g. multi-level gap
scenarios with both matchable limits and matchable stops on the same
side) should treat that interleaving as undefined.
§Duplicate inserts
add_order does not deduplicate. Adding the same client_order_id twice
without an intervening delete_order puts two RestingOrder entries in
the vec and they will both match. Callers must ensure each
client_order_id appears at most once across both sides.
§Performance
Per-level buckets are SmallVecs with INLINE_ORDERS_PER_LEVEL inline
slots so the common case (1-3 orders per price) avoids heap allocation
per bucket. Above that threshold the bucket spills to the heap. Adds and
deletes are O(log L) for the BTreeMap lookup plus O(B) for the bucket
scan/shift, where L is the number of distinct price levels per book and
B is orders at that level: both small in practice.
An AHashMap index from ClientOrderId to (side, BookKind, Price)
makes OrderMatchingCore::get_order, OrderMatchingCore::order_exists,
and the lookup portion of OrderMatchingCore::delete_order hash-fast;
the follow-on bucket scan is O(B). The map is used purely for point
queries (never iterated), so its randomized seed does not affect
determinism.
Structs§
- Order
Matching Core - A generic order matching core. See module docs for ordering, modify, duplicate, and performance contracts.
- Resting
Order - Lightweight order information for matching/trigger checking.
Enums§
- Match
Action - An action returned by
OrderMatchingCore::iteratewhen an order matches.
Constants§
- INLINE_
ORDERS_ PER_ LEVEL - Inline capacity for orders at a single price level. Sized to cover the
typical 1-3 orders per level; above this the per-bucket
SmallVecspills to the heap.