Guide
Making Polymarket backtests reproducible
If someone cannot re-run your backtest and get your numbers, the result is an anecdote. Reproducibility on Polymarket comes from pinning four things: the data window, the markets, the strategy parameters and the execution assumptions.
What this guide covers
- Pin the sample: the coin, market_type, days window and sampleSize must all be recorded
- Pin the strategy: entry list, exit, taker_fee and trade_lag_seconds are part of the result
- Pin the fill model: mid, touch or walk-the-book changes the numbers materially
- Point-in-time endpoints let a re-run read the same observations a prior run used
What to pin
A backtest result is a function of four inputs: the data window, the set of markets sampled, the strategy parameters and the execution assumptions. Change any one and the number changes. Record all four or you have not recorded the result.
The archive's report includes the trades and exit reasons, which you should store alongside the summary metrics. The summary alone cannot be reproduced if the underlying trades are lost.
| Input | Fields |
|---|---|
| Data window | days, start/end timestamps |
| Markets | coin, market_type, sampleSize |
| Strategy | side, entry[], exit, max_trades_per_market |
| Execution | taker_fee, trade_lag_seconds, fill model |
Point-in-time data as the anchor
The strongest form of reproducibility reads only point-in-time observations: the book at or before each decision, the reference row at or before each decision. Because the archive stamps every observation, a re-run can request the same instants and see the same values.
This is why snapshot-at exists. It returns the nearest observation at or before a time, so a documented set of decision timestamps reproduces the same inputs deterministically rather than depending on when the query ran.
Recording the execution model
The fill model is the most-often-omitted input. A result computed with mid fills and one computed with walk-the-book fills are different results wearing the same strategy name. Write down which was used, and at what size.
If you publish a number, publish the assumptions beside it - fee, lag, fill model, size. A reader who disagrees with an assumption can then re-run with their own, which is far more useful than a bare win rate.
A reproducibility checklist
Before calling a result done: confirm the data window is recorded to the timestamp; confirm the strategy parameters are stored verbatim; confirm the fill model and size are stated; confirm the trades table is saved; and confirm a fresh run with the same inputs returns the same metrics.
The last step is the one people skip. Run it once yourself, from a clean slate, and the reproducibility of the result stops being an assumption.
Code examples
import json
import requests
BASE = "https://api.polyorderbooks.com/v1"
HEADERS = {"X-API-Key": "your-key", "Content-Type": "application/json"}
run = {
"data": {"days": 7},
"markets": {"coin": "btc", "market_type": "5m", "sampleSize": 100},
"strategy": {"side": "yes",
"entry": [{"type": "reference", "change": 0.001}],
"exit": {"type": "hold_to_expiry"}},
"execution": {"taker_fee": 0.002, "trade_lag_seconds": 1,
"fill_model": "walk-the-book"},
}
request_body = {
"strategy": {
"market": run["markets"],
"side": run["strategy"]["side"],
"entry": run["strategy"]["entry"],
"exit": run["strategy"]["exit"],
"taker_fee": run["execution"]["taker_fee"],
"trade_lag_seconds": run["execution"]["trade_lag_seconds"],
},
"sampleSize": run["markets"]["sampleSize"],
"days": run["data"]["days"],
}
r = requests.post(f"{BASE}/ai/backtest", headers=HEADERS, json=request_body)
r.raise_for_status()
with open("result.json", "w") as f:
json.dump({"inputs": request_body, "report": r.json()["report"]}, f)Free tier
The Starter plan is free, includes order books, prices, and metrics at 250ms resolution, with 3 days of history, 60 requests/min, 1,000/day, 1 free AI backtest, and 3 strategy backtests. No credit card required.
Paid data windows from $19/mo extend history to 30–120 days at higher throughput. Backtest AI add-on is +$19/mo or standalone at $29/mo.
FAQ
What makes a backtest reproducible?
Pinning the data window, the sampled markets, the strategy parameters and the execution assumptions - fee, lag and fill model - so a fresh run returns the same numbers.
Why save the trades table?
Summary metrics alone cannot be re-derived if the underlying trades are lost. Storing the trades and exit reasons makes the result auditable.
How do point-in-time endpoints help?
They return observations at or before a recorded time, so a documented set of decision timestamps reproduces the same inputs on any later run.
What is the most-often-missing input?
The fill model. Mid and walk-the-book fills produce different results under the same strategy name, so the model and order size must be recorded.