DocsReferenceMetrics glossary
ReferenceReference

Metrics glossary

Every metric a finished backtest returns in its results summary, defined here for lookup: what it means, its units, and exactly how the engine computes it. This page is the per-metric dictionary. For how to read a full run and its three result frames together, see reading backtest results.

Updated Jul 20265 min read

Every return-based, risk-adjusted, and drawdown metric on this page is computed off a mid-marked NLV curve. Entry and exit legs fill per the run's fill mode, but the per-minute net-liquidation value (NLV) that feeds the equity curve always marks at mid. Annualization defaults to 252 trading periods.

How these are computed

All metrics come from a finished run's summary, derived at the end of the run from its snapshot stream, its closed positions, and its trade ledger. Return-based and risk-adjusted metrics are built from one per-day NLV series: the latest snapshot NLV on each distinct trading date, in chronological order. The count of those dates is trading_days, the denominator for annualization.

Annualization scales daily statistics to a yearly figure using annualization_periods, which defaults to 252 (a ~252-session equity year). It is a configurable positive integer: 365 for a 24/7 crypto calendar, 260 for a 5-day market, so annualization matches the run's cadence.

Return-based metrics use per-day log returns of the NLV curve, ln(NLV_t / NLV_{t-1}), with starting capital as day 0. A non-positive prior-day or current-day NLV short-circuits that day's return to 0.0 (the log is undefined). Log returns make the daily sum tie out exactly with ln(final_NLV / starting_capital), with no arithmetic-vs-geometric gap inflating the ratios on a high-volatility run.

Return metrics

Metric
Units
Formula
total_return_pct
percent
(final_nlv - starting_capital) / starting_capital * 100
compound_annual_growth_rate
fraction
(1 + total_return_pct/100) ** (annualization_periods / trading_days) - 1
annualized_volatility
fraction
std(daily_log_returns) * sqrt(annualization_periods)

total_return_pct is the raw percentage change in NLV over the run.

compound_annual_growth_rate (CAGR) annualizes that return using annualization_periods and the run's trading_days. It returns 0 if trading_days is below 1 or if the growth base (1 + total_return_pct/100) is non-positive (a run that lost more than 100%).

annualized_volatility is the sample standard deviation of the per-day log returns, scaled by sqrt(annualization_periods). It returns 0 when the run has fewer than 2 daily returns.

Note the mixed units: total_return_pct is a percent, while CAGR and volatility are fractions (0.15 means 15%). Both CAGR and volatility depend on annualization_periods.

Risk-adjusted metrics

Metric
Formula
sharpe_ratio
mean(r) / std(r) * sqrt(annualization_periods)
sortino_ratio
mean(r) / downside_std * sqrt(annualization_periods)

Both run on the per-day log-return series r off the mid-marked equity curve, and both scale by sqrt(annualization_periods).

sharpe_ratio uses the sample standard deviation (n-1) of all daily returns as the denominator. The risk-free rate is 0. It returns 0 if there are fewer than 2 returns or if the standard deviation is 0.

sortino_ratio keeps the same numerator, mean(r) over all returns, but uses only the negative returns for its denominator (their sample standard deviation). It returns 0 if there are fewer than 2 negative returns or that downside deviation is 0.

Drawdown

Metric
Units
max_drawdown_pct
positive percent
max_drawdown_dollars
positive dollars

Both are the largest peak-to-trough decline over the full per-snapshot NLV series, not the per-day series. max_drawdown_pct is the largest (peak - trough) / peak * 100; max_drawdown_dollars is the largest peak - trough in dollars. Both are reported as positive magnitudes. The peak and trough need not fall on the same date, and the two metrics can be driven by different peak-trough pairs.

Trade statistics

Each closed position contributes one row of trade data. A closed position's P&L is the sum of its cash flows over its life. Open-at-end positions are excluded from every trade statistic, but their mid mark still counts in final_nlv_dollars.

Metric
Units
Meaning
trade_count
count
Closed positions.
win_count
count
Closed positions with P&L greater than 0.
loss_count
count
Closed positions with P&L less than 0.
win_rate
fraction
win_count / trade_count, or 0 when there are no trades.
average_pnl_per_trade_dollars
dollars
Total closed P&L divided by trade_count.
average_winning_pnl_dollars
dollars
Mean P&L over the winners only.
average_losing_pnl_dollars
dollars
Mean P&L over the losers only (negative).
average_holding_period_minutes
minutes
Mean of close-minus-open across closed positions.
max_concurrent_positions
count
Largest number of positions open in any one snapshot.
total_realized_pnl_dollars
dollars
Sum of closed-position P&L.
final_nlv_dollars
dollars
NLV at the end of the run.

win_count and loss_count split on strictly greater-than and strictly less-than 0, so a position that closes at exactly 0 P&L is in trade_count but is neither a win nor a loss. win_count + loss_count can therefore be below trade_count.

average_holding_period_minutes is in minutes, not days. A normal session is 390 minutes, so average_holding_period_minutes / 390 approximates full sessions held.

Margin & utilization

avg_margin_utilization_pct is the mean, over snapshots with positive NLV, of maintenance margin divided by NLV. Despite the _pct name it is a fraction, not scaled by 100: 0.35 means maintenance margin averaged 35% of NLV over the run. Snapshots where NLV is non-positive are dropped from the average.

This ties to the run's margin curve, which carries initial margin, maintenance margin, and excess liquidity per snapshot, in dollars and as a fraction of NLV. For how maintenance margin is set and decomposed, see margin and auto-decomposition.

Risk-mechanism counters

Counts of what the risk layer did over the run. Each stays at 0 on a run that never trips it.

Counter
Increments when
adjustment_count
An adjustment is accepted and applied.
soft_trim_count
A position is closed by a soft-trim response to a margin deficit.
forced_liquidation_count
A position is closed by a hard-deficit forced liquidation.
margin_warning_count
A margin-warning event fires (excess liquidity crosses the warning threshold).
liquidation_exhausted_count
A forced liquidation runs out of positions to close and the deficit remains.
greedy_fallback_position_count
A position whose latest margin decomposition used the greedy fallback rather than an exact set-packing solution.

Halt fields

Field
Type
halted
bool
halt_timestamp
timestamp or null
halt_reason
equity_below_zero or null

halted is true when the run stopped early on a risk condition. The only halt reason is equity_below_zero: NLV at or below 0. When a run halts, halt_timestamp carries the minute it happened and halt_reason carries the cause; on a normal run all three are false or null.

A halted run is terminal. It does not produce a resume snapshot, so it cannot be extended. For where the halt sits in a run's lifecycle, see that page.

Close reasons

Every row in the trade ledger carries one of six close reasons.

Close reason
The position closed because
exit_signal
Its exit signal fired.
expiration
It was force-closed at expiration.
soft_trim
A soft-trim margin response closed it.
forced_liquidation
A hard-deficit forced liquidation closed it.
equity_halt
The run halted and all open positions were force-closed.
adjustment_full_close
An adjustment action closed out its last leg.
Note

margin_insufficient is not a close reason. It is a no-action reason recorded when an entry is rejected before the position ever opens, so the position has no trade-ledger row at all. It belongs with entry-rejection reasons, not here. If you arrived expecting it in this list from an older enum, that is why it is absent.

Quarterly P&L (derived, not a stored stat)

Note

Quarterly P&L is not a first-class metric in the run summary. It is a derived view, built by grouping per-trade ledger P&L (or the per-snapshot equity curve) into calendar quarters after the run. Treat any quarterly breakdown as a rollup you compute over the ledger, not as a number the engine returns.

Benchmark-relative metrics

Every run carries a benchmark, S&P 500 total return by default, so the strategy's results can be read against a market baseline. This page defines the strategy's own metrics; for how a run reports against its benchmark, see reading backtest results.

One thing worth restating before you compare any of these numbers to a broker statement: commissions are modeled as fixed per-contract dollars and can be set to zero. See fills, marks and slippage for how commissions and the run's fill mode price into a finished number, and backtest assumptions and realism for the full list of what a finished number does and does not include.

Was this page helpful?