Skip to content

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.

ParameterRequiredDescription
algoIdYesAlgorithm ID
versionYesAlgorithm version to test
symbolYesTrading pair (e.g., BTC/USDC)
exchangeIdYesExchange (default: hyperliquid)
startTimeYesBacktest start date
endTimeYesBacktest end date
capitalScalerNoMultiplier for startingCapitalUSD (default: 1.0)

Backtests run asynchronously. You get a runId to poll for results with get_backtest_results.

The capitalScaler multiplies the algorithm’s startingCapitalUSD:

effective capital = startingCapitalUSD × capitalScaler

This 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.

The backtester processes your algorithm through these stages:

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).

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.

Aggregates candles into multiple timeframes (1m, 5m, 15m, 1h, 4h, 1d) so indicators at different resolutions can be computed efficiently.

Computes all indicator values across the full date range using the warmup period. Each indicator produces a time series of numeric values.

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).

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

Backtests model realistic execution costs:

CostDefaultDescription
FeesExchange-specific (bps)Deducted from trade value on entry and exit
SlippageExchange-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.

Trading performance metrics calculated from the trade history:

MetricDescription
totalTradesTotal number of completed trades
winningTrades / losingTradesCount of profitable vs unprofitable trades
winRatePercentage of winning trades
totalPnlUSDNet profit/loss in USD
grossProfitUSD / grossLossUSDSum of all winning / losing trade P&L
avgPnlUSDAverage P&L per trade
avgWinUSD / avgLossUSDAverage winning / losing trade
largestWinUSD / largestLossUSDBest and worst single trade
profitFactorGross profit / gross loss (> 1.0 = profitable)
MetricDescription
sharpeRatioAnnualized return / volatility of returns. Measures risk-adjusted performance. > 1.0 is good, > 2.0 is strong.
sortinoRatioLike Sharpe but only penalizes downside volatility. Higher is better — ignores upside variance.
calmarRatioAnnualized return / max drawdown. Measures return relative to worst-case drawdown.
maxDrawdownPctLargest peak-to-trough equity decline as a percentage
maxDrawdownUSDLargest peak-to-trough equity decline in USD
MetricDescription
longTrades / shortTradesCount by direction
longWinRate / shortWinRateWin rate by direction
longPnlUSD / shortPnlUSDP&L by direction
avgTradeDurationBarsAverage trade length in bars
avgTradeDurationSecondsAverage trade length in seconds
avgWinDurationBars / avgLossDurationBarsDuration of winners vs losers
totalFeesUSD / totalSlippageUSDTotal execution costs

Diagnostic metrics about how the algorithm’s indicators and conditions behaved:

Each indicator gets a usefulness score (0–100) measuring how much it contributes:

FieldDescription
flipCountHow many times the indicator signal changed
flipRateFlips per bar
avgDurationTrueBars / avgDurationFalseBarsAverage length of true/false periods
pctTimeTruePercentage of time the signal was true
triggeringFlipCountFlips that actually triggered a condition change
blockingCountTimes this indicator prevented a condition from triggering
usefulnessScoreComposite 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.

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.

FieldDescription
pctTimeFlatPercentage of time with no position
pctTimeLong / pctTimeShortPercentage of time in long/short positions
avgTimeFlatBarsAverage bars between trades
avgTimeLongBars / avgTimeShortBarsAverage position duration by direction

How positions were closed:

ReasonDescription
signalExit condition indicators triggered
stopLossFixed stop loss hit
stopLossTrailingTrailing stop loss hit
takeProfitTake profit level hit
endOfBacktestPosition was open when backtest ended
FieldDescription
indicatorFlipsTotal indicator signal changes
conditionChangesTimes entry/exit conditions changed state
stateTransitionsCASH → POSITION → TIMEOUT transitions
specialIndicatorEventsSL/TP set, update, and hit events

Daily snapshots of portfolio value:

FieldDescription
timestampUTC timestamp (daily at 23:59)
equityTotal portfolio value (cash + unrealized P&L)
realizedPnLCumulative realized profit/loss
unrealizedPnLCurrent open position P&L
drawdownPctCurrent drawdown from peak equity

Use plot_backtest to generate an equity curve chart (PNG) with drawdown overlay. Supports date-range zoom and JSON export.