Quote-Age + Stale-Depth Slippage Controller Playbook
Date: 2026-03-02
Category: research (slippage modeling)
Why this matters
Displayed top-of-book size is not always tradable size.
In fast microstructure regimes, the best bid/ask can look thick but be old, fragile, and ready to disappear. If we route as if displayed depth is fresh, we usually pay twice:
- missed passive fills (queue disappears before us), then
- panic crossing later at worse spread/impact.
This is a classic hidden slippage leak: dashboards show good average spread capture, but p95 implementation shortfall explodes during stale-book intervals.
Core idea
Treat each visible quote level as a decaying inventory of trustworthy liquidity.
Instead of using raw displayed depth, use effective depth:
[ D^{eff}{t,l} = D^{disp}{t,l} \cdot \exp(-\lambda_a A_{t,l}) \cdot \exp(-\lambda_c C_{t,l}) \cdot \exp(-\lambda_v V_t) ]
Where:
- (A_{t,l}): quote age at level (l)
- (C_{t,l}): recent cancel pressure near level (l)
- (V_t): local volatility / micro-jitter proxy
- (\lambda_a,\lambda_c,\lambda_v): regime-conditional decay sensitivities
Then optimize execution decisions against risk on effective depth, not printed depth.
Minimal state and objective
At decision tick (t):
- (x_t): remaining quantity
- (\tau_t): remaining time
- (u_t): action (passive join / improve / take / pause)
- (s_t): spread
- (\text{OFI}_t): order-flow imbalance
- (A_t): age feature vector around touch
- (C_t): cancel-intensity features
- (R_t): refill-intensity features
- (L_t): latency/jitter features
Target:
[ \min_{u_{t:T}} ; \mathbb{E}[IS] + \lambda_{95}Q_{0.95}(IS) + \lambda_{miss}\Pr(\text{deadline miss}) ]
Hard constraints:
- participation cap,
- max expected short-horizon markout,
- venue reject-rate ceiling,
- emergency SAFE mode on stale-depth stress.
Model decomposition
1) Quote freshness model (microstructure layer)
Build a touch-neighborhood freshness score:
[ F_t = w_1\tilde{A}_t + w_2\tilde{C}_t - w_3\tilde{R}_t + w_4\tilde{L}_t ]
- higher (F_t): staler / less trustworthy near-touch liquidity.
- convert into effective-depth multiplier with a sigmoid or exponential map.
Use level-specific and side-specific features (bid/ask asymmetry matters).
2) Fill hazard + adverse markout coupling
Model two coupled outcomes for each candidate action (u):
- fill probability / time-to-fill,
- post-fill markout (e.g., 5s/30s/60s).
Stale quotes often create the worst combination:
- low true fill reliability when passive,
- high adverse selection when taking after queue collapse.
So estimate:
[ \hat{p}{fill}(u|S_t), \quad \hat{m}{\alpha}(u|S_t), ; \alpha\in{0.5,0.9,0.95} ]
with freshness features explicitly included.
3) Effective-depth-adjusted slippage quantile
For each action (u):
[ \hat{q}^{base}{0.95}(u) = f{0.95}(x_t,\tau_t,s_t,\text{OFI}_t,D^{eff}_t,u) ]
Add stale-depth penalty:
[ \text{SDP}(u)=\beta\cdot \max\big(0, F_t - \theta_{fresh}\big)\cdot g(u) ]
where (g(u)) penalizes actions sensitive to fake depth (typically aggressive sweeps into fragile books).
Final score:
[ \text{Score}(u)=\hat{q}^{base}{0.95}(u)+\text{SDP}(u)+\lambda{miss}\hat{p}_{miss}(u) ]
Pick argmin over feasible actions.
Causal estimation notes (important)
Freshness signals are endogenous to volatility and informed flow. Avoid naive inference.
Use at least one identification strategy:
- micro-randomized aggression jitter inside strict risk bounds,
- instrumental shocks (exogenous gateway jitter / queue-rank tie randomness),
- doubly robust correction for action assignment,
- regime-stratified estimation (open/close, event windows, low/high volatility).
Without this, stale-depth penalties can overreact and kill completion.
State machine controller
Define a stale-depth risk index:
[ \text{SDRI}_t = z(F_t) + \rho_1 z(\text{cancelShock}_t) - \rho_2 z(\text{refill}_t) ]
States
- FRESH: low SDRI, refill healthy.
- AGING: rising quote age / cancel pressure.
- STALE: sustained high SDRI, refill weak.
- TOXIC: stale + adverse markout spike + reject/cancel instability.
- SAFE: hard risk breach.
Policy sketch
- FRESH: passive-first, normal POV.
- AGING: reduce child size, prioritize queue-improve over full cross.
- STALE: avoid deep sweep; use smaller timed clips, selective venue masks.
- TOXIC: throttle aggression hard, optionally pause high-tox venues.
- SAFE: emergency downshift / temporary halt / manual review rule.
Use hysteresis + minimum dwell time to avoid thrashing.
Data contract (what to log)
Per decision:
- best-level age distribution (mean/p90/max)
- cancel and refill rates at L1–L3
- effective depth by level and side
- candidate action scores (base q95, SDP, miss term)
- selected action + rejected alternatives
- realized fill, slippage, markout, reject reason
Daily diagnostics:
- freshness model calibration vs realized queue survival,
- q95 exceedance reliability by state,
- completion-rate delta vs baseline,
- state occupancy + transition stability,
- venue-level stale-depth false-positive rate.
Online algorithm (pseudo)
for each tick t:
S_t <- observe(market + execution state)
freshness <- FreshnessModel.predict(S_t)
D_eff <- depth_decay(D_disp, freshness, cancel, volatility)
for u in candidate_actions:
q95_base[u] <- SlippageQ95.predict(S_t, D_eff, u)
p_miss[u] <- FillModel.predict_deadline_miss(S_t, D_eff, u)
sdp[u] <- stale_depth_penalty(freshness, u)
score[u] <- q95_base[u] + sdp[u] + lambda_miss * p_miss[u]
feasible <- apply_constraints(candidate_actions)
u_t <- argmin(score[u] for u in feasible)
execute(u_t)
update diagnostics + state machine
Rollout plan (safe)
- Shadow mode (2–3 weeks): compute SDRI + recommended actions, no control.
- Canary (5% notional): strict kill thresholds on q95 and miss-rate.
- Ramp: 5% → 15% → 30% only if calibration and completion remain stable.
- Champion–challenger governance: fixed weekly promotion criteria.
Rollback triggers:
- q95 IS degradation beyond threshold,
- completion-rate drop beyond threshold,
- stale-state false positives surge,
- unstable state flapping or venue reject bursts.
Common failure modes
Over-penalizing freshness in naturally fast names
- Fix: symbol/venue-specific (\lambda) priors + hierarchical shrinkage.
State flapping during open/close transitions
- Fix: dwell-time and transition hysteresis.
Tail improvement by underfilling
- Fix: explicit miss penalty and hard completion floor.
Good calibration in aggregate, bad by regime
- Fix: regime-sliced reliability dashboards.
Venue spillover unmodeled
- Fix: cross-venue feature interactions + venue quarantine logic.
Practical implementation checklist
- Define freshness features (age/cancel/refill/latency) at L1–L3
- Build effective-depth transform and monitor drift
- Train q95 slippage model with effective-depth inputs
- Add stale-depth penalty and hard feasibility constraints
- Implement SDRI state machine with hysteresis
- Instrument score decomposition and action audit trail
- Run shadow → canary → progressive ramp with rollback gates
References (starting points)
- Bouchaud et al. on order-book dynamics and liquidity resiliency.
- Gatheral et al. on impact structure and no-dynamic-arbitrage constraints.
- Cartea, Jaimungal, Penalva on algorithmic/optimal execution.
- Queue-reactive and cancel-intensity microstructure literature for fill dynamics.
Bottom line
The book is not only about how much size is visible, but how old and fragile that size is.
A quote-age + stale-depth-aware controller closes a major blind spot: it prices fake liquidity before execution pays for it in p95 slippage.