Record and analyse order journeys¶
OrderJourneyTracer is an execution listener that records the full
event sequence of every order it observes. Attach it to a
BacktestRunner once and it captures every status transition with
queue position, timestamps, and maker/taker classification for
post-trade analysis.
Use it¶
"""Read OrderJourneyTracer analytics from Python.
Wiring note: in C++ `OrderJourneyTracer` is an `IOrderExecutionListener`
fed by `onOrderEvent` (see tests/test_order_journey_tracer.cpp). The
Python class exposes the collector and its analytics, but it is not an
`ExecutionListener` subclass, so `BacktestRunner.add_execution_listener`
does not accept it -- attach the tracer on the C++ side and read the
results here.
"""
import flox_py as flox
tracer = flox.OrderJourneyTracer(
max_orders=10_000,
max_records_per_order=64,
sample_rate=1.0,
)
# Every recorded step, as one structured numpy array.
rows = tracer.result()
print("columns:", rows.dtype.names)
# Inspect a single order.
trace = tracer.journey(order_id=42)
for row in trace:
print(row["seq"], row["status"], row["ts_ns"],
row["queue_ahead"], row["is_maker"])
# Aggregate analytics. On an empty trace the ratios return NaN.
print("orders:", tracer.order_count())
print("records:", tracer.record_count())
print("median ack latency:", tracer.median_ack_latency_ns(), "ns")
print("median time to first fill:",
tracer.median_time_to_first_fill_ns(), "ns")
print("maker fill ratio:", tracer.maker_fill_ratio())
print("cancel race loss rate:", tracer.cancel_race_loss_rate())
What gets recorded¶
Each row carries:
order_idand per-orderseq(0-based)status—OrderEventStatusnumeric codets_ns— event emission timestampfill_qty,fill_price— populated on fill eventsqueue_ahead,queue_total— populated on fill andQUEUE_POSITION_UPDATEDeventsis_maker— populated on fill eventssubmitted_at_ns...expired_at_ns— full per-stage timestamp snapshot
The output is a numpy structured array, so downstream analysis can use
pandas.DataFrame(arr) or numpy operations directly.
Reading it back¶
| Method | Returns |
|---|---|
result() |
Every recorded event as one structured array, one row per event |
journey(order_id) |
The same shape, restricted to one order |
order_count() |
Orders currently tracked |
record_count() |
Total events retained across all tracked orders |
median_ack_latency_ns() |
Median submit-to-ack latency across tracked orders |
median_time_to_first_fill_ns() |
Median submit-to-first-fill latency |
maker_fill_ratio() |
Fraction of fills classified maker |
cancel_race_loss_rate() |
Fraction of cancels that lost the race to a fill |
clear() |
Drop every tracked order and reset the counters |
Bounded memory¶
Two caps keep the tracer safe in long runs:
max_orders(default 1,000,000) — once exceeded, the oldest tracked order (by first-seen time) is evicted whole.max_records_per_order(default 64) — extra events past this cap are dropped silently for the affected order.
Sampling¶
sample_rate in [0.0, 1.0] selects orders deterministically:
(order_id * sample_salt) % 1000 is compared against a threshold.
The same sample_salt produces the same selection across runs, so
results are reproducible.
Notes¶
- The tracer is a research tool. It implements
IOrderExecutionListenerand ships as a Python binding only; Node / QuickJS / Codon parity is not enforced for this category of read-only post-trade utility (same precedent asHurstandADF). - The tracer fires from the engine's order-event path, after the position tracker updates. Attached PnL trackers and other listeners see events in the same order.
- For live runs, the tracer accumulates events from the live connector's order callbacks too, but live-side queue position reads as zero (exchanges do not publish queue position).