Stats glossary¶
Backtest.show_stats() and qtrade.utils.stats.calculate_stats() print
a dict with ~25 metrics. This page explains what each one means, how
it’s computed, and a rough guide to interpretation.
For per-asset breakdowns in a portfolio backtest, see
calculate_stats_per_asset(broker) — it returns the trade-level subset
of these metrics keyed by asset symbol.
Time / value¶
Metric |
Meaning |
|---|---|
Start |
First bar timestamp (from |
End |
Last bar processed ( |
Duration |
|
Start Value |
Initial equity (typically equal to starting cash). |
End Value |
Equity at the end of the backtest. |
Returns¶
Metric |
Formula |
Notes |
|---|---|---|
Total Return [%] |
|
Raw, not annualized. |
Total Commission Cost[%] |
Sum of all commissions paid over the run |
Helpful for spotting strategies that bleed on fees. |
Buy & Hold Return [%] |
|
Benchmark to beat. Strategies losing to B&H typically aren’t worth running over passive holding. |
Return (Ann.) [%] |
Geometric mean of daily returns, annualized to 252 (stocks) or 365 (crypto/FX) days |
Compounded annual growth rate. |
Volatility (Ann.) [%] |
|
Standard deviation of returns, scaled to a year. |
The 252 vs 365 annualization factor is auto-detected: if the equity
curve includes weekends as trading days (>≈ 60% of weekend bars are
filled), it’s treated as 365-day; otherwise 252.
Risk¶
Metric |
Formula |
Notes |
|---|---|---|
Max Drawdown [%] |
|
The biggest peak-to-trough decline. Always ≤ 0. |
Max Drawdown Duration |
Length of the longest contiguous “below peak” period |
Returned as a |
Trade statistics¶
Metric |
Notes |
|---|---|
Total Trades |
Count of closed trades across all assets. |
Win Rate [%] |
Percentage of closed trades with positive profit. |
Best Trade [%] |
Largest single trade profit (in account currency, not %). The label says “[%]” for legacy reasons — it’s a P&L value. |
Worst Trade [%] |
Same, but the most negative profit. |
Avg Winning Trade [%] |
Mean profit of winning trades. |
Avg Losing Trade [%] |
Mean profit of losing trades (negative). |
Avg Winning Trade Duration |
Mean time-in-market for winners. |
Avg Losing Trade Duration |
Mean time-in-market for losers. |
If “winners hold longer than losers” you have a “let winners run, cut losers short” pattern. Reverse means you’re cutting winners early.
Performance ratios¶
These compress return-and-risk into a single number. Different ratios penalize different things.
Profit Factor¶
sum(wins) / sum(|losses|)
Higher is better. > 1 means you’re net positive. > 2 is rare and good. NaN if there are no losing trades.
Expectancy¶
(sum(wins) - sum(|losses|)) / total_trades
Average dollar profit per trade. Positive expectancy means each trade is worth taking on average.
Sortino Ratio¶
mean(daily_returns) / std(daily_returns[< 0]) × √annual_days
Like Sharpe, but only penalizes downside volatility. Strategies with asymmetric return distributions (most positive trend-followers) score better here than on Sharpe.
Calmar Ratio¶
abs(annualized_return) / abs(max_drawdown)
How much annual return per unit of worst-case drawdown. > 1 is decent (your annual gains exceed your worst dip).
Omega Ratio¶
sum(returns > threshold) / |sum(returns < threshold)|
Threshold is 0 (so: gains divided by losses, summed across all daily return observations). Captures the full distribution of returns rather than mean/std summary. > 1 is positive.
Sanity-checking your stats¶
A few things to verify before getting excited about a backtest:
Total Trades is reasonable — single-digit trade counts can produce Sharpe = 5 by accident. You want enough trades for the metrics to be statistically meaningful.
Total Return > Buy & Hold — beating B&H is the bar. Losing to it on a long-only strategy means your timing is hurting more than helping.
Win Rate × Avg Win > (1 - Win Rate) × |Avg Loss| — the basic expectancy condition. If this fails, your strategy is structurally losing.
Use
walk_forward_optimizebefore trustingoptimizeresults — in-sample Sharpe is almost always too high. See Walk-forward optimization.