Time-in-Force Expiry Cliff and Re-entry Slippage
Date: 2026-03-07
Category: research (execution / slippage modeling)
Why this playbook exists
Many desks treat passive child-order expiry as harmless housekeeping:
- order sits with a short TIF,
- if not filled, it expires,
- router re-submits a fresh child.
In production, this creates a hidden slippage channel when many children share similar TTL/TIF logic. Expiries bunch up, queue priority resets together, and the strategy re-enters at worse prices under urgency pressure.
I call this the Expiry Cliff Tax (ECT).
Core failure mode
For a buy schedule:
- Passive child rests near touch with TIF =
T. - Market is stable enough to avoid immediate crossing, but not stable enough to guarantee fill.
- At
T, many siblings expire in the same short window (expiry cluster). - Re-entry is delayed by local throttle, sequencing, or risk checks.
- When re-entering:
- queue priority is reset,
- touch has drifted up,
- urgency controller increases aggression.
Net result: higher implementation shortfall despite “disciplined” passive behavior.
Symmetric for sells.
Data contract (minimum)
At child-order level:
parent_id,child_id,symbol,side,target_qtytif_type(IOC/FOK/GTD/day/custom TTL),ttl_ms,expire_tssubmit_ts,ack_ts,cancel_ts,expired_tsreentry_send_ts,reentry_ack_ts,reentry_order_typefill_ts,fill_px,fill_qtyarrival_px,mid_ts,microprice_ts, spread/depth snapshotsurgency_state,residual_qty,time_to_deadline- reject/throttle codes on re-entry path
Without exact expired_ts and reentry_send_ts, expiry-driven cost gets misattributed to volatility.
Metrics that expose Expiry Cliff Tax
1) Expiry Cluster Ratio (ECR)
[ ECR_{\Delta} = \frac{#{\text{expiries in sliding window }\Delta}}{#{\text{active children}}} ]
Typical Δ: 50-250ms. High ECR indicates synchronized order deaths.
2) Re-entry Gap (REG)
[ REG = t_{reentry_ack} - t_{expired} ]
Track p50/p90/p95 by symbol × venue × regime.
3) Queue Reset Penalty (QRP)
Estimate expected queue-position loss from expiry + re-submit versus counterfactual keep-resting path.
Operational proxy:
[ QRP \approx \text{ExpectedWait}{newQueue} - \text{ExpectedWait}{oldQueue} ]
Convert wait delta to bps via short-horizon drift/markout model.
4) Expiry Cliff Tax (ECT, bps)
For buy side:
[ ECT = 10^4 \cdot \frac{\sum_i q_i (p_i^{actual} - p_i^{cf,no-expiry-cliff})}{\sum_i q_i p_i^{cf,no-expiry-cliff}} ]
cf,no-expiry-cliff can be replay-simulated with staggered expiries.
5) Expiry-to-Aggression Escalation Rate (EAER)
[ EAER = \frac{#{\text{re-entry switches passive}\rightarrow\text{aggressive within }\tau}}{#{\text{expired children}}} ]
High EAER means expiry policy indirectly forces spread crossing.
Modeling blueprint
Treat child lifecycle as a multi-state process:
RESTING -> FILLED | EXPIRED -> REENTERED(passive/aggressive) -> FILLED
Model total cost as mixture:
[ C = \pi_{direct-fill} C_{direct-fill} + \pi_{expiry-reentry} C_{expiry-reentry} + \pi_{forced-catchup} C_{forced-catchup} ]
Submodel A: Expiry hazard
Estimate probability that resting child expires before fill.
Useful features:
- queue rank proxy, spread state, imbalance
- local cancel intensity and refill rate
- remaining TTL
- short-horizon volatility + microprice drift
Submodel B: Re-entry gap and mode
Predict REG and whether re-entry turns aggressive.
Features:
- burst expiries in same symbol/venue
- router backlog/reject pressure
- urgency and residual schedule pressure
- session regime (open/close/event)
Submodel C: Counterfactual cost
Estimate what cost would have been under:
- staggered expiry (
ttl + jitter), or - dynamic TTL extension when fill hazard is near threshold.
This is the basis for estimating ECT and control benefit.
Control design
Control 1: TTL jittering (anti-clustering)
Do not assign identical TTL to all children.
- baseline TTL:
T - apply bounded jitter:
T + U[-j, +j] - choose
jby liquidity regime (larger in thick books, smaller in thin names)
Goal: reduce synchronized expiry cliffs without destabilizing schedule.
Control 2: Hazard-aware TTL extension
If fill hazard is improving near expiry, extend TTL slightly instead of hard expiry.
Pseudo-rule:
- if
P(fill in next x ms)above threshold and markout risk acceptable, - extend by
δms (bounded by total schedule SLA).
Control 3: Re-entry backpressure guard
When many expiries occur simultaneously, cap re-entry send rate to avoid reject storms.
- token bucket per venue/symbol
- reserve bandwidth for urgent residuals
- degrade low-priority passive re-entries first
Control 4: Queue-preserving refresh policy
Prefer in-place amend paths that preserve queue priority where venue rules allow. If not available, avoid unnecessary cancel/re-submit loops.
Control 5: Deadline-coupled aggression ceiling
After expiry, aggression can rise, but cap it by real-time ECT burn-rate.
- prevents panic crossing
- avoids paying spread repeatedly after clustered expiries
Calibration workflow
- Reconstruct full child-order state timeline with microsecond ordering.
- Label expiry clusters and re-entry branches.
- Fit expiry hazard + re-entry mode models.
- Build counterfactual simulator (staggered TTL and extension policy).
- Validate by regime: open, midday, close, event windows.
- Run shadow + canary before portfolio-wide rollout.
Promotion gates (example)
Promote only if canary shows:
- ECT reduction >= 15%
- p95 net slippage improvement >= 4 bps in target universe
- EAER reduction >= 10%
- no material underfill increase (
<= 1.0pp) - reject rate not worse by
> 0.3pp
Rollback if:
- 2 consecutive windows with p95 slippage regression > +6 bps
- missed completion SLA breaches threshold
- re-entry rejects spike and persist
Common false conclusions
"Short TTL is always safer."
Not when it synchronizes expiries and forces queue resets."Expiry cost is just market drift."
Often partially self-inflicted by policy timing."More immediate re-submission fixes missed fills."
Can worsen re-entry congestion and trigger reject loops."Arrival benchmark is enough."
Arrival-only hides lifecycle branch costs. You need expiry-state attribution.
Minimal policy sketch
for each resting child:
if near_expiry:
compute fill_hazard, markout_risk
if fill_hazard high and markout_risk acceptable:
extend_ttl_bounded()
else:
expire_or_replace_with_jittered_reentry()
if expiry_cluster_detected:
rate_limit_reentry()
prioritize high-urgency residuals
cap aggression by ECT burn-rate
Desk-level takeaway
TIF is not only a compliance/control knob — it is a slippage surface parameter.
If you ignore expiry clustering and re-entry dynamics, you will repeatedly pay an invisible queue-reset tax.
Model the lifecycle explicitly, then control expiry timing as carefully as price and size.