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
Running a Backtest
Section titled “Running a Backtest”Select your algorithm, choose a date range, and hit Run. The backtest runs asynchronously — you get a runId to poll for results.
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 |
initialCapitalUSD | No | Starting capital in USD (default: 1000). Drives position sizing. |
leverage | No | Leverage multiplier applied to Starting Capital (default: 1) |
Capital Sizing
Section titled “Capital Sizing”effective notional = initialCapitalUSD × leverageTest 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.
Fees & Slippage
Section titled “Fees & Slippage”| 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.
Results
Section titled “Results”Key Metrics (Check These First)
Section titled “Key Metrics (Check These First)”These five metrics give you the fastest read on whether your strategy is working:
| Metric | What It Tells You | Good Range |
|---|---|---|
winRate | Percentage of winning trades | > 40% (depends on R:R) |
profitFactor | Gross profit / gross loss | > 1.5 (> 2.0 is strong) |
sharpeRatio | Risk-adjusted return (annualized return / volatility) | > 1.0 (> 2.0 is strong) |
maxDrawdownPct | Largest peak-to-trough equity decline | < 20% (lower is better) |
totalPnlUSD | Net profit/loss in USD | Positive, obviously |
Full Metrics Reference
Section titled “Full Metrics Reference”Returns
Section titled “Returns”| Metric | Description |
|---|---|
totalTrades | Total completed trades |
winningTrades / losingTrades | Profitable vs unprofitable trades |
totalPnlUSD | Net profit/loss in USD |
grossProfitUSD / grossLossUSD | Sum of winning / losing trade P&L |
avgPnlUSD | Average P&L per trade |
avgWinUSD / avgLossUSD | Average winning / losing trade |
largestWinUSD / largestLossUSD | Best and worst single trade |
Risk-Adjusted
Section titled “Risk-Adjusted”| Metric | Description |
|---|---|
sharpeRatio | Annualized return / volatility. > 1.0 is good, > 2.0 is strong |
sortinoRatio | Like Sharpe but only penalizes downside volatility — better for asymmetric strategies |
calmarRatio | Annualized return / max drawdown — how well you’re compensated for worst-case pain |
maxDrawdownPct | Largest peak-to-trough equity decline (%) |
maxDrawdownUSD | Largest peak-to-trough equity decline (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 / avgTradeDurationSeconds | Average trade length |
avgWinDurationBars / avgLossDurationBars | Duration of winners vs losers |
totalFeesUSD / totalSlippageUSD | Total execution costs |
Indicator Analysis
Section titled “Indicator Analysis”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.
| Field | What It Tells You |
|---|---|
flipCount | How many times the 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) |
Entry scoring weights: entropy (20%) + efficiency (35%) + criticality (30%) + bottleneck (15%)
Exit scoring weights: entropy (25%) + selectivity (40%) + signal density (35%)
Near-Miss Analysis
Section titled “Near-Miss Analysis”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.
State Distribution
Section titled “State Distribution”| Field | Description |
|---|---|
pctTimeFlat | Time with no position |
pctTimeLong / pctTimeShort | Time in long/short positions |
avgTimeFlatBars | Average bars between trades |
Exit Reason Breakdown
Section titled “Exit Reason Breakdown”| Reason | Description |
|---|---|
signal | Exit condition triggered |
stopLoss | Fixed SL hit |
stopLossTrailing | Trailing SL hit |
takeProfit | TP level hit |
endOfBacktest | Position open at backtest end |
Equity Curve
Section titled “Equity Curve”Event-driven snapshots of portfolio value — one data point per trade entry, exit, SL, or TP:
| Field | Description |
|---|---|
timestamp | UTC timestamp |
equity | Total portfolio value |
drawdownPct | Current drawdown from peak |
Use plot_backtest to generate an equity curve chart (PNG) with drawdown overlay.
Optimization
Section titled “Optimization”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
Optimization Goal
Section titled “Optimization Goal”Choose what the optimizer should target:
| Goal | Direction | Description |
|---|---|---|
| Balanced (Sharpe) | Maximize | Risk-adjusted returns — good general-purpose default |
| Max Sharpe | Maximize | Maximize Sharpe ratio explicitly |
| Min Drawdown | Minimize | Minimize maximum drawdown — for conservative strategies |
| Max PnL | Maximize | Maximize raw profit — for aggressive strategies |
Search Ranges
Section titled “Search Ranges”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).
| Field | Description |
|---|---|
| Name | Label for the parameter (e.g., rsi_threshold) |
| Path | JSON path to the algorithm setting |
| Type | int or float |
| Low / High | Bounds for the search range |
| Step | Increment between values |
Default parameters are pre-loaded:
| Parameter | Type | Range | Step |
|---|---|---|---|
rsi_threshold | float | 20–40 | 1 |
stop_pct | float | 0.5–3.0 | 0.1 |
tp_pct | float | 0.5–5.0 | 0.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).
Validation Settings
Section titled “Validation Settings”The optimizer uses rolling cross-validation to evaluate each parameter combination — splitting the backtest period into overlapping train/test folds.
| Setting | Description |
|---|---|
| Train Days | Length of each training window |
| Test Days | Length of each out-of-sample test window |
| Step Days | How far the window slides forward between folds |
See Rolling Cross-Validation for details on how folds work.
Trials
Section titled “Trials”Number of parameter combinations to evaluate. Default: 10, minimum: 5.
Running & Monitoring
Section titled “Running & Monitoring”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.
Results
Section titled “Results”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)
Custom Scoring (Agent/API)
Section titled “Custom Scoring (Agent/API)”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.2Warm Starting (Agent/API)
Section titled “Warm Starting (Agent/API)”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.
Rolling Cross-Validation
Section titled “Rolling Cross-Validation”Walk-forward analysis that tests whether your algorithm generalizes to unseen data. This is the best defense against overfitting.
How It Works
Section titled “How It Works”|── 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.
Parameters
Section titled “Parameters”| Parameter | Description |
|---|---|
algo_id / version | Algorithm |
symbol / exchange_id | Trading pair |
start_time / end_time | Full date range |
train_window_days | Length of each training window |
test_window_days | Length of each test window |
step_days | Step between folds |
initial_capital_usd | Starting capital per fold (default 1000) |
leverage | Leverage multiplier (default 1) |
Example
Section titled “Example”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
Interpreting Results
Section titled “Interpreting Results”| What You See | What It Means | What to Do |
|---|---|---|
| Consistent Sharpe/win rate across folds | Strategy generalizes well | Safe to deploy |
| Much better train metrics than test | Likely overfit | Reduce parameter space, simplify, or add regularization |
| Later folds performing worse | Losing edge (regime change) | Re-optimize on recent data, or reconsider the strategy entirely |
Workflow: Backtest → Optimize → Validate
Section titled “Workflow: Backtest → Optimize → Validate”- Backtest your base algorithm in Backtest mode to establish a baseline
- Switch to Optimize mode and configure your goal, search ranges, and validation windows
- Run optimization — the optimizer handles cross-validation automatically per trial
- Check the Folds tab — compare train vs test metrics across folds
- If consistent — update your algorithm with the best parameters and deploy
- If overfit — narrow the search ranges, reduce parameters, or simplify the strategy
Agent-Driven Optimization
Section titled “Agent-Driven Optimization”For step-by-step control via the agent or API, use the bo_suggest tool:
- Run a backtest with initial parameters
- Score the results
- Pass observed params + scores to
bo_suggestfor next suggestion - Run another backtest with suggested params
- Repeat until satisfied
Under the Hood: Simulation Pipeline
Section titled “Under the Hood: Simulation Pipeline”You don’t need to understand the pipeline to use backtesting, but knowing what happens helps you interpret edge cases and trust the results.
1. Data Loading
Section titled “1. Data Loading”Validates inputs, loads historical candles, filters to date range, and computes warmup offset (extra bars needed 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. Falls back to OHLC interpolation if high-resolution data isn’t available.
3. MipMap Building
Section titled “3. MipMap Building”Aggregates candles into multiple timeframes (1m, 5m, 15m, 1h, 4h, 1d) for efficient multi-resolution indicator computation.
4. Indicator Calculation
Section titled “4. Indicator Calculation”Computes all indicator values across the full date range using the warmup period.
5. Resampling
Section titled “5. Resampling”Converts continuous indicator values into boolean signals based on signal type and threshold. Detects signal crossings — the exact bars where an indicator flips.
6. Event-Driven Simulation
Section titled “6. Event-Driven Simulation”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
What’s Next?
Section titled “What’s Next?”| Want to… | Go to |
|---|---|
| Take your strategy live | Deploy Features → |
| Tweak your algorithm settings | Build Features → |
| Walk through your first backtest | Backtest Walkthrough → |