Chord Backtests API
Bar-close strategy backtests over Chord's stored candles: any Hyperliquid perp, 13 FX pairs and metals. Simulated, nothing is traded.
Bar-close strategy backtests over Chord's stored candles: any Hyperliquid perp, 13 FX pairs and metals. Simulated, nothing is traded.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/getchord-xyz:getchord-xyz-backtests-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Chord Backtests API
version: '2026-10-05'
summary: Trade, data and agent endpoints for eight EVM chains (Ethereum 1, Arbitrum One 42161, Base 8453, BNB Chain 56, Avalanche 43114, Monad 143, Arc 5042, Robinhood Chain 4663) and Solana (data and swaps).
description: 'Chord is a non-custodial trading app and on-chain data index. The API has three kinds of endpoint:
- **Trade**: `/quote` finds the best route across every venue on the chain; `/swap` turns it into a transaction you sign yourself. Chord never holds funds or keys.
- **Read**: every swap on the chain, rolled up by token, venue and hour, from Chord''s own index — tokens, launches, holders, top wallets, portfolio PnL, liquidity positions and pools. No key needed. Avalanche, Base, BNB Chain, Arbitrum One and Ethereum have the same rollups under `/chains/{chain}/*`; Solana has its own data family under `/chains/solana/*` (base58 addresses, case-sensitive) and its swaps under `/sol/trade/*`.
- **Agent**: the analyst (`/ask`) and the streams.
Amounts are decimal strings in the token''s smallest unit. Timestamps are Unix seconds unless the field is `updatedAt` (milliseconds). `GET /portfolio/{wallet}` and `/portfolio/{wallet}/activity` differ on both: amounts there are in whole tokens and every time is in milliseconds. Reads answer anonymous callers at 60 requests a minute per IP; an `X-API-Key` header raises that to your plan. Every non-2xx body is `{ error, message }`.'
contact:
name: Chord
url: https://getchord.xyz/developer
x-logo:
url: https://getchord.xyz/chord/pwa-192x192.png?v=20261019
servers:
- url: https://api.getchord.xyz/v1
description: Production
tags:
- name: Backtests
description: 'Bar-close strategy backtests over Chord''s stored candles: any Hyperliquid perp, 13 FX pairs and metals. Simulated, nothing is traded.'
paths:
/backtest:
get:
tags:
- Backtests
operationId: backtest
summary: Backtest a strategy
description: 'One bar-close strategy replayed over stored candles of a Hyperliquid perp or an FX pair / metal. Simulated: nothing is traded.
**Model** (the same text comes back in `assumptions`):
- A signal is computed on a CLOSED bar and filled at the NEXT bar''s open.
- One position at a time, all-in (equity × `leverage`), long, short or both.
- `feeBps` is charged per side on notional. Default 4.5 for Hyperliquid, 1 for FX.
- `stopPct` / `takePct` are checked inside each bar against its high and low. When both could hit in the same bar the stop is taken first; a bar that opens beyond the level fills at its open.
- A position still open at the last bar is closed at that bar''s close (`reason: "end"`). Equity at or below zero ends the run.
- No funding, no spread beyond the fee, no slippage. FX uses Dukascopy BID prices.
**Strategies and their parameters** (pass only the ones you want to change):
| `strategy` | Rule | Parameters (default) |
| --- | --- | --- |
| `supertrend` | Long while the Supertrend direction is up, short while it is down. | `atr` (10), `mult` (3) |
| `ema_cross` | Long while EMA(`fast`) is above EMA(`slow`), short while below. | `fast` (20), `slow` (50) |
| `rsi` | Long when RSI falls below `lower`, short when it rises above `upper`, flat again when RSI crosses back through 50. | `length` (14), `lower` (30), `upper` (70) |
| `donchian` | Long on a close above the highest high of the previous `length` bars, short on a close below their lowest low; held until the opposite breakout. | `length` (20) |
| `buy_hold` | Always long. | none |
**Symbols**: any Hyperliquid perp by its coin id, matched without case (`BTC`, `eth`, `kPEPE`); a leading `$` and a trailing `USDT` / `USD` / `-PERP` are dropped. FX and metals: `EURUSD`, `GBPUSD`, `USDJPY`, `USDCHF`, `AUDUSD`, `USDCAD`, `USDSEK`, `NZDUSD`, `EURJPY`, `GBPJPY`, `EURGBP`, `XAUUSD` (also `gold`), `XAGUSD` (also `silver`).
**How much history there is**: the window is the last `days` ending now, cut to what the store holds. Hyperliquid candles start from what its API served when the store was built (1m a few days, 15m about seven weeks, 1h about seven months, 4h and 1d years) and grow from there; FX has 1h bars since 2016 and 1m bars for about the last 400 days (`/fx/lake` shows how far each symbol is filled). Fewer than 50 bars in the window is a `400` whose message says what the store keeps. Read `from`, `to` and `bars` in the answer for the window that was really tested.'
parameters:
- name: symbol
in: query
required: false
schema:
type: string
default: BTC
description: A Hyperliquid perp coin id or an FX / metal symbol (list above).
- name: interval
in: query
required: false
schema:
type: string
enum:
- 1m
- 5m
- 15m
- 30m
- 1h
- 4h
- 1d
default: 1h
description: Bar size. 5m and 30m are resampled from finer stored bars.
- name: strategy
in: query
required: false
schema:
type: string
enum:
- supertrend
- ema_cross
- rsi
- donchian
- buy_hold
default: supertrend
description: ''
- name: days
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 3650
default: 365
description: Length of the window, ending now.
- name: side
in: query
required: false
schema:
type: string
enum:
- long
- short
- both
default: both
description: Which signals are traded.
- name: atr
in: query
required: false
schema:
type: number
default: 10
description: '`supertrend`: ATR length.'
- name: mult
in: query
required: false
schema:
type: number
default: 3
description: '`supertrend`: band width in ATRs.'
- name: fast
in: query
required: false
schema:
type: number
default: 20
description: '`ema_cross`: fast EMA length.'
- name: slow
in: query
required: false
schema:
type: number
default: 50
description: '`ema_cross`: slow EMA length.'
- name: length
in: query
required: false
schema:
type: number
description: '`rsi`: RSI length (default 14). `donchian`: channel length in bars (default 20).'
- name: lower
in: query
required: false
schema:
type: number
default: 30
description: '`rsi`: go long below this.'
- name: upper
in: query
required: false
schema:
type: number
default: 70
description: '`rsi`: go short above this.'
- name: feeBps
in: query
required: false
schema:
type: number
minimum: 0
description: Fee per side in basis points of notional. Default 4.5 (Hyperliquid) or 1 (FX).
- name: stopPct
in: query
required: false
schema:
type: number
exclusiveMinimum: 0
description: Stop-loss, percent from entry. Omitted = none.
- name: takePct
in: query
required: false
schema:
type: number
exclusiveMinimum: 0
description: Take-profit, percent from entry. Omitted = none.
- name: leverage
in: query
required: false
schema:
type: number
minimum: 0.1
maximum: 20
default: 1
description: Position size as a multiple of equity. Values outside 0.1–20 are clamped.
responses:
'200':
description: ''
content:
application/json:
schema:
type: object
properties:
strategy:
type: string
enum:
- supertrend
- ema_cross
- rsi
- donchian
- buy_hold
default: supertrend
params:
type: object
additionalProperties:
type: number
description: The parameters the run used, defaults filled in.
side:
type: string
description: long, short or both.
metrics:
type: object
properties:
totalReturnPct:
type: number
description: Return of the strategy over the window, after fees.
buyHoldPct:
type: number
description: Return of holding the asset over the same bars.
cagrPct:
type:
- number
- 'null'
description: Annualised return. Null when the tested window is shorter than three months, or equity ended at zero.
maxDrawdownPct:
type: number
description: Largest peak-to-trough fall of equity, as a positive number.
sharpe:
type:
- number
- 'null'
description: Mean over standard deviation of per-bar returns, annualised by the bar size. Null when equity never moved.
trades:
type: integer
description: Closed trades in the window.
winRatePct:
type:
- number
- 'null'
description: Share of trades that made money. Null with no trades.
profitFactor:
type:
- number
- 'null'
description: Sum of winning returns over sum of losing returns. Null when nothing lost.
avgWinPct:
type:
- number
- 'null'
description: Null with no winning trade.
avgLossPct:
type:
- number
- 'null'
description: Negative. Null with no losing trade.
exposurePct:
type: number
description: Share of bars spent in a position.
feesPaidPct:
type: number
description: Sum of the fees charged (each side of each trade, times leverage), in percent.
equity:
type: array
items:
type: object
properties:
t:
type: integer
description: Unix time in MILLISECONDS (every time field of this endpoint is; the rest of the API uses seconds).
equity:
type: number
description: Strategy equity, starting at 1.
hold:
type: number
description: Buy-and-hold equity, starting at 1.
description: The equity curve, thinned to about 300 points.
trades:
type: array
items:
type: object
properties:
side:
type: string
enum:
- long
- short
entryT:
type: integer
description: Unix time in MILLISECONDS (every time field of this endpoint is; the rest of the API uses seconds).
entry:
type: number
description: 'Fill price: the open of the bar after the signal.'
exitT:
type: integer
description: Unix time in MILLISECONDS (every time field of this endpoint is; the rest of the API uses seconds).
exit:
type: number
description: ''
retPct:
type: number
description: Return of the trade after both fees, leverage applied.
reason:
type: string
enum:
- signal
- stop
- take
- end
description: '`end` = still open at the last bar, closed there.'
bars:
type: integer
description: Bars held.
description: The most recent 200 trades, oldest first.
symbol:
type: string
description: 'The symbol as resolved: the Hyperliquid coin id or the FX symbol.'
source:
type: string
enum:
- hyperliquid
- dukascopy
interval:
type: string
enum:
- 1m
- 5m
- 15m
- 30m
- 1h
- 4h
- 1d
default: 1h
from:
type: integer
description: Unix time in MILLISECONDS (every time field of this endpoint is; the rest of the API uses seconds).
to:
type: integer
description: Unix time in MILLISECONDS (every time field of this endpoint is; the rest of the API uses seconds).
bars:
type: integer
description: Bars the run used. `from` and `to` are the first and last of them.
assumptions:
type: string
description: 'The model of this run in one sentence: fill rule, fee, leverage, price source.'
example:
strategy: supertrend
params:
atr: 10
mult: 3
side: both
metrics:
totalReturnPct: -29.7396335093976
buyHoldPct: -24.840856481481477
cagrPct: -29.77364346765159
maxDrawdownPct: 43.938798730789316
sharpe: -0.67928989324471
trades: 58
winRatePct: 37.93103448275862
profitFactor: 0.7686488016790433
avgWinPct: 4.190301346373836
avgLossPct: -3.3314820839884187
exposurePct: 99.58847736625515
feesPaidPct: 5.2199999999999935
equity:
- t: 1760227200000
equity: 1
hold: 1.000244140625
- t: 1760342400000
equity: 1
hold: 1.0321994357638888
trades:
- side: long
entryT: 1760356800000
entry: 114153
exitT: 1760601600000
exit: 110526
retPct: -3.267314656645025
reason: signal
bars: 17
- side: short
entryT: 1760601600000
entry: 110526
exitT: 1760947200000
exit: 111160
retPct: -0.6636206865353033
reason: signal
bars: 24
symbol: BTC
source: hyperliquid
interval: 4h
from: 1760212800000
to: 1791705600000
bars: 2188
assumptions: Signal on bar close, fill at next open; 4.5 bps fee per side; Hyperliquid perp candles, no funding; no slippage.
'400':
description: Invalid parameters — `error` names the check that failed (`INVALID_PARAMS`, `BAD_ADDRESS`, `BAD_LIMIT`, `BAD_HOURS`, …).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: The lane behind this endpoint is not ready or not enabled here (`NOT_READY`, `CHAIN_NOT_ENABLED`, `NOT_ENABLED`, `*_UNAVAILABLE`). Retry later; do not treat it as an empty answer.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/fx/lake:
get:
tags:
- Backtests
operationId: fxLake
summary: FX store coverage
description: 'How far the FX / metal candle store behind `/backtest` is filled, per symbol: the last month of 1h bars and the last day of 1m bars. `state` is `running` while a fill pass is in progress.'
responses:
'200':
description: ''
content:
application/json:
schema:
type: object
properties:
state:
type: string
enum:
- 'off'
- running
- idle
lastError:
type:
- string
- 'null'
description: ''
lastAt:
type: integer
description: Unix ms of the last fill pass, 0 when none ran in this process.
files:
type: integer
description: Files written by the last pass.
snapshotAt:
type: integer
description: Unix time, milliseconds — when the server built this answer.
from:
type: string
description: First month of the 1h store, `YYYY-MM`.
minuteDays:
type: integer
description: How many days back the 1m store reaches.
symbols:
type: object
additionalProperties:
type: object
properties:
hourThrough:
type:
- string
- 'null'
description: Last month with 1h bars, `YYYY-MM`.
minuteThrough:
type:
- string
- 'null'
description: Last day with 1m bars, `YYYY-MM-DD`.
description: Keyed by symbol.
example:
state: idle
lastError: null
lastAt: 0
files: 0
snapshotAt: 1791737879748
from: 2016-01
minuteDays: 400
symbols:
EURUSD:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
GBPUSD:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
USDJPY:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
USDCHF:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
AUDUSD:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
USDCAD:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
USDSEK:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
NZDUSD:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
EURJPY:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
GBPJPY:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
EURGBP:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
XAUUSD:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
XAGUSD:
hourThrough: 2026-09
minuteThrough: '2026-10-10'
'503':
description: The lane behind this endpoint is not ready or not enabled here (`NOT_READY`, `CHAIN_NOT_ENABLED`, `NOT_ENABLED`, `*_UNAVAILABLE`). Retry later; do not treat it as an empty answer.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Error:
type: object
properties:
error:
type: string
description: Stable machine code, e.g. `INVALID_PARAMS`, `NOT_READY`, `RATE_LIMIT_EXCEEDED`.
message:
type: string
description: For humans.
statusCode:
type: integer
description: HTTP status, when included.
description: Every non-2xx body.
required:
- error
example:
error: INVALID_PARAMS
message: amount must be an integer string in the token's smallest unit
securitySchemes:
ApiKey:
type: apiKey
in: header
name: X-API-Key
description: Optional on every read. A key from getchord.xyz/developer raises the rate limit to your plan and meters usage per key. Required for `?only=` on quotes and for the Ask agent API.
Session:
type: http
scheme: bearer
description: A Google sign-in session minted by the app (`nts_…`). Only the browser surfaces use it; agents use `ApiKey`.