Run a backtest
POST https://rest.haasapi.com/v1/backtests
Runs a script backtest as a job resource: the request is accepted with 202 and a server-minted backtest_id - poll GET /backtests/{backtest_id} until the status is finished or failed. Acceptance does NOT verify the script exists or the market has synced price history; either problem surfaces as a failed status on the poll. Order sizing not scripted explicitly simulates with a fixed 100-quote-unit trade amount.
Requires the labs scope.
Request body
Section titled “Request body”{ "script_id": "7a4b2f91d6e53c8942a0bd7e1f5896c4", "account_id": "af85799d-a214-4fd2-92ea-443c5eed18ad", "market_tag": "BINANCE_BTC_USDT", "interval": 5, "start_at": "2026-08-28T20:30:00Z", "end_at": "2026-08-29T20:30:00Z"}CreateBacktestRequest
Section titled “CreateBacktestRequest”| Field | Type | Description |
|---|---|---|
script_id |
string | The HaasScript to backtest, from GET /v1/scripts. NOT verified before acceptance: an unknown id is accepted and the job then reports failed. |
account_id |
string | The account whose fees and wallet the simulation uses, from GET /v1/accounts. Prefer a simulated account. |
market_tag |
string | The market to backtest on, from GET /v1/markets. The market needs synced price history on the server; a market without it leaves the job queued until it fails. |
interval |
integer (int32) | Chart interval in minutes - one of the supported chart intervals (1, 5, 15, 60, 1440, …). |
start_at |
string | Start of the window to backtest, RFC 3339 (e.g. 2026-08-28T20:30:00Z). |
end_at |
string | End of the window to backtest, RFC 3339. Must be after start_at; the maximum window length depends on your plan. |
Example request
Section titled “Example request”curl "https://rest.haasapi.com/v1/backtests" \ -X POST \ -H "Authorization: Bearer hk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"script_id":"7a4b2f91d6e53c8942a0bd7e1f5896c4","account_id":"af85799d-a214-4fd2-92ea-443c5eed18ad","market_tag":"BINANCE_BTC_USDT","interval":5,"start_at":"2026-08-28T20:30:00Z","end_at":"2026-08-29T20:30:00Z"}'Response
Section titled “Response”202 Accepted - the job was queued; poll its status. Returns a BacktestJob.
{ "backtest_id": "6f19dc7db5904c27a87c5d684ff8a889", "status": "queued", "script_id": "7a4b2f91d6e53c8942a0bd7e1f5896c4", "account_id": "af85799d-a214-4fd2-92ea-443c5eed18ad", "market_tag": "BINANCE_BTC_USDT", "interval": 5, "start_at": "2026-08-28T20:30:00Z", "end_at": "2026-08-29T20:30:00Z", "result": null}Fields
Section titled “Fields”BacktestJob
Section titled “BacktestJob”| Field | Type | Description |
|---|---|---|
backtest_id |
string | Backtest identifier, minted by the server on POST /v1/backtests - the id every other backtest endpoint accepts. |
status |
BacktestStatus | Job status. queued - accepted, loading the script and price history; running - executing; finished - done, result is set; failed - the execution failed. A failed job keeps no stored record, so it eventually answers 404. |
script_id |
string, nullable | The HaasScript the backtest runs. null when the stored record no longer carries it. |
account_id |
string, nullable | The account whose fees and wallet the simulation uses. null while the backtest is still executing - the live execution record does not carry it. |
market_tag |
string, nullable | The market the backtest ran on - joins against market_tag on /v1/markets. null while the backtest is still executing. |
interval |
integer (int32), nullable | Chart interval in minutes. Echoed on the 202 answer; null on later polls - the stored record does not carry it. |
start_at |
string, nullable | Start of the backtested window, RFC 3339 UTC. null while the backtest is still executing. |
end_at |
string, nullable | End of the backtested window, RFC 3339 UTC. null while the backtest is still executing. |
result |
BacktestResult | The result summary. Set only when status is finished; null otherwise. |
BacktestResult
Section titled “BacktestResult”| Field | Type | Description |
|---|---|---|
realized_profit |
string (decimal) | Realized profit over the backtested window, in profit_currency. |
fees |
string (decimal) | Total fee costs, in profit_currency. |
roi |
string (decimal) | Return on investment, percent. |
profit_currency |
string | The currency the profit and fee figures are denominated in. |
orders_filled |
integer (int32) | Orders that filled during the backtest. |
positions_closed |
integer (int32) | Positions closed during the backtest. |
win_rate |
string (decimal) | Winning positions as a percentage of closed positions, 0-100. |
Errors
Section titled “Errors”All errors are RFC 9457 problem documents.
| Status | Description |
|---|---|
400 |
The request is malformed (invalid limit, unusable cursor, …). (code: invalid_request) A field is missing or malformed, or end_at is not after start_at. (code: invalid_request) |
401 |
Missing, malformed, disabled, or revoked API key. (code: invalid_api_key) |
403 |
The requested window exceeds your plan’s backtest depth, or your plan lapsed. The detail names the limit. (code: forbidden) |
404 |
The script id is malformed. An unknown-but-well-formed id is ACCEPTED and fails on the poll instead. (code: script_not_found) |
409 |
A request with this Idempotency-Key is still being processed. (code: idempotency_in_progress) |
422 |
The Idempotency-Key was already used with a different request. (code: idempotency_key_reuse) |
500 |
An unexpected error inside the API. Nothing about the request caused it; retry, and quote the instance value if it persists. (code: internal_error) |
502 |
An internal service answered in a way the API could not serve. Retry. (code: upstream_error) |
503 |
The request could not be served right now. Retry with the same key - do not discard it. (code: temporarily_unavailable) |
504 |
An internal service did not answer within the deadline. Retry with backoff. (code: upstream_timeout) |
{ "type": "https://docs.haasapi.com/errors/invalid_request", "title": "Invalid request", "status": 400, "detail": "The request is malformed (invalid limit, unusable cursor, …).", "code": "invalid_request"}