Build a backtester

The hard part of a backtester was never the loop; it's the data honesty. ?asof= reruns any query against the market as it was — delisted names still in it — so the loop is the only part you write.

The recipe

1. Pick candidates without look-ahead

An as-of scan evaluates your entry condition with only what was knowable at that moment.

curl -s -X POST "https://api.tickerbot.io/v2/scan" \
  -H "Authorization: Bearer $TICKERBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "q": "rsi_14 < 30 AND above_sma_200 AND market_cap > 1e9",
        "asof": "2024-01-05" }'

Delisted names keep their rows and bar history — ask about SIVB or LEH and you get records, not a 404 — so match sets are not conditioned on survival.

2. Walk the dates

The backtest loop is the same call in a for-loop: one as-of scan per rebalance date.

for (const date of rebalanceDates) {          // e.g. every Monday, 2020 → today
  const res = await fetch('https://api.tickerbot.io/v2/scan', {
    method: 'POST',
    headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ q: ENTRY_CONDITION, asof: date }),
  })
  const { results } = await res.json()
  positions = strategy.rebalance(positions, results, date)   // your rules
}

3. Price the trades

Fills and the equity curve come from /v2/series: up to 50 tickers × 25 columns on one aligned time grid.

curl -s "https://api.tickerbot.io/v2/series?tickers=AAPL,MSFT,RIOT\
&columns=close,volume_today&interval=1d&from=2024-01-01&to=2024-06-30" \
  -H "Authorization: Bearer $TICKERBOT_API_KEY"

# One shared grid: each row is a date, every ticker's close aligned.

4. Take it live

The exact q the backtest validated becomes the live subscription — no rewrite between research and production.

curl -s -X POST https://api.tickerbot.io/v2/scan/subscribe \
  -H "Authorization: Bearer $TICKERBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "q": "rsi_14 < 30 AND above_sma_200 AND market_cap > 1e9",
        "target_url": "https://your-bot.example.com/hook" }'

Endpoints used

EndpointRole in this build
POST /v2/scan + asofThe candidate set at any past moment: the backtest’s selection side
GET /v2/seriesAligned history, 50 tickers × 25 columns: fills and the equity curve
GET /v2/tickers/{ticker}/bars/{interval}OHLCV bars down to 1s: intraday fill detail
POST /v2/scan/subscribeThe validated condition, running live: same q

More guides