Predictefy Docs
Browse documentation

Backtest a strategy on historical candles

Pull OHLCV for an outcome, read the quality vocabulary, and refuse to backtest on data that cannot support it.

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.

Request

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

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"
ParameterDetail
outcomeIdThe series. id is a compatibility alias; marketId also accepted
resolution1s 5s 10s 30s 1m 5m 15m 30m 1h 4h 6h 1d
start / endISO timestamp or epoch milliseconds
limit1–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.

The candle record

{
  "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:

ValueMeaning
true-candleThe venue published this candle
point-derivedBuilt from point-in-time prices
trade-derivedBuilt from the trades tape
rest-derivedA coarse REST trade-tape candle
book-derivedBuilt from order-book state
rollupAggregated 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.

Filter before you backtest

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.

What history you can actually read

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.

Cost

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.