A trading backtest is only as believable as the data decisions it records. The point is not to make every model conservative. The point is to make every assumption explicit enough that another developer can replay the run and understand why each trade was accepted, rejected, or skipped.
Use this checklist before promoting an options or stock strategy from research to paper trading.
Quick answer
A realistic backtest needs to preserve point-in-time instrument selection, row-level timestamps, source object semantics, quote freshness, spread checks, missing-data behavior, corporate-action handling, fees, slippage, and a manifest that names the data inputs. If any of those are missing, the result may still be useful exploration, but it is not production evidence.
The checklist
| Area | Required question | What to store |
|---|---|---|
| Instrument universe | What symbols or contracts existed at the decision time? | Ticker list, contract query, as_of, filters, and selected identifiers. |
| Source object | Did the model use quotes, trades, aggregates, snapshots, or reference? | Endpoint, object type, request URL, and request id. |
| Timestamps | What did the system know at decision time? | Signal timestamp, row timestamps, bar boundaries, and execution timestamp. |
| Quotes | Was bid/ask evidence available? | Bid, ask, midpoint, spread, quote age, and quote timestamp. |
| Trades | Was last-sale data used only for the right purpose? | Trade price, size, condition context when available, and timestamp. |
| Bars | Were bars complete before the signal fired? | Bar start/end, OHLC, volume, trade count, and missing intervals. |
| Corporate actions | Did deliverables or symbols change? | Contract reference fields, split-adjustment flags, and adjusted-contract reject reasons. |
| Costs | Were fees and slippage included? | Fee model, spread model, fill rule, and reject thresholds. |
| Missing data | What happened when data was missing or stale? | Skip reason, fallback policy, and count of affected opportunities. |
| Manifest | Can the run be replayed? | Code version, data windows, endpoint versions, parameters, cache versions, and environment. |
Point-in-time selection
The most damaging error is selecting instruments from information that was not available yet. For options, discover contracts with the relevant as_of date before requesting quotes, trades, or bars. For stocks, define the ticker universe that existed for the test date and store reference metadata used by the selector.
Do not fix an empty historical universe by substituting today's chain. Reject it and log the reason.
Quote-aware fills
Options backtests usually need quote context for entries and exits, not last trade alone. A fill rule can use midpoint, bid, ask, marketable-limit logic, or a slippage model, but it needs to be visible in the artifact.
At minimum, log:
- Selected contract.
- Signal timestamp.
- Quote timestamp.
- Bid and ask.
- Spread width.
- Quote age.
- Fill price rule.
- Reject reason when the quote is missing, stale, crossed, locked, or too wide.
Missing data policy
Missing data is not one condition. A contract might have no trades, no quotes in the window, no listed series on the research date, no aggregate bar because no qualifying trades occurred, or no entitlement for a quote endpoint. Treat those cases separately.
Good run reports include skip counts by reason. That prevents a strong-looking equity curve from hiding the fact that most opportunities were discarded by data availability.
Promotion gate
Before a model moves to paper trading, require:
- A saved manifest.
- Deterministic replay for a sample period.
- Quote-aware fill evidence for every filled option trade.
- Explicit skip reasons.
- Out-of-sample or walk-forward validation.
- Stress tests for spread, slippage, and missing-data assumptions.
- A paper-trading parity checklist.