Skip to content
Get an API key

Backtest a strategy on historical candles

Historical candles are the input to any backtest. Predictefy labels every candle with where it came from and how good it is, and a backtest that ignores those labels will produce confident numbers from data that cannot support them.

Candles key on an outcome, not a market — a binary market has one series per side. Identifiers covers why.

Terminal window
curl -s "$PREDICTEFY_API_URL/api/polymarket/fetchOHLCV?outcomeId=OUTCOME_ID&resolution=1h&start=2026-01-01T00:00:00Z&end=2026-06-30T00:00:00Z&limit=5000" \
-H "Authorization: Bearer pk_live_YOUR_KEY"
Parameter Detail
outcomeId The series. id is a compatibility alias; marketId also accepted
resolution 1s 5s 10s 30s 1m 5m 15m 30m 1h 4h 6h 1d
start / end ISO timestamp or epoch milliseconds
limit 1–5000

5m, 15m, 30m, 4h and 6h are aggregated at query time from stored history rather than being stored natively. That is not a defect, but it does mean those buckets inherit the quality of whatever they were built from — which the response tells you.

{
"timestamp": 1767225600000,
"open": 0.41,
"high": 0.44,
"low": 0.4,
"close": 0.43,
"volume": 128400,
"source": "official",
"sourceType": "true-candle",
"quality": "ok",
"isTrueCandle": true
}

timestamp, open, high, low and close are always present. Everything else is optional, and the optional fields are the ones that decide whether a backtest is meaningful.

source — where the series came from: official, onchain, write-forward, derived.

sourceType — how the bucket was built:

Value Meaning
true-candle The venue published this candle
point-derived Built from point-in-time prices
trade-derived Built from the trades tape
rest-derived A coarse REST trade-tape candle
book-derived Built from order-book state
rollup Aggregated from finer buckets

qualityok, partial, suspect, or mixed. mixed means the bucket aggregates inputs of differing quality, which is the expected value for a query-time aggregation.

isTrueCandle — the single boolean that separates published candles from reconstructed ones.

function usable(candle) {
// A backtest that mixes published candles with book-derived reconstructions is
// measuring two different things and reporting one number.
if (candle.quality === 'suspect') return false;
if (candle.sourceType === 'book-derived') return false;
return true;
}
const candles = body.data.filter(usable);
const coverage = candles.length / body.data.length;
if (coverage < 0.95) {
throw new Error(`only ${(coverage * 100).toFixed(1)}% of buckets are usable — widen the window or drop the venue`);
}

Decide the rule up front and record it with the result. “Backtested on 1h candles, excluding suspect and book-derived buckets, 98.2% coverage” is a claim someone can check. A bare Sharpe ratio is not.

volume is nullable. A null is “not reported”, not zero — a volume filter that treats null as zero silently discards every venue that does not publish it.

Two limits apply, and they are different:

  • Venue coverage. Not every venue has history for every resolution. Historical data records what exists, including which venues have sub-minute data and in what id format.
  • Plan window. Your plan caps how far back you may read. Requesting beyond it returns PLAN_REQUIRED rather than a silently truncated series — see Credits & billing.

Check meta.provenance on the response: venue-native means every returned bucket came from the venue, predictefy-store means our own store served it, and merged means both. A backtest that spans a provenance change is comparing two datasets.

History reads are priced above catalog reads, and a backtest is many of them — one per outcome per window. Pull once and cache locally; re-running a strategy should not re-read the API. Current weights are in Credits & billing.