REST history or replay
Use REST history when records can be retrieved and ordered after the fact. Use WebSocket replay when the channel replays and event cadence, sequence, local book state, or gap handling is part of the test. Core L4 bulk replay ignores
speed; it uses a checkpoint and bounded event batches instead.
Backtest contract
Declare the venue family, symbol, data family, route or channel, integer-millisecond window, page budget or replay delivery, quality gate, output schema, and request IDs before the run starts. Keep raw API rows separate from derived features or strategy output. Choose one hypothesis that can be tested with one data family, and confirm the symbol’s namespace in Venues and market types if it is not known.Freeze the window
Compute the window once, before any request, and save it with the run. This example uses one hour of Hyperliquid core BTC: the hour that ended one hour ago, a window every plan’s history reaches, including Free’s most recent 30 days. A backtest over older history needs the full retained archive of Build or above.source run-window.env rather than recomputing them, because recomputing selects a different hour. Requests also accept RFC 3339 times, but integer milliseconds keep the window identical across your shell, scripts, and manifest.
Run a REST window
1
Freeze the other inputs
Save the family, symbol, data family, page size, quality threshold, gap policy, and output path with
START and END before calling the API.2
Check data quality for the same window
/v1/data-quality/status and freshness only matter when the window ends near now. Keep these responses with the run even when the job later stops.3
Pull one bounded page
4
Follow only returned cursors
If
meta.next_cursor is present, send that exact value within the declared page budget. Do not write an unbounded loop: stop at the page budget even when a cursor remains.5
Switch surfaces when order matters
If event order rather than returned rows is the requirement, stop the REST run and continue with a replay run below, over the same frozen window. Record it as a separate run.
START and END, and keep the cursor chain and request IDs per page.
Run a replay
1
Bound the replay
Use the frozen
START and END, an explicit channel and symbol, the delivery model, and the speed when applicable. Avoid endless replay jobs.2
Start the replay
set -a; source run-window.env; set +a; node replay.js. Speed 10 is inside every plan’s standard replay limit, including Free’s.3
Handle gaps
Record
gap_detected events and decide whether the run is invalid, partial, or rebuildable. Do not silently interpolate missing market events and present the result as complete.4
Watch consumer lag
If standard replay outpaces the strategy loop or output writer, stop or slow down rather than silently dropping messages. For core L4 bulk replay, reduce the window or stop the run when bounded queues cannot absorb a batch.
Result decisions
Label every run complete, partial, or stopped. Complete means the configured window ran to its stop condition without unresolved gaps, a remaining cursor, or consumer backlog. Partial means the output may still be useful for review, but the manifest names the missing interval, the remaining cursor, or the state to rebuild. Stopped means the strategy output should not be compared against clean runs.Failure and recovery
400: fix the numeric bounds, symbol, or named parameter before retrying.401: repair the key source or header.429: retryrate_limitedwithin the retry budget and stop oninsufficient_credits, as When you reach a limit describes.- A remaining cursor at the page budget: mark the run partial and resume under a new bounded manifest.
- A quality or
gap_detectedresult: stop the derived calculation, narrow the window, or rebuild the relevant state from the documented checkpoint path. - Missing rights for the intended use: stop and read Data rights before creating a benchmark, index, financial product, or bulk distribution.
Run manifest
Store the same fields with every run so results can be compared later.timed at a given speed, or checkpoint_anchored_bulk for L4), the gap policy, and the replay events that ended the run. WebSocket replay lists every field.
The zero UUID is a shape placeholder only. Replace it with the UUID returned by each request.