# Backtest a strategy

`GET https://api.getchord.xyz/v1/backtest`

Source: https://getchord.xyz/docs/api-reference/operations/backtest/

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 | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `symbol` | query | no | string (default `BTC`) | A Hyperliquid perp coin id or an FX / metal symbol (list above). |
| `interval` | query | no | `1m` · `5m` · `15m` · `30m` · `1h` · `4h` · `1d` (default `1h`) | Bar size. 5m and 30m are resampled from finer stored bars. |
| `strategy` | query | no | `supertrend` · `ema_cross` · `rsi` · `donchian` · `buy_hold` (default `supertrend`) |  |
| `days` | query | no | integer (default `365`) | Length of the window, ending now. |
| `side` | query | no | `long` · `short` · `both` (default `both`) | Which signals are traded. |
| `atr` | query | no | number (default `10`) | `supertrend`: ATR length. |
| `mult` | query | no | number (default `3`) | `supertrend`: band width in ATRs. |
| `fast` | query | no | number (default `20`) | `ema_cross`: fast EMA length. |
| `slow` | query | no | number (default `50`) | `ema_cross`: slow EMA length. |
| `length` | query | no | number | `rsi`: RSI length (default 14). `donchian`: channel length in bars (default 20). |
| `lower` | query | no | number (default `30`) | `rsi`: go long below this. |
| `upper` | query | no | number (default `70`) | `rsi`: go short above this. |
| `feeBps` | query | no | number | Fee per side in basis points of notional. Default 4.5 (Hyperliquid) or 1 (FX). |
| `stopPct` | query | no | number | Stop-loss, percent from entry. Omitted = none. |
| `takePct` | query | no | number | Take-profit, percent from entry. Omitted = none. |
| `leverage` | query | no | number (default `1`) | Position size as a multiple of equity. Values outside 0.1–20 are clamped. |

## Response 200

Example (captured from the live API):

```json
{
  "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**: Invalid parameters — `error` names the check that failed (`INVALID_PARAMS`, `BAD_ADDRESS`, `BAD_LIMIT`, `BAD_HOURS`, …).
- **503**: 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.
