Model order replace acknowledgement¶
Like cancellation, an order replace request takes time to acknowledge at the exchange. The simulator can model that delay so a backtest can reproduce the late-replace-after-fill race: the original order filling in the window between replaceOrder and the venue's ack.
Events¶
Async replace produces a three-event sequence on success:
- REPLACE_SUBMITTED — fires immediately on replaceOrder()
- REPLACE_ACCEPTED — fires at the sampled ack deadline
- REPLACED — terminal "old gone, new alive"
If the original order fills before the ack arrives:
- FILLED — on the original order
- REPLACE_REJECTED — with reject_reason "late_replace_after_fill"
REPLACED still fires through the existing onOrderReplaced listener; the new in-flight statuses dispatch through three new virtuals onOrderReplaceSubmitted, onOrderReplaceAccepted, onOrderReplaceRejected. On the strategy callback surface, the in-flight events flow through the existing on_order_update / onOrderUpdate path.
Configure¶
Two BacktestConfig knobs:
- replaceAckLatencyNs — base ack delay. Default 0 preserves the legacy synchronous replaceOrder().
- replaceAckJitterNs — uniform jitter band, clamped non-negative.
RNG sampling shares cancelAckSeed.
The bindings expose the pair as one SimulatedExecutor setter, set_replace_ack_latency(latency_ns, jitter_ns=0). BacktestRunner's constructor takes only (registry, fee_rate, initial_capital) and owns its executor internally, so a BacktestRunner's replace latency is configured through BacktestConfig in C++.
Detect the race from a strategy¶
Notes¶
- Neither the Python nor the Node SimulatedExecutor exposes a replace call. A backtest replace is emitted from the strategy (modify_order / emitModify); the runner turns that Modify signal into replaceOrder on its own executor.
- The pending replace is dropped if the order is canceled by other means before the ack arrives. Replace ack finalization runs on the next simulator tick after the deadline passes.
- Conditional orders (stops, take-profits, trailing) use the same async path when replaceAckLatencyNs > 0.
- The clock source is the engine's simulated clock; live runs follow whichever clock the connector attached.