Quote-Age + Stale-Depth Slippage Controller Playbook

2026-03-02 · finance

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:

  1. missed passive fills (queue disappears before us), then
  2. 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:

Then optimize execution decisions against risk on effective depth, not printed depth.


Minimal state and objective

At decision tick (t):

Target:

[ \min_{u_{t:T}} ; \mathbb{E}[IS] + \lambda_{95}Q_{0.95}(IS) + \lambda_{miss}\Pr(\text{deadline miss}) ]

Hard constraints:


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 ]

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):

  1. fill probability / time-to-fill,
  2. post-fill markout (e.g., 5s/30s/60s).

Stale quotes often create the worst combination:

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:

  1. micro-randomized aggression jitter inside strict risk bounds,
  2. instrumental shocks (exogenous gateway jitter / queue-rank tie randomness),
  3. doubly robust correction for action assignment,
  4. 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

Policy sketch

Use hysteresis + minimum dwell time to avoid thrashing.


Data contract (what to log)

Per decision:

Daily diagnostics:

  1. freshness model calibration vs realized queue survival,
  2. q95 exceedance reliability by state,
  3. completion-rate delta vs baseline,
  4. state occupancy + transition stability,
  5. 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)

  1. Shadow mode (2–3 weeks): compute SDRI + recommended actions, no control.
  2. Canary (5% notional): strict kill thresholds on q95 and miss-rate.
  3. Ramp: 5% → 15% → 30% only if calibration and completion remain stable.
  4. Champion–challenger governance: fixed weekly promotion criteria.

Rollback triggers:


Common failure modes

  1. Over-penalizing freshness in naturally fast names

    • Fix: symbol/venue-specific (\lambda) priors + hierarchical shrinkage.
  2. State flapping during open/close transitions

    • Fix: dwell-time and transition hysteresis.
  3. Tail improvement by underfilling

    • Fix: explicit miss penalty and hard completion floor.
  4. Good calibration in aggregate, bad by regime

    • Fix: regime-sliced reliability dashboards.
  5. Venue spillover unmodeled

    • Fix: cross-venue feature interactions + venue quarantine logic.

Practical implementation checklist


References (starting points)


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.