Skip to main content
Before attaching a strategy to a live agent, you can validate it against historical data using the backtesting API. You submit a StrategySpec — a structured description of your entry signals, exit signals, regime filter, and position parameters — and Halyrd queues an asynchronous backtest run. Once finished, the result includes a full set of performance metrics and a passed_gate boolean that tells you whether the strategy meets the minimum quality bar required before it can be promoted to the agent. You can also query the GET /indicators endpoint to discover which indicators are available for use in your StrategySpec.

POST /backtests

Queues a new backtest run. The request body must contain a complete StrategySpec. The endpoint returns 202 Accepted immediately with a backtest_id you can use to poll for results.
POST http://localhost:8000/backtests
Content-Type: application/json
Request body
{
  "strategy_spec": {
    "market_mode": "spot",
    "name": "mean-reversion-v1",
    "signals": {
      "entry": {
        "all_of": [
          { "indicator": "price_vs_sma", "op": "<=", "value": -0.05, "window": 48 },
          { "indicator": "rsi", "op": "<=", "value": 25, "window": 14 },
          { "indicator": "fear_greed", "op": "<=", "value": 40 }
        ]
      },
      "exit": {
        "any_of": [
          { "indicator": "price_vs_sma", "op": ">=", "value": 0.0 },
          { "indicator": "rsi", "op": ">=", "value": 65 }
        ]
      }
    },
    "regime_filter": {
      "stand_aside_when": [
        { "indicator": "funding_rate", "op": ">=", "value": 0.03 }
      ]
    },
    "params": {
      "position_size_pct": 90,
      "max_concurrent": 1
    }
  }
}

StrategySpec fields

FieldDescription
market_modeTrading mode for this strategy — currently spot.
nameHuman-readable strategy name for identification in results.
signals.entryEntry condition using all_of (all conditions must be true) or any_of (at least one must be true).
signals.exitExit condition using the same all_of / any_of structure.
regime_filter.stand_aside_whenConditions that, when met, suppress new entries entirely (e.g. high funding rate).
params.position_size_pctPercentage of available equity to deploy per trade.
params.max_concurrentMaximum number of positions the strategy may hold simultaneously.
Each signal condition contains an indicator name, a comparison operator (op), a threshold value, and an optional window (lookback periods for time-series indicators). Response — 202 Accepted
{
  "backtest_id": "bt_abc123"
}

GET /backtests/

Returns the current status and, once finished, the full metrics for a backtest run.
GET http://localhost:8000/backtests/bt_abc123
Response
{
  "id": "bt_abc123",
  "status": "finished",
  "metrics": {
    "net_return_pct": 4.2,
    "max_drawdown_pct": 8.1,
    "expectancy": 0.0042,
    "trades_per_day": 1.3,
    "win_rate": 0.62,
    "ratio_avg_win_loss": 1.4
  },
  "passed_gate": true
}

Status values

ValueDescription
queuedThe backtest is waiting for a worker to pick it up.
runningThe backtest is actively processing historical data.
finishedThe backtest completed successfully; metrics and passed_gate are populated.
failedThe backtest encountered an error; inspect the error field for details.

Metrics fields

FieldDescription
net_return_pctTotal percentage return over the backtest period, net of fees.
max_drawdown_pctLargest peak-to-trough portfolio decline observed during the backtest, as a percentage.
expectancyAverage expected profit per trade as a fraction of trade size. Positive values indicate an edge.
trades_per_dayAverage number of trades closed per calendar day over the backtest period.
win_rateFraction of trades that closed with a positive PnL (e.g. 0.62 = 62% win rate).
ratio_avg_win_lossRatio of average winning trade size to average losing trade size. Values above 1.0 indicate wins are larger than losses on average.

passed_gate

passed_gate is true when all three of the following conditions hold simultaneously:
  • expectancy > 0 — the strategy has a measurable positive edge
  • Closed trades during the backtest period ≥ the configured trade_floor — sufficient sample size
  • max_drawdown_pct ≤ the configured drawdown cap — risk is within acceptable bounds
A strategy must pass the gate before it can be attached to an agent and promoted to live.

WebSocket progress events

While a backtest is running, you can subscribe to real-time progress over the WebSocket connection. Two event types are emitted:
{ "event": "backtest_progress", "backtest_id": "bt_abc123", "pct": 45 }
{ "event": "backtest_done", "backtest_id": "bt_abc123", "status": "finished" }
EventDescription
backtest_progressEmitted periodically while the backtest is running. pct is the estimated completion percentage (0–100).
backtest_doneEmitted once when the backtest reaches a terminal state (finished or failed). Poll GET /backtests/{id} after receiving this to retrieve the full result.
Backtests typically complete in under a minute. There is no cancel endpoint — once submitted, a run processes to completion. Backtest sessions persist on the server, so you can retrieve results via GET /backtests/{id} at any time after the run finishes, even after reconnecting.

GET /indicators

Returns the list of indicators available for use in StrategySpec signal and regime filter conditions.
GET http://localhost:8000/indicators
Response
[
  { "name": "rsi", "description": "Relative Strength Index", "params": ["window"] },
  { "name": "price_vs_sma", "description": "Price deviation from simple moving average", "params": ["window"] },
  { "name": "fear_greed", "description": "Crypto Fear & Greed Index (0–100)", "params": [] },
  { "name": "funding_rate", "description": "Perpetual futures funding rate", "params": [] }
]
Use the name values from this response directly as the indicator field in your StrategySpec signal conditions.