Backtesting
The backtesting engine simulates your algorithm against historical market data using the same state machine and indicator logic as live trading. This page covers how to run backtests, what happens under the hood, and how to read the results.
Running a Backtest
Section titled “Running a Backtest”Inputs
Section titled “Inputs”| Parameter | Required | Description |
|---|---|---|
algoId | Yes | Algorithm ID |
version | Yes | Algorithm version to test |
symbol | Yes | Trading pair (e.g., BTC/USDC) |
exchangeId | Yes | Exchange (default: hyperliquid) |
startTime | Yes | Backtest start date |
endTime | Yes | Backtest end date |
capitalScaler | No | Multiplier for startingCapitalUSD (default: 1.0) |
Backtests run asynchronously. You get a runId to poll for results with get_backtest_results.
Capital Scaling
Section titled “Capital Scaling”The capitalScaler multiplies the algorithm’s startingCapitalUSD:
effective capital = startingCapitalUSD × capitalScalerThis lets you test the same algorithm at different capital levels without modifying the config. A scaler of 2.0 on a $1,000 algorithm runs with $2,000.
Simulation Pipeline
Section titled “Simulation Pipeline”The backtester processes your algorithm through these stages:
1. Data Loading
Section titled “1. Data Loading”Validates inputs, loads historical candles from the database, filters to the date range, and computes the warmup offset (extra bars needed before the trading start for indicator calculation).
2. Sub-Bar Loading
Section titled “2. Sub-Bar Loading”Loads intra-bar price data for accurate stop loss and take profit detection. If high-resolution data isn’t available, falls back to OHLC interpolation within each candle.
3. MipMap Building
Section titled “3. MipMap Building”Aggregates candles into multiple timeframes (1m, 5m, 15m, 1h, 4h, 1d) so indicators at different resolutions can be computed efficiently.
4. Indicator Calculation
Section titled “4. Indicator Calculation”Computes all indicator values across the full date range using the warmup period. Each indicator produces a time series of numeric values.
5. Resampling
Section titled “5. Resampling”Converts continuous indicator values into boolean signals based on each indicator’s signal type and threshold. Detects signal crossings — the exact bars where an indicator flips from false to true (or vice versa).
6. Event-Driven Simulation
Section titled “6. Event-Driven Simulation”Runs the state machine (CASH → POSITION → TIMEOUT → CASH) over the resampled signals. On each bar:
- Checks if entry/exit conditions are met (AND/OR logic across indicator groups)
- Scans for stop loss and take profit hits using sub-bar data
- Executes trades with fee and slippage deductions
- Tracks equity, drawdown, and trade records
Fees & Slippage
Section titled “Fees & Slippage”Backtests model realistic execution costs:
| Cost | Default | Description |
|---|---|---|
| Fees | Exchange-specific (bps) | Deducted from trade value on entry and exit |
| Slippage | Exchange-specific (bps) | Price impact — longs slip up on entry, down on exit; shorts the reverse |
Both are applied to every trade. Position size is calculated after deducting entry fees and slippage from available capital.
Results
Section titled “Results”Swap Metrics
Section titled “Swap Metrics”Trading performance metrics calculated from the trade history:
Returns
Section titled “Returns”| Metric | Description |
|---|---|
totalTrades | Total number of completed trades |
winningTrades / losingTrades | Count of profitable vs unprofitable trades |
winRate | Percentage of winning trades |
totalPnlUSD | Net profit/loss in USD |
grossProfitUSD / grossLossUSD | Sum of all winning / losing trade P&L |
avgPnlUSD | Average P&L per trade |
avgWinUSD / avgLossUSD | Average winning / losing trade |
largestWinUSD / largestLossUSD | Best and worst single trade |
profitFactor | Gross profit / gross loss (> 1.0 = profitable) |
Risk-Adjusted
Section titled “Risk-Adjusted”| Metric | Description |
|---|---|
sharpeRatio | Annualized return / volatility of returns. Measures risk-adjusted performance. > 1.0 is good, > 2.0 is strong. |
sortinoRatio | Like Sharpe but only penalizes downside volatility. Higher is better — ignores upside variance. |
calmarRatio | Annualized return / max drawdown. Measures return relative to worst-case drawdown. |
maxDrawdownPct | Largest peak-to-trough equity decline as a percentage |
maxDrawdownUSD | Largest peak-to-trough equity decline in USD |
Trade Breakdown
Section titled “Trade Breakdown”| Metric | Description |
|---|---|
longTrades / shortTrades | Count by direction |
longWinRate / shortWinRate | Win rate by direction |
longPnlUSD / shortPnlUSD | P&L by direction |
avgTradeDurationBars | Average trade length in bars |
avgTradeDurationSeconds | Average trade length in seconds |
avgWinDurationBars / avgLossDurationBars | Duration of winners vs losers |
totalFeesUSD / totalSlippageUSD | Total execution costs |
Algo Metrics
Section titled “Algo Metrics”Diagnostic metrics about how the algorithm’s indicators and conditions behaved:
Indicator Analysis
Section titled “Indicator Analysis”Each indicator gets a usefulness score (0–100) measuring how much it contributes:
| Field | Description |
|---|---|
flipCount | How many times the indicator signal changed |
flipRate | Flips per bar |
avgDurationTrueBars / avgDurationFalseBars | Average length of true/false periods |
pctTimeTrue | Percentage of time the signal was true |
triggeringFlipCount | Flips that actually triggered a condition change |
blockingCount | Times this indicator prevented a condition from triggering |
usefulnessScore | Composite score (0–100) based on entropy, efficiency, criticality |
Entry scoring weights: entropy (20%) + efficiency (35%) + criticality (30%) + bottleneck (15%)
Exit scoring weights: entropy (25%) + selectivity (40%) + signal density (35%)
A low usefulness score suggests the indicator is redundant or too noisy — consider removing or replacing it.
Near-Miss Analysis
Section titled “Near-Miss Analysis”Shows how close conditions came to triggering without actually firing. Useful for fine-tuning thresholds — if an indicator frequently reaches 90% of its trigger point, a small threshold adjustment might capture more opportunities.
State Distribution
Section titled “State Distribution”| Field | Description |
|---|---|
pctTimeFlat | Percentage of time with no position |
pctTimeLong / pctTimeShort | Percentage of time in long/short positions |
avgTimeFlatBars | Average bars between trades |
avgTimeLongBars / avgTimeShortBars | Average position duration by direction |
Exit Reason Breakdown
Section titled “Exit Reason Breakdown”How positions were closed:
| Reason | Description |
|---|---|
signal | Exit condition indicators triggered |
stopLoss | Fixed stop loss hit |
stopLossTrailing | Trailing stop loss hit |
takeProfit | Take profit level hit |
endOfBacktest | Position was open when backtest ended |
Event Counts
Section titled “Event Counts”| Field | Description |
|---|---|
indicatorFlips | Total indicator signal changes |
conditionChanges | Times entry/exit conditions changed state |
stateTransitions | CASH → POSITION → TIMEOUT transitions |
specialIndicatorEvents | SL/TP set, update, and hit events |
Equity Curve
Section titled “Equity Curve”Daily snapshots of portfolio value:
| Field | Description |
|---|---|
timestamp | UTC timestamp (daily at 23:59) |
equity | Total portfolio value (cash + unrealized P&L) |
realizedPnL | Cumulative realized profit/loss |
unrealizedPnL | Current open position P&L |
drawdownPct | Current drawdown from peak equity |
Use plot_backtest to generate an equity curve chart (PNG) with drawdown overlay. Supports date-range zoom and JSON export.