All research
7 min readMicrostructureExecution

The cancel-race: a measurable adverse-selection mechanism

Resting quotes that fill in the last fraction of a second before a tick move are systematically toxic. A short note on detection, mitigation, and the engineering pitfall in the middle.

On any maker strategy that quotes near touch, a recognisable share of the bad fills look the same: the bot sent a cancel for a stale quote, but the exchange filled it first. The fill lands a few tens of milliseconds before the cancel is acknowledged. The mid then moves immediately against the position. We have started calling these cancel-race fills, and they are one of the more tractable adverse-selection mechanisms to identify and partially mitigate.

What the pattern looks like

Three observations co-occur:

  • An outbound cancel request for an order at price P, sent at time t0.
  • A fill notification for the same order at the same price P, received at t0 + a few to a few tens of ms.
  • A mid move away from P within the next 100 ms–1 s, of magnitude larger than the typical fill-time markout on this surface.

The cancel-then-fill pair is the operational signature; the markout is what makes it expensive. The bot wanted out of that quote because its model thought the quote was stale; the venue's order book agreed (something arrived first) and converted the resting quote into a fill at the now-bad price.

Why these fills are worse than average

A cancel-then-fill race is, by construction, conditional on the bot having already decided the quote was wrong. The model that wants the cancel is the same model that quoted the price; if it changed its mind, it has some reason — typically a fresh signal that the mid is about to move. The fills that beat the cancel are precisely the fills the strategy has already classified as undesirable. Their markout is conditional on a signal that didn't make it into the quote-management decision in time.

Quantitatively, in the same OKX SUI-USDT-SWAP session referenced in the markout-vs-rebate note, cancel-race fills carried markout roughly 2–3× the session-wide mean. They were a small fraction of total fills but a disproportionate share of the session's realised adverse-selection cost. We do not yet have a clean decomposition of how much of the session-mean markout is attributable to this specific mechanism, but the directional signal is robust.

Detection

On most exchange APIs the detection is straightforward if you have millisecond-resolution timestamps on both order events and fill events:

  • Match each fill to the cancel request (by client order id) that was sent for the same order.
  • A cancel-race fill is one where the cancel request timestamp is earlier than the fill exchange-timestamp by less than a small threshold (we use 100 ms; the right number is venue-specific and depends on round-trip latency to the matching engine).
  • Record this as a per-fill boolean on your fills table; later stratify markout by it.

The detection is post-hoc. You cannot prevent the fill at the moment of cancel — by definition the order matched before the cancel landed. What you can do is structure the strategy so that the rate of attempted late cancels is low to begin with.

Mitigation: quote aging, properly enforced

The single most effective mitigation is a strict age cap on resting quotes, enforced at the quote-management layer rather than waiting for a reprice signal. Two thresholds work well:

  • Behind-touch max age. A quote that has been behind the inside for more than a short interval is cancelled regardless of whether the repricing logic thinks it would be a meaningful move. The assumption is that “behind the touch” for that long means the book has moved past you and the fill, if it comes, will be adverse.
  • At-touch max age. Even at the touch, a quote older than a longer interval is cancelled. Quotes that have sat at the touch for seconds without filling are more likely to be queue-positioned behind informed participants who will move first when the mid moves.

The exact thresholds are venue- and symbol-specific — short enough that adverse fills are starved of opportunity, long enough that valid quotes get a chance to work. They have to be calibrated against the venue's cancel-roundtrip latency and the symbol's typical mid-move cadence; copying values across venues does not work. The principle is general. The mistake to avoid is treating these caps as “eventually we'll reprice” — they are hard cancels, invoked synchronously, that bypass whatever the broader replace logic would have decided. The cap exists precisely because the broader replace logic is the part that's losing the race.

The engineering pitfall: defined-but-not-enforced

We have seen this pattern enough times now that it deserves a callout. A strict age cap is the kind of thing that gets defined early, unit-tested, given a configuration field — and then never actually wired into the live quote-management path. The reprice logic still runs; the strict cap sits in a module that nobody calls. The bot looks like it has the protection it doesn't have.

Two checks catch this. First, log every cancel with its trigger reason and count the distribution of trigger reasons per session; if the “strict age cap” reason has zero or near-zero counts despite a session of two-sided quoting, the cap is almost certainly not wired in. Second, in any postmortem on a bad markout day, look for cancels that should have fired under the cap and didn't — they will be visible as quotes that were behind touch for several seconds and then filled at the old price. Either of those checks will surface the unwired-cap bug within a single session of operation.

What this does not address

Strict age caps reduce the number of attempted late cancels by cancelling more aggressively in the first place — they trade some valid quotes (that would have filled benignly) for protection against the bad ones. On a venue with strong post-only protection and good cancel reliability, the trade-off is favourable; on venues with worse cancel latency or higher cancel rejection rates, the same threshold may cause more harm than it prevents.

And the cancel-race is one mechanism, not the mechanism. Quote toxicity also shows up as: stale-mid quotes (the bot's mid feed lags the venue's book), one-sided informed flow (everyone hits you on one side for a minute), and cross-venue lead-lag (you fill on the slow venue at a price that the fast venue has already moved past). These mechanisms have their own signatures and require their own diagnostics. Cancel-race is notable because it is the cheapest one to detect from already-logged data and the most directly addressable through execution discipline rather than alpha work.