Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.predexon.com/llms.txt

Use this file to discover all available pages before exploring further.

The most accurate way to backtest a prediction-market strategy is to replay the orderbook snapshots that actually existed at decision time — not a candle, not a mid price, the real bids and asks. Predexon stores per-token orderbook snapshots starting January 1st, 2026 and exposes them as a free, unlimited endpoint.
Free & unlimited. GET /v2/polymarket/orderbooks doesn’t count against your monthly quota on any plan.

What you get per snapshot

Each snapshot is a full L2 book for one token at one point in time:
{
  "timestamp": 1736294400000,
  "token_id": "82855...",
  "condition_id": "0x...",
  "bids": [{ "price": "0.62", "size": "5000" }, ...],
  "asks": [{ "price": "0.63", "size": "3000" }, ...]
}
Snapshots are produced at the cadence the market produced them — busy markets give you more snapshots per second, illiquid markets fewer. Timestamps are milliseconds.

Walk an interval

The simplest replay loop: pull snapshots between two timestamps, then iterate.
import requests, time

API_KEY = "YOUR_API_KEY"
BASE = "https://api.predexon.com"
HEADERS = {"x-api-key": API_KEY}

TOKEN_ID = "82855..."          # the outcome you're backtesting
START_MS = 1736294400000       # 2026-01-08 00:00 UTC
END_MS   = 1736380800000       # 2026-01-09 00:00 UTC

cursor = START_MS
while cursor < END_MS:
    snaps = requests.get(
        f"{BASE}/v2/polymarket/orderbooks",
        headers=HEADERS,
        params={
            "token_id": TOKEN_ID,
            "start_time": cursor,
            "end_time": END_MS,
            "limit": 200,
        },
    ).json()

    if not snaps:
        break

    for s in snaps:
        # your strategy sees the book exactly as it was at s["timestamp"]
        best_bid = float(s["bids"][0]["price"]) if s["bids"] else 0
        best_ask = float(s["asks"][0]["price"]) if s["asks"] else 1
        spread   = best_ask - best_bid
        # decide(s, best_bid, best_ask, spread)

    cursor = snaps[-1]["timestamp"] + 1
limit caps at 200 snapshots per call. The walk pattern above advances the cursor to the last snapshot’s timestamp plus one millisecond.

Simulating fills

The honest way to simulate a fill against a historical book:
Order typeSimulated fill
Market buy of size SWalk the asks ascending. Take size from each level until you’ve filled S or exhausted the book. Average price = your fill price.
Market sell of size SSame, walking the bids descending.
Limit buy at P, size SFill only if best_ask ≤ P at the snapshot. Take size from asks ≤ P only.
Resting limit orderAt each subsequent snapshot, check if a trade would have crossed your price. The trades endpoint is the ground truth for what actually traded — match against it.
For resting limit orders, pair orderbook snapshots with the trades endpoint over the same window. The trades tape tells you what actually crossed; the orderbook tells you whether your resting order would have been the resting side.

Slippage and depth checks

You can answer “could I have actually traded $X in this market at the time?” directly from a snapshot:
def simulate_market_buy(snapshot, usd_to_spend):
    spent, filled = 0.0, 0.0
    for level in snapshot["asks"]:
        price = float(level["price"])
        size  = float(level["size"])
        level_cost = price * size
        if spent + level_cost <= usd_to_spend:
            spent  += level_cost
            filled += size
        else:
            remaining_usd = usd_to_spend - spent
            partial_size  = remaining_usd / price
            spent  += remaining_usd
            filled += partial_size
            break
    avg_price = spent / filled if filled else None
    return {"filled_shares": filled, "avg_price": avg_price, "usd_spent": spent}
Run this across every snapshot in your window to get a slippage curve for the size you actually want to trade — a much better sanity check than blindly trusting candle data.

Backtesting tips

  • Use both sides of the market. Subscribe (or replay) Yes and No outcomes. Spreads between them often imply arbitrage opportunities or stale liquidity.
  • Snapshots aren’t tick-level. They’re produced when the book changes. Between two snapshots, assume the book held — don’t interpolate.
  • Watch for empty books. Newly created markets, low-liquidity outcomes, and weekends all produce sparse snapshots. Handle bids: [] and asks: [] gracefully.
  • Pair with the trades endpoint. GET /v2/polymarket/trades gives you the actual fills that happened — useful for validating your simulated execution.

Going live with the same logic

Once a strategy backtests well, the live version uses the same shapes via the orderbook WebSocket channel instead of REST. The book_snapshot event has the same bids / asks shape, and price_change events let you maintain the book incrementally — so your decision function rarely needs to change between backtest and prod.