Skip to main content

bulk_replace_order

Function bulk_replace_order 

Source
pub async fn bulk_replace_order(
    __arg0: State<AppState>,
    __arg1: SonicJson<BulkReplaceOrderRequest>,
) -> Result<SonicJson<BulkReplaceOrderResponse>, ApiError>
Expand description

Replace multiple orders using cancel-all-then-create-all ordering.

Per-leg semantics mirror PUT /order (cancel old + place new), but across a batch every leg’s cancel is enqueued before any create, so the engine cannot match a new leg against a sibling’s still-resting opposite. This eliminates the self-trade-prevention kill path that fired when the bulk dispatched independent ReplaceOrder commands. A per-leg response is returned in original index order; a leg whose cancel failed (already filled / not found) is skipped for the create phase and returned with the cancel rejection details. Not atomic across the batch: the gap between a leg’s cancel committing and its create landing is per-leg. See engineering-docs/docs/flows/order-lifecycle.mdx for tradeoffs vs. the single-command PUT /order path. Bulk replace orders.

Executes cancel-all-then-create-all ordering across the batch: every leg’s cancel is enqueued before any create, so the engine is guaranteed to process all cancels (removing every old leg from the book) before any new order matches. Without this, the engine’s FIFO processing of independent OrderAction::ReplaceOrder commands meant each command’s phase-2 place could match against a sibling’s still-resting opposite leg, triggering self-trade prevention and losing the new leg entirely.

The path is:

  1. validate_replace_request per leg (sig / auth / parse / precision).
  2. orchestrate_bulk_replace dispatches all CancelOrder commands, awaits responses, then dispatches CreateOrder commands only for legs whose cancel actually reached Canceled.
  3. Per-leg results merge: prefer the create response; fall back to a cancel-only result when the cancel landed but the create didn’t.

Semantics vs. single-command ReplaceOrder: the PUT /order single- replace path dispatches one OrderAction::ReplaceOrder engine command that handles the cancel and place atomically within one engine tick and one journal entry (src/rsm/unified_engine/order_routing.rs:process_replace_order). The bulk path, by contrast, issues separate CancelOrder and CreateOrder commands (2N journal entries per bulk of N), so there is a per-leg gap between a leg’s cancel committing and its create landing. Real-world MMs re-quote every cycle, so a missed create is recovered on the next tick. The PUT /order single-replace endpoint is unchanged.