The Backtest Fills at the Mid. The Market Doesn't.
Published 2026-07-26 · By Drew Shelem
The anatomy of the gap between a backtest and live trading on Polymarket’s 5–15-minute crypto markets
Article 5 of the cycle. The previous one — what to do once your live result has diverged from the backtest — was about diagnosing a divergence. This one is about what divergence is expected even when nothing is broken, and, more importantly, where it actually hides.
The spread turned out to be the floor
The folklore about trading bots goes like this: “a backtest always lies, because in reality the spread and the fees eat your edge.” Half of that claim is testable, so I tested it — on 60,072,872 ticks over 14 days across six flagship markets (BTC, ETH, SOL × 5m and 15m).
The result is counterintuitive. The median spread on these markets is one cent. That’s the minimum price increment — literally the floor. The spread sits on the floor almost all the time and eats no edge at all. And the fee is already in the model — it isn’t “hidden,” it’s known in advance from a formula.
So where does the gap come from? Not from where the folklore points. That’s what this article is about.
The thesis, and the condition under which I’d admit it’s wrong
Thesis. On Polymarket’s liquid flagship markets, the gap between backtest and live is not the median spread (≈1¢, negligible) and not the fee (already modeled). The gap lives in two places invisible to “fill at the mid, always” execution:
- The spread tail in the final seconds before the market resolves — exactly where the edge on a binary market concentrates.
- Fill assumptions — the backtest’s silent premise that if the signal condition is met, the trade opened, and it opened at the mid. In reality “condition met” is not yet “position exists”: a limit order may not fill, and a market order fills worse. This is a separate and possibly heavier half of the gap — it gets its own section below.
The falsification condition — and it’s measurable, therefore checkable. The thesis would be wrong if:
- the median spread were larger than the fee (then “the fee is the largest known cost” collapses) — but it came out smaller almost everywhere;
- the spread tail did not concentrate near expiration (then “the gap is in the last minute” collapses) — but it does: on SOL-5m the p90 spread in the final 30 seconds reaches 10¢ while the median is unchanged.
I ran both attempts to refute before writing a line of this. Both failed in my favor — on the data below.
Definitions and frame
So there’s no ambiguity downstream: four terms and one fact about the backtester itself.
Bid, ask, mid. The book has a best buy price (bid) and a best sell price (ask). The middle is mid = (bid + ask) / 2. The difference ask − bid is the spread.
Taker and maker. If you take what’s on offer right now, you’re a taker: you buy at the ask, sell at the bid. You “cross the spread.” If you post a limit order and wait for someone to come to you, you’re a maker: you pay no fee and receive a rebate, but you pay in a different currency — your order fills not always, and not at a convenient moment.
“Round trip.” One entry plus one exit. It’s convenient to count costs per round trip, because you cross the spread twice: on the way in and on the way out.
What the backtester actually does (and the paper bot on the same engine). This is the key fact; without it the article means nothing. Execution runs at the snapshot mid. The taker fee is charged — but off that same mid. The spread, the walk through the book, slippage, and the probability of a non-fill are not in the model (the user can set slippage manually; by default there is none). So the backtester lives in an idealized world: the middle of the book, an instant 100% fill, and an honest fee.
Hold that arrangement in mind: the backtest gets the fee right. The spread and the fills, it does not. So the gap is not about the fee. The gap is the price of everything the model leaves out.
The numbers
From here on, measurements. Every table comes with a plain-language note: what the number is, why it matters, and where to look. All figures are from one window (14 days, 6 markets, 60M ticks); the limits of that window are collected honestly in their own section below.
Book completeness and arbitrage — the backdrop that makes the rest measurable
| Check | Result | What it means in plain terms |
|---|---|---|
| One-sided book (only a bid or only an ask) | 0% of 60M ticks | The book is almost always two-sided — the mid is always defined, so the spread can be computed honestly |
| Arbitrage |price_up + price_down − 1| | median and p90 = 0.0000 | YES and NO sum to one almost perfectly — i.e. the Down side mirrors Up |
| — same arbitrage, maximum | 0.99 (single outlier) | One anomalous tick, probably a computation artifact — not a property of the market |
Note for the beginner. The first row is hygiene: if the book were often one-sided, the “spread” would be fiction in places. It isn’t. The second row is why I only measured the Up side: since up + down ≈ 1 always, the Down spread is built the same way. (Strictly, that’s an inference from the arbitrage figure, not a separate measurement of Down — see limitations.)
Spread by price — where it actually sits
| Market | Spread across price deciles 0.1–0.9 | Extreme deciles | p90 |
|---|---|---|---|
| BTC-15m, BTC-5m, ETH-15m, ETH-5m | median 1.00¢ everywhere | 1.00¢ | 1.00¢ everywhere |
| SOL-15m, SOL-5m | median 1.00¢ everywhere | 1.00¢ | 2–3¢ at mid prices |
Note. “Median 1.00¢ everywhere” is not “cheap,” it’s the floor. One cent is the minimum quote increment; the spread physically cannot be tighter. The takeaway: on these markets the spread is pinned to the bottom almost all the time, and as a standing cost it’s tiny. The only difference between BTC/ETH and SOL is in the tail (p90), and only SOL occasionally widens to 2–3¢. Keep this in mind — the interesting part isn’t here, it’s in the time cross-section.
Spread versus fee — which one wins
The crypto taker fee is computed as 0.07 · p · (1 − p) (primary source: Polymarket’s help center; re-check the rate on your publication date — 2026 fees keep changing). In cents per share:
| Entry price p | Fee, ¢/share | Median spread, ¢ | Which is larger |
|---|---|---|---|
| 0.50 | ≈1.75 | ~1.00 | fee (by a lot) |
| 0.65 | ≈1.59 | ~1.00 | fee |
| 0.83 | ≈1.00 | ~1.00 | parity |
| 0.90 | ≈0.63 | ~1.00 | spread |
| 0.95 | ≈0.33 | ~1.00 | spread |
Note — and an important caveat. It’s tempting to say “the fee is always larger than the spread.” That’s wrong. The crossover is around price 0.83: in the middle of the range (0.17–0.83) the fee is bigger, toward the extremes the spread is. And here’s the practical consequence. The viral bots from the cycle’s first article — the ones that “buy the obvious side” at 0.85 and up — operate precisely in the zone where the spread is no smaller than the fee. The precise statement is: which one dominates depends on the entry price; in the middle it’s the fee, near the extremes it’s the spread. Not “the fee, always.”
But both of these are about the price level. The backtest/live gap is about something else.
Spread versus time to resolution — this is where the gap lives
I sliced the spread into fine buckets by time to expiration. Median and p90, in cents:
| Market | 0–30 sec (median / p90) | 30 sec – 1 min | Further out |
|---|---|---|---|
| BTC-15m | 0.50 / 1.00 | 1.00 / 1.00 | 1.00 stable |
| ETH-15m | 0.10 / 2.00 | 0.90 / 2.00 | 1.00 stable |
| SOL-15m | 0.20 / 5.00 | 1.00 / 5.00 | 1.00, p90 holds 2–3¢ |
| SOL-5m | 1.00 / 10.00 | 1.00 / 4.00 | 1.00, p90 ~2¢ |
Note — this is the article’s key table, read it slowly. Look at the two columns in the first bucket. The median in the last 30 seconds stays low — the typical tick still costs about a cent. But the p90 — the 90th percentile, i.e. “the spread in one of the ten worst moments” — explodes: up to 5¢ on SOL-15m, up to 10¢ on SOL-5m. That’s ten times the median and nearly six times the mid-price fee.
Here’s what that means literally. If your strategy enters or exits in the final minute — and on a binary market the edge concentrates exactly there, where the outcome is nearly decided — then with meaningful probability you land not on the median cent but in the tail. A backtest that fills at the mid never pays that tail. It pays exactly zero spread, always. The difference between “zero” and “occasionally 10¢” is one of the two sources of the gap.
Putting it together: what the backtest omits
Per round trip, for a taker entry:
- Median case: omitted spread ≈ 1¢ per round trip. Against the already-modeled fee of ~1.75¢ in the middle, that’s noise. Here the backtest is almost honest.
- Tail case (final seconds): omitted spread up to 10¢ per round trip. Here the backtest is optimistic, and by a lot.
But that’s only the first source — the execution price. There’s a second, separate and deeper one: the assumption that the trade even opened. It needs its own section — it’s next.
The backtest’s core assumption: “condition met” is not yet a trade
Here’s the deepest source of divergence, and it isn’t about price at all. It’s about whether the trade happened.
The backtester is simple: the signal condition fires, the trade is booked, at the mid, immediately. Between “condition met” and “position opened at the intended price,” reality puts an order book, and the book is under no obligation to let you through. Two order types — two ways reality departs from that assumption.
A limit order may not fill at all. You want to buy at 0.55. For that to happen, the market has to come to 0.55, and there has to be enough size left there after everyone ahead of you in the queue. If the price jumped 0.52 → 0.60 — and on these markets, as the spread tail showed, that’s exactly what happens in the last seconds — your limit at 0.55 never fills. You missed the trade the backtest booked. And you missed precisely the case where the signal was right and the move happened.
A market order will fill — but worse. It takes the ask, and if there’s little size at the top of the book, it walks down through the next levels. In a calm moment that’s the median cent. In a sharp move it’s the tail, up to 10¢ on SOL-5m, plus the walk through the book. A mid-price backtest pays neither.
See the asymmetry in full: exactly when the trade matters most — a sharp move along your signal — the safe order (the limit) fails, and the aggressive order (the market) fills at the worst price. The backtest’s “fill at the mid, always” is what no real order gives you: it has the certainty of a market order at a price better than a limit order. That’s neither an optimistic nor a pessimistic assumption — it’s a nonexistent one.
And the consequence runs deeper than a per-trade cost. Missed limits mean the live bot trades a different set of trades than the backtest — not the same set at a slightly worse price, but a different sample. The trades that get missed are disproportionately the “easy” ones: the price moved decisively, the signal was right, but you weren’t taken at the good price. So the win rate and edge of the live sample can differ from the backtest not because of costs, but because it’s a different sample of opportunities, filtered adversely. This is a mechanism separate from the spread, and it is no less important.
None of this did my run measure: I did not compute the fill rate, the share of missed limits, or the depth of the book walk. The mechanism is a fact of how an exchange book works, not a hypothesis. But its magnitude on these markets I do not know, and I won’t present it as known. There’s exactly one way to measure it — compare which trades the paper bot took against which ones actually opened live on the same signal.
The full map: why a backtest doesn’t match live
Let me gather all the causes in one place and split them by an honest criterion: what is measured on this data, what is mechanically sound but unmeasured here, and what is out of scope for this article (closed in other articles of the cycle). The “direction” column shows which way the cause pushes the live result.
| Cause | Mechanism | Status on this data | Direction |
|---|---|---|---|
| Crossing the spread (taker) | buy@ask / sell@bid instead of mid | measured: median 1¢, tail up to 10¢ | live worse |
| Spread tail at expiration | p90 explodes in the final seconds | measured | live worse, badly — if you trade there |
| Limit non-fill | price ran away, queue, no size at the level | mechanism is fact; magnitude unmeasured | missed trade → different sample |
| Book walk (market order) | depth is finite, size digs deeper | magnitude unmeasured | live worse |
| Adverse selection (maker) | you get filled when you’re wrong | unmeasured | live worse |
| Latency | price moves over signal → order → fill, especially in the tail | unmeasured | live worse (usually) |
| Own market impact | your order moves the price / signals others | unmeasured; small on liquid | live worse |
| Resolution-source mismatch | backtest resolves off its own source/timestamp, the market off its oracle | unmeasured; match your source to the settlement source | either way — false wins and losses on near-ties |
| Fee | charged off the actual fill, not the mid | formula known | negligible — already in the model |
Out of scope for this article (closed elsewhere): look-ahead and survivorship — that’s an incorrect setup, not a gap under a correct one (articles 0–1); regime change between the backtest period and the live period — that’s degradation diagnosis (article 4). Here I hold the regime fixed and speak only about execution.
Note for the beginner — read the “direction” column all the way down. Almost every mechanism pushes live the same way: worse than the backtest. The one exception is a pure maker with a high fill rate (next section). That’s why the rule “the backtest is almost always more optimistic than live” isn’t pessimism of temperament — it’s a consequence of the construction: the “mid + 100% fill” idealization is better than any real execution along every axis at once, save one.
What would refute this
This section is the primary carrier of trust, and it’s also where the most honest counterexample lives.
The counterexample that qualifies the thesis. A pure maker with a high fill rate on a calm market can beat the mid-price backtest: it doesn’t cross the spread and, on top of that, collects the rebate. For it, the live result comes out above the backtest. That doesn’t refute the article — it draws its boundary. The logic “live ≤ backtest − spread” holds for a taker entry. For a maker, the sign of the gap depends on the fill rate and adverse selection, and it has to be measured separately, not inferred from this article.
The measurable conditions on which the thesis would have fallen (and didn’t):
- Had the median spread been ≥ the fee, “the fee is the larger cost” would collapse. Median 1¢ against 1.75¢ in the middle — it didn’t.
- Had the tail not concentrated at expiration, “the gap is in the last minute” would collapse. SOL-5m p90 = 10¢ in the final 30 seconds — it didn’t.
How to check this yourself: take your own tick log with bid/ask, compute ask − bid bucketed by time to expiration, and look at the p90 in the last bucket. If your tail is flat, your strategy is insensitive to this source of gap — and that’s valuable information about it.
Limitations and what I don’t know
Stated plainly, because without it the numbers above could be read for more than they are.
- The spread was measured on the Up side only. Down was not measured separately. That it mirrors Up is an inference from arbitrage (
up + down ≈ 1at the median and p90), not a measurement. The method for Down is identical; it re-checks in a single run if you want it. - The window is 14 days, one snapshot. It’s not a long-run property of the market, and it isn’t checked against a second window. The spread and its tail aren’t stationary; on a different period, at a different volatility, the numbers shift.
- The scope is 6 liquid flagship markets (BTC/ETH/SOL × 5m/15m). Hourly markets do exist on the platform, but they weren’t in this sample — the conclusion may differ for them. Less-liquid (non-flagship) markets are out of scope too; an early hypothesis about a “blind zone at the start of the window” was not confirmed on the six flagships, but that guarantees nothing for illiquid ones.
- Not measured: limit fill rate, share of missed trades, adverse selection, latency, book depth and the walk through levels under size, own market impact, resolution-source mismatch. Every one of these mechanisms is sound by market construction (see the map of causes), but I did not measure its magnitude on this data and won’t present it as measured.
- Hold-to-resolution only. If the strategy exits early, the outcome isn’t binary, and part of the “edge in the final seconds” reasoning has to be redefined.
- The single arbitrage outlier of 0.99 is almost certainly a computation artifact, not a real YES/NO dislocation.
What to do tomorrow
The practical layer. Five steps, from cheapest to most expensive.
- Compute the fee in advance — it’s deterministic.
0.07 · p · (1 − p)on crypto, by entry price. It’s the one large cost you can see before the trade. Re-read the rate at the primary source on launch day: Polymarket’s 2026 fees keep changing, and the US exchange uses a different, flat model altogether. - Measure your spread by time to expiration, not on average. The average will lie — it hides the tail. What you need is the p90 in the last bucket, because that’s where the outcome-deciding trades land.
- Don’t assume “condition met” = “trade opened.” This is the backtest’s core silent assumption. Measure it directly: compare the list of trades the paper bot booked against the ones that actually opened live on the same signal. The difference is your missed limits — and it isn’t noise, it’s a systematically “easy” set of trades you’ll come up short on. If you trade as a maker, measure the fill rate and adverse selection; if as a taker, budget for crossing the spread on the way in and out, with the tail included.
- Isolate the gap as a measurable quantity — don’t declare it absent. This is the cycle’s eighth domain trap in pure form: the backtest/live divergence is a quantity in its own right, one to measure. The cleanest way is to run one and the same signal engine in two modes: paper at the mid and live on real fills. Then the only moving variable between them is execution, and the gap shows up explicitly. That’s exactly why in gogobots the paper and live bots share a single signal engine — not so it’s “fast,” but so the gap can’t be hidden. You can measure your own gap on real data without risking capital, in paper mode, free, before any subscription.
- If the strategy lives in the final seconds, model the tail, not the median. The median cent is harmless. The ten-cent tail on SOL-5m is not. A strategy that’s profitable at the median spread and unprofitable at the tail one isn’t a strategy — it’s a bet that execution will always go your way.
FAQ
Why is my live worse than the backtest, if the backtest is clean — no look-ahead, no overfitting? Because even a clean mid-price backtest doesn’t pay what you pay: crossing the spread (especially in the tail near expiration) and the cost of non-fills / adverse selection on limit orders. That’s an expected gap, not a sign of something broken. Measure it first, then go looking for the break.
Does the spread eat my edge on these markets? Most often no — at the median. The median spread here is ~1¢, the floor. What eats edge is the fee (in the middle of the price range) and the tail of the spread in the last minute. If your edge doesn’t live near expiration or near the price extremes, the spread is nearly irrelevant to you.
Is a maker always cheaper than a taker? On fees, yes: the maker pays 0% and gets a rebate. But “cheaper on fees” ≠ “better off.” You pay for it in non-fills and in getting filled at the inconvenient moment. Which one wins has to be measured on your strategy, not assumed.
Can I backtest at ask/bid instead of the mid? You can and should, if you trade as a taker. But swapping the mid for ask/bid only closes the median spread. The tail at expiration and the probability of a non-fill still have to be modeled separately.
In live I get fewer trades and a different win rate than in the backtest. Is that a bug? Probably not. The backtest books a trade the moment the condition is met; live, some limits don’t fill — the price ran away, there wasn’t enough size at the level. The trades that get missed are mostly the “easy” ones where the move happened. So the live sample isn’t the backtest sample at a worse price — it’s a different set of opportunities, selected adversely. A different win rate is expected here, and you can’t compare the metrics head-to-head.
Limit orders or market orders? A trade-off with no free option. A limit pays no spread and no fee but risks not filling — right in the sharp moves where the signal is most valuable. A market always fills but pays the spread, the fee, and the book walk, and in the tail near expiration that’s expensive. The mid-price backtest models neither regime honestly — it takes the best of both. Which regime is yours is up to the strategy, but you have to measure the cost in the regime you actually trade.
Disclaimer
This material is educational and is not financial advice. Past results do not predict future results. Trading on prediction markets carries real risk, up to and including the total loss of capital. Access to Polymarket is restricted or prohibited in some jurisdictions — verify legality where you live before trading.