Measuring execution quality when the order book is anonymous
If you are simulating a passive strategy, the number you want is queue position: how much size sits ahead of your order at your price. It determines your fill probability, and getting it wrong is the difference between a market-making backtest that looks profitable and one that is.
On prediction-market venues the book is anonymized and published as aggregated L2, so that number is not observable. This post is about what you can measure instead, and how to keep the resulting estimates conservative rather than flattering.
Why exact queue position is off the table
L2 aggregates by price. You see that 12,000 contracts rest at 0.43; you do not see whether that is one order or forty, or when each arrived. There are no order identifiers and no add/cancel/modify events to track. Even a true order-by-order feed would only give you queue position for other people's orders — locating your own still requires your own order acknowledgements, which no historical dataset can supply.
So the honest framing is not 'estimate my queue position' but 'establish bounds that make my fill assumptions defensible'.
What you can bound
- Size ahead at arrival. The resting size at your price the instant you would have posted is an upper bound on what must clear before you fill — everything already there is, by price-time priority, ahead of you.
- Level lifetime. How long that price level persisted before being consumed or withdrawn. A level that typically dies in 400 ms will not host a passive order you intended to leave for a minute.
- Depletion rate. How fast size at a level shrinks, conditional on the mid not moving. This is the closest thing to a fill-rate prior you can get without order IDs.
- Refill behaviour. Whether a consumed level gets requoted, and how quickly. Deep-looking books that never refill after a sweep are thinner than they appear.
The decomposition that actually matters: fills vs cancels
Because Polymarket price_change events carry the new absolute size at a price rather than a signed change, a level going from 5,000 to 3,000 is ambiguous on its face. Either 2,000 contracts traded, or 2,000 contracts were cancelled. Those two worlds have opposite implications for your passive order: in the first, you advanced 2,000 places in the queue; in the second, you may not have moved at all — the cancelled orders could have been behind you.
The fix is to join the trade tape against the book tape on asset, price and time, and attribute as much of each size reduction as the trades explain. The residual is cancel flow. Then look at the ratio. A price level whose size reductions are mostly cancels is not depth you can rely on trading against, and a queue model that treats every decrement as a fill will overstate your fill rate — systematically, and worst in exactly the fast markets you are most interested in.
This is one of the concrete reasons to keep the trade stream and the book stream in the same archive with the same clock. Joining a book feed from one source to a trade feed from another, on timestamps generated by different machines, reintroduces the ambiguity you were trying to remove.
Two clocks, and picking one on purpose
Every record in our raw tape carries recv_ts — the millisecond timestamp at which our capture process received the frame — and preserves whatever timestamp the venue put inside the frame itself (surfaced as book_time in the reconstructed snapshots). These are different clocks and they answer different questions.
- Ordering by receive time models what a remote observer could actually have known at that instant. It includes transport latency and is therefore the conservative choice for anything that claims to be tradeable.
- Ordering by the venue's own timestamp models an idealized, zero-latency view. Useful for studying the venue's internal sequencing; dangerous as a basis for a P&L claim.
Neither is wrong. Silently mixing them is. Pick one, state it, and if the strategy only works under the venue clock, you have learned something important about it.
Slippage against the book that existed
For aggressive orders the exercise is simpler and the discipline is the same: walk the resting depth that was actually there, rather than assuming a fill at mid. In practice:
- Mark the decision against the best bid or ask you would cross, not the mid. Half-spread is not free, and on wide prediction-market books it is most of the cost.
- Walk the ladder for size. Charge yourself every level you consume, and check the reconstructed snapshot's level count — ours carries the true total number of levels on each side, so you can tell 'my order exhausted the visible book' from 'my order exhausted the top fifty levels I was shown'.
- Account for your own impact. If your simulated clip is a meaningful fraction of the level, the counterfactual book after your trade is not the book in the data.
- Check refill over your holding horizon. Exit liquidity is a separate assumption from entry liquidity and deserves its own test.
Sanity checks before you trust any of it
- Drop or flag windows where the book was known to be stale. Our reconstruction marks books that saw a capture gap and have not yet re-anchored on a fresh full-book event — do not compute execution statistics across those.
- Look for crossed or locked books. Persistent crossing after a resync almost always means a reconstruction bug, not an arbitrage.
- On binary contracts, cross-check the complementary side. The two legs of a binary should price close to complementary; a book that looks liquid on one side only is usually telling you the quoting is one-sided rather than that you found free money.
- Re-run your headline result with the sampling resolution halved. If it moves materially, you are measuring your sampling, not the market.
SupaGamma is an institutional-grade historical data platform for prediction markets — trades, L2 order books, OHLCV, and the raw tape across Polymarket, Kalshi, Hyperliquid and more.
See pricing →