AymarData
Backtest · Polymarket

Polymarket Backtest: Tick-Level Up/Down Books & Settlements

The canonical Polymarket backtest entry point — reconstruct L2 books, score settlements, and download free samples before you wire a full research loop.

Polymarket Up/Down markets are short-dated binaries with dense books. AymarData stores ~207.0M messages from 2025-06-01 so you can replay windows instead of guessing fills from candles.

This page owns the general “polymarket backtest” intent. Use the 15m and 5m child pages when your query is interval-specific.

Inputs

What you need to backtest Polymarket properly

What a serious backtest needs beyond candles.

Concrete window ids

List with /v1/markets, then pass slugs like btc-updown-15m-<unix> into book/trades/settlement.

Outcome-aware books

Pass outcome=up or down on every book/trades call for two-sided markets.

Realistic fills

Buy at ask, sell at bid; cap size by top-of-book; skip one-sided books.

Settlements

Terminal value comes from /v1/settlement — exclude unresolved windows from P&L.

Coverage

Live Polymarket coverage

Per-family pages under /data/polymarket/* publish weighted counts from PUBLIC_TOTALS.

Messages

~207.0M

From 2025-06-01

Avg events/day

404,297

Platform-wide

Families on site

20 crypto Up/Down

BTC ETH SOL XRP × 5 intervals

As of

2026-10-25

Code

End-to-end Polymarket backtest (REST + requests)

Screens btc-updown-15m for mid < 0.45, records ask entries, and checks settlement when present.

import os
import requests

API = "https://api.aymardata.com/v1"
KEY = os.environ["AYMARDATA_API_KEY"]  # export AYMARDATA_API_KEY=ad_live_…
H = {"Authorization": f"Bearer {KEY}", "User-Agent": "aymardata-backtest/1.0"}

def list_windows(q: str, interval: str, limit: int = 40):
    r = requests.get(
        f"{API}/markets",
        headers=H,
        params={"source": "polymarket", "q": q, "interval": interval, "limit": limit},
        timeout=30,
    )
    r.raise_for_status()
    # De-dupe Up/Down rows that share the same window slug
    seen, out = set(), []
    for row in r.json()["data"]:
        m = row["market"]
        if m in seen:
            continue
        seen.add(m)
        out.append(row)
    return out

def book(market: str, outcome: str, at: str, depth: int = 5):
    r = requests.get(
        f"{API}/book",
        headers=H,
        params={
            "source": "polymarket",
            "market": market,
            "outcome": outcome,
            "at": at,
            "depth": depth,
        },
        timeout=30,
    )
    r.raise_for_status()
    return r.json()

def settlement(market: str):
    r = requests.get(
        f"{API}/settlement",
        headers=H,
        params={"source": "polymarket", "market": market},
        timeout=30,
    )
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()

def mid(snap):
    bids, asks = snap.get("bids") or [], snap.get("asks") or []
    if not bids or not asks:
        return None
    return (bids[0]["price"] + asks[0]["price"]) / 2

THRESHOLD = 0.45
rows = []
for w in list_windows("btc-updown-15m", "15m"):
    market = w["market"]
    hist = w.get("history_from")
    if not hist:
        continue
    # Sample ~60s after history starts
    at = hist.replace("+00:00", "Z")
    if at.endswith("Z") is False and "T" in at:
        at = at + ("Z" if "+" not in at else "")
    snap = book(market, "up", at if "T" in at else hist)
    m = mid(snap)
    if m is None or m >= THRESHOLD:
        continue
    # Buy Up at the ask; score later against settlement when resolved
    entry = (snap["asks"] or [{}])[0].get("price")
    sett = settlement(market)
    won = None
    if sett:
        outcome = str(sett.get("outcome") or sett.get("winner") or "").lower()
        won = outcome in ("up", "yes", "1")
    rows.append({"market": market, "mid": m, "entry": entry, "won": won})

# Counts only — label any P&L/win-rate/edge you add:
# "Illustrative output — not a real result"
print(f"signals={len(rows)} sample={rows[:3]}")

API base https://api.aymardata.com. Set AYMARDATA_API_KEY from your dashboard. Examples use requests. Any P&L, win-rate, or edge figure derived from these snippets is illustrative output — not a real result.

Free samples

Download sample CSV / Parquet

No key required — small files for schema and local smoke tests.

FAQ

Common questions

How is this different from the 15m / 5m backtest pages?

This page targets the generic “polymarket backtest” query and the overall workflow. Interval pages go deeper on 15-minute and 5-minute window structure.

Where is the longer tutorial?

See the step-by-step guide: How to backtest Polymarket Up/Down markets.

Related

Data pages and guides

Catalog leaves for schema and samples; guides for longer walkthroughs.

Run your first backtest with real ticks

Get a key, paste the snippet above, then expand to the full catalog.

Educational content, not trading or investment advice. Past data doesn't predict future results.