- Lookahead biasFailure mode
event_timestampClock- At the touchFill
- Resting depthSize to
A backtest that uses information it could not have had at decision time will always look brilliant, and always fail live. Lookahead bias is the single most common reason a prediction-market strategy that printed money on paper bleeds it in the market. Avoiding it is less about clever code than about one stubborn discipline: at every step, only use what was knowable then.
Everything in this post is reproducible on the captured snapshots, and the same discipline is wired into the visual Strategy Builder and the AI Backtest Agent on the backtest page. But the discipline matters whether you use those tools or roll your own, so here is the method, plainly.
Where lookahead sneaks in
Future-peeking signals
A signal computed on the current bar already contains the outcome you are about to trade on, the classic off-by-one that inflates every result.
- Use prior values only
- Lag every feature
- Decide on closed data
Wrong clock
Aligning on your processing time instead of Polymarket’s emit time leaks order that was not knowable when the decision was made.
- Bracket on emit time
- Attach prior records
- No forward leakage
Free fills
Filling at the mid pretends you traded for free at a price nobody offered, a hidden edge that vanishes the moment you cross a real spread.
- Fill at the touch
- Charge the spread
- Cap to resting size
Build signals on prior values only
The rule is simple and unforgiving: the decision at any instant may only depend on data timestamped strictly before that instant. If your signal uses the same snapshot you are about to act on, you have already seen the future. Lag every feature by at least one observation, and make the decision on data that was fully closed when the decision fired.
Use event_timestamp as the clock
Every snapshot carries two clocks: event_timestamp, when Polymarket emitted the change, and capture_timestamp, when we processed it. For knowing what was true when, event_timestamp is the honest axis. All timestamps are UTC at millisecond precision, so when you join two feeds you attach the most recent prior record from the other, never a record stamped after your decision. That single join rule is what keeps a multi-feed backtest free of forward leakage.
- 1Order every snapshot by event_timestamp, not by capture time or row order.
- 2For each decision instant, take the latest snapshot strictly before it, the most recent prior record, never the concurrent one.
- 3When joining a second feed, a spot reference, another market, attach its most recent prior record on the same UTC clock.
- 4Compute the signal from those prior values, then evaluate the fill on the book that existed at the decision instant.
Fill at the touch and charge the spread
A buy lifts the ask; a sell hits the bid. Filling at the mid quietly hands your strategy half the spread on every trade, an edge that does not exist. Cross the real spread instead: pay best_ask to buy, receive best_bid to sell. The spread field on each snapshot is the cost you should be charging, and on thin prediction markets it is often the difference between a profitable backtest and an honest one.
Size to resting depth, not to wishes
A backtest that assumes you can take any size at the touch is fiction. The book only holds so much at each level, bid_depth_total and ask_depth_total tell you how much was actually resting. Cap your fill to what was there, and walk the ladder for anything larger, paying worse prices as you climb. Resting size is intent to trade, not guaranteed volume, so treat it as a ceiling, not a promise.
The fields the discipline rides on
event_timestampWhat was knowable when, the decision clockbest_bidbest_askWhere a real fill lands, the touch- spreadThe cost to charge on every crossing trade
bid_depth_totalask_depth_totalThe size ceiling at the touch
No survivorship bias either
Lookahead’s quieter cousin is survivorship. If your universe only includes markets that are still around, you have silently dropped the ones that resolved and disappeared, and those carry exactly the outcomes you need to test against. Closed markets stay queryable here precisely so your backtest universe contains the markets that ended, not just the ones still trading.
A backtest is a promise about the future written in the past tense. Lookahead bias is breaking that promise without noticing, every fix here is just keeping it.
Backtest it honestly
The Strategy Builder and AI Backtest Agent bake in prior-value signals and touch fills; the historical guide shows how to pull the windows.
Frequently asked questions
What is lookahead bias in a backtest?
It is using information at decision time that you could not actually have had then, a signal computed on the same bar you trade on, a join that pulls a future record, a fill at a price set after your decision. It makes a strategy look far better on paper than it can ever be live, because the live version never gets to see the future.
Should I align on event_timestamp or capture_timestamp?
On event_timestamp, Polymarket’s own emit time, because it represents what was knowable when. All timestamps are UTC at millisecond precision, so when you join feeds you attach the most recent prior record on that clock and never a record stamped after your decision. capture_timestamp is useful for measuring our processing latency, but using it for decisions would let processing order leak into the result.
Why fill at the touch instead of the mid?
Because a real buy lifts the ask and a real sell hits the bid. Filling at the mid hands your strategy half the spread on every trade, an edge that does not exist. Pay best_ask to buy and receive best_bid to sell, and charge the spread field as the cost. On thin prediction markets that difference often separates a profitable-looking backtest from an honest one.
How does survivorship bias differ from lookahead bias?
Lookahead bias is using future information; survivorship bias is testing only on markets that still exist, silently dropping the ones that resolved and disappeared, which carry the outcomes you most need. Closed markets stay queryable here, so you can build a backtest universe that includes the markets that ended rather than only the ones still trading.



