Configure realistic ack-latency distributions¶
The default ack-latency knobs (set_submit_ack_latency(latency_ns,
jitter_ns)) sample uniform jitter around a fixed base. Real venue
latency is heavy-tailed (lognormal-like p50 ≈ 5ms, p99 ≈ 80ms) and
correlated: when the venue is under load, every order is slow,
not each one independently.
LatencyDistribution ships four kinds plus a burst-correlation
knob. The scalar set_submit_ack_latency setter still works and
maps to the Uniform kind for backward compatibility.
Kinds¶
| Kind | Use when |
|---|---|
Constant |
Deterministic baselines, parity tests. |
Uniform |
Legacy base ± jitter behaviour. |
Lognormal |
Real venue ack timings — heavy right tail. |
Empirical |
You have a recorded histogram and want to resample it. |
Burst correlation¶
Set with set_burst_correlation(rho) where rho is in [0, 1).
Implementation is AR(1) on the standard-normal residual for
Lognormal (or on the rank index for Empirical). The independent-
draw default is rho = 0.
Why care: a strategy that places, races, and cancels every 50ms
under a uniform model behaves very differently from the same
strategy under a rho = 0.6 burst regime, where slow acks cluster.
The latter matches what live traders observe during venue load
spikes.
Apply from a strategy¶
"""Apply a heavy-tailed, burst-correlated ack-latency distribution."""
import flox_py as flox
exec = flox.SimulatedExecutor()
# Lognormal: median 5ms, sigma 0.7 → p99 ~ 50ms tail.
dist = flox.LatencyDistribution.lognormal(median_ns=5_000_000, sigma=0.7)
# Couple successive draws via AR(1): when the venue is slow, the
# next ack tends to be slow too.
dist.set_burst_correlation(0.4)
exec.set_submit_ack_latency_distribution(dist)
exec.set_cancel_ack_latency_distribution(
flox.LatencyDistribution.lognormal(median_ns=8_000_000, sigma=0.6))
exec.set_replace_ack_latency_distribution(
flox.LatencyDistribution.lognormal(median_ns=12_000_000, sigma=0.6))
# Empirical: resample from an observed histogram.
recorded = [1_000_000, 1_500_000, 2_000_000, 3_000_000, 4_500_000,
6_000_000, 9_000_000, 15_000_000, 35_000_000, 80_000_000]
empirical = flox.LatencyDistribution.empirical(recorded)
exec.set_submit_ack_latency_distribution(empirical)
assert dist.median_ns() == 5_000_000
Calibrating from real data¶
A first-pass workflow when you have a venue tape with timestamped submits and acks:
- Compute
ack_latency = ack_ts - submit_tsfor each pair. - Fit lognormal:
mu = mean(ln(latency)),sigma = stdev(ln(latency)), median =exp(mu). - Estimate burst correlation: lag-1 autocorrelation of the
ln(latency)series, clamped to[0, 0.95].
For Empirical use, downsample the latency series (every Nth sample or a histogram bucket) and pass it directly. The estimator resamples with replacement.
Notes¶
- The scalar
set_submit_ack_latencysetter is preserved and maps toUniform(base - jitter, base + jitter). Existing tests keep passing. - Distributions are copied on attach. Mutating a distribution after
attaching does not affect the executor — call
set_*_latency_distributionagain to update. - Empirical sampling uses replacement, so finite samples cannot exhaust the source histogram.