Skip to content

Backtest

Backtesting engine, results metrics, optimization, and cross-validation

Before risking real capital, backtest your algorithm against historical data. The backtesting engine uses the same logic as live trading — what you test is what runs.

For a hands-on first backtest, see the Backtest Walkthrough →

On this page: Running a Backtest · Fees & Slippage · Results · Optimization · Rolling Cross-Validation · Under the Hood


Select your algorithm, choose a date range, and hit Run. The backtest runs asynchronously — you get a runId to poll for results.

ParameterRequiredDescription
algoIdYesAlgorithm ID
versionYesAlgorithm version to test
symbolYesTrading pair (e.g., BTC/USDC)
exchangeIdYesExchange (default: hyperliquid)
startTimeYesBacktest start date
endTimeYesBacktest end date
initialCapitalUSDNoStarting capital in USD (default: 1000). Drives position sizing.
leverageNoLeverage multiplier applied to Starting Capital (default: 1)
effective notional = initialCapitalUSD × leverage

Test the same algorithm at different capital levels by changing initialCapitalUSD (or leverage) without touching the algorithm config. The backtester uses these values to size every position the algorithm opens.


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.


These five metrics give you the fastest read on whether your strategy is working:

MetricWhat It Tells YouGood Range
winRatePercentage of winning trades> 40% (depends on R:R)
profitFactorGross profit / gross loss> 1.5 (> 2.0 is strong)
sharpeRatioRisk-adjusted return (annualized return / volatility)> 1.0 (> 2.0 is strong)
maxDrawdownPctLargest peak-to-trough equity decline< 20% (lower is better)
totalPnlUSDNet profit/loss in USDPositive, obviously
MetricDescription
totalTradesTotal completed trades
winningTrades / losingTradesProfitable vs unprofitable trades
totalPnlUSDNet profit/loss in USD
grossProfitUSD / grossLossUSDSum of winning / losing trade P&L
avgPnlUSDAverage P&L per trade
avgWinUSD / avgLossUSDAverage winning / losing trade
largestWinUSD / largestLossUSDBest and worst single trade
MetricDescription
sharpeRatioAnnualized return / volatility. > 1.0 is good, > 2.0 is strong
sortinoRatioLike Sharpe but only penalizes downside volatility — better for asymmetric strategies
calmarRatioAnnualized return / max drawdown — how well you’re compensated for worst-case pain
maxDrawdownPctLargest peak-to-trough equity decline (%)
maxDrawdownUSDLargest peak-to-trough equity decline (USD)
MetricDescription
longTrades / shortTradesCount by direction
longWinRate / shortWinRateWin rate by direction
longPnlUSD / shortPnlUSDP&L by direction
avgTradeDurationBars / avgTradeDurationSecondsAverage trade length
avgWinDurationBars / avgLossDurationBarsDuration of winners vs losers
totalFeesUSD / totalSlippageUSDTotal execution costs

Each indicator gets a usefulness score (0-100) that tells you whether it’s actually contributing to your strategy’s decisions — or just adding noise.

FieldWhat It Tells You
flipCountHow many times the 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)

Entry scoring weights: entropy (20%) + efficiency (35%) + criticality (30%) + bottleneck (15%)

Exit scoring weights: entropy (25%) + selectivity (40%) + signal density (35%)

Shows how close conditions came to triggering without actually firing. This is valuable when your strategy isn’t trading enough — near-misses tell you which thresholds are too tight and by how much.

FieldDescription
pctTimeFlatTime with no position
pctTimeLong / pctTimeShortTime in long/short positions
avgTimeFlatBarsAverage bars between trades
ReasonDescription
signalExit condition triggered
stopLossFixed SL hit
stopLossTrailingTrailing SL hit
takeProfitTP level hit
endOfBacktestPosition open at backtest end

Event-driven snapshots of portfolio value — one data point per trade entry, exit, SL, or TP:

FieldDescription
timestampUTC timestamp
equityTotal portfolio value
drawdownPctCurrent drawdown from peak

Use plot_backtest to generate an equity curve chart (PNG) with drawdown overlay.


Switch to Optimize mode at the top of the Test Workspace to access parameter optimization. The optimizer uses Optuna’s TPE (Tree-structured Parzen Estimator) to intelligently search the parameter space — each trial informs the next, converging faster than random search.

Optimization is most useful when you have a working strategy but aren’t sure about exact parameter values — RSI period, stop loss percentage, threshold levels, etc.

For a hands-on first optimization, see the Backtest Walkthrough → Optimize

Choose what the optimizer should target:

GoalDirectionDescription
Balanced (Sharpe)MaximizeRisk-adjusted returns — good general-purpose default
Max SharpeMaximizeMaximize Sharpe ratio explicitly
Min DrawdownMinimizeMinimize maximum drawdown — for conservative strategies
Max PnLMaximizeMaximize raw profit — for aggressive strategies

Define which parameters to vary, their type, range, and step size. Each parameter maps to a specific path in your algorithm config (e.g., longEntry.required.0.params.threshold).

FieldDescription
NameLabel for the parameter (e.g., rsi_threshold)
PathJSON path to the algorithm setting
Typeint or float
Low / HighBounds for the search range
StepIncrement between values

Default parameters are pre-loaded:

ParameterTypeRangeStep
rsi_thresholdfloat20–401
stop_pctfloat0.5–3.00.1
tp_pctfloat0.5–5.00.1

Add, remove, or adjust parameters and use the range sliders to set bounds visually.

Supported types: int (integer range), float (float range), categorical (discrete choices).

The optimizer uses rolling cross-validation to evaluate each parameter combination — splitting the backtest period into overlapping train/test folds.

SettingDescription
Train DaysLength of each training window
Test DaysLength of each out-of-sample test window
Step DaysHow far the window slides forward between folds

See Rolling Cross-Validation for details on how folds work.

Number of parameter combinations to evaluate. Default: 10, minimum: 5.

Click Run Optimize to start. The progress view shows:

  • Progress bar — trial completion percentage
  • Best score — highest score achieved so far
  • Top 5 leaderboard — best parameter combinations ranked by score, with test PnL for each trial

Hit Stop Search at any time — results from completed trials are preserved.

When optimization completes:

  • Best parameters — the values that scored highest
  • Trial leaderboard — top 5 trials with score and test PnL
  • Folds tab — performance breakdown per cross-validation fold (train vs test metrics per window)

Default: maximize Sharpe ratio. When using the agent or API, you can define custom Python scoring functions that run in a sandbox:

score = results['swapMetrics']['sharpeRatio'] * 0.5 + \
results['swapMetrics']['profitFactor'] * 0.3 - \
results['swapMetrics']['maxDrawdownPct'] * 0.2

Pass warm_start_params and warm_start_scores to seed the optimizer with known good regions. Useful when re-optimizing after a market regime shift — you don’t start from scratch.


Walk-forward analysis that tests whether your algorithm generalizes to unseen data. This is the best defense against overfitting.

|── Train ──|── Test ──|
|── Train ──|── Test ──|
|── Train ──|── Test ──|
|── Train ──|── Test ──|

The date range is divided into overlapping folds. Each fold trains on one window and evaluates on the next unseen period. Windows step forward by step_days.

ParameterDescription
algo_id / versionAlgorithm
symbol / exchange_idTrading pair
start_time / end_timeFull date range
train_window_daysLength of each training window
test_window_daysLength of each test window
step_daysStep between folds
initial_capital_usdStarting capital per fold (default 1000)
leverageLeverage multiplier (default 1)

12-month backtest with 90-day train, 30-day test, 30-day step:

  • Fold 1: Train Jan-Mar, Test Apr
  • Fold 2: Train Feb-Apr, Test May
  • Fold 3: Train Mar-May, Test Jun
  • ~9 out-of-sample test periods
What You SeeWhat It MeansWhat to Do
Consistent Sharpe/win rate across foldsStrategy generalizes wellSafe to deploy
Much better train metrics than testLikely overfitReduce parameter space, simplify, or add regularization
Later folds performing worseLosing edge (regime change)Re-optimize on recent data, or reconsider the strategy entirely

Workflow: Backtest → Optimize → Validate

Section titled “Workflow: Backtest → Optimize → Validate”
  1. Backtest your base algorithm in Backtest mode to establish a baseline
  2. Switch to Optimize mode and configure your goal, search ranges, and validation windows
  3. Run optimization — the optimizer handles cross-validation automatically per trial
  4. Check the Folds tab — compare train vs test metrics across folds
  5. If consistent — update your algorithm with the best parameters and deploy
  6. If overfit — narrow the search ranges, reduce parameters, or simplify the strategy

For step-by-step control via the agent or API, use the bo_suggest tool:

  1. Run a backtest with initial parameters
  2. Score the results
  3. Pass observed params + scores to bo_suggest for next suggestion
  4. Run another backtest with suggested params
  5. Repeat until satisfied

You don’t need to understand the pipeline to use backtesting, but knowing what happens helps you interpret edge cases and trust the results.

Validates inputs, loads historical candles, filters to date range, and computes warmup offset (extra bars needed for indicator calculation).

Loads intra-bar price data for accurate stop loss and take profit detection. Falls back to OHLC interpolation if high-resolution data isn’t available.

Aggregates candles into multiple timeframes (1m, 5m, 15m, 1h, 4h, 1d) for efficient multi-resolution indicator computation.

Computes all indicator values across the full date range using the warmup period.

Converts continuous indicator values into boolean signals based on signal type and threshold. Detects signal crossings — the exact bars where an indicator flips.

Runs the state machine (CASH → POSITION → TIMEOUT → CASH) over resampled signals. On each bar:

  • Checks entry/exit conditions (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

Want to…Go to
Take your strategy liveDeploy Features →
Tweak your algorithm settingsBuild Features →
Walk through your first backtestBacktest Walkthrough →