#[non_exhaustive]pub struct PerformanceMetrics {Show 38 fields
pub total_return_pct: f64,
pub annualized_return_pct: f64,
pub sharpe_ratio: f64,
pub sortino_ratio: f64,
pub max_drawdown_pct: f64,
pub max_drawdown_duration: i64,
pub win_rate: f64,
pub profit_factor: f64,
pub avg_trade_return_pct: f64,
pub avg_win_pct: f64,
pub avg_loss_pct: f64,
pub avg_trade_duration: f64,
pub total_trades: usize,
pub winning_trades: usize,
pub losing_trades: usize,
pub largest_win: f64,
pub largest_loss: f64,
pub max_consecutive_wins: usize,
pub max_consecutive_losses: usize,
pub calmar_ratio: f64,
pub total_commission: f64,
pub long_trades: usize,
pub short_trades: usize,
pub total_signals: usize,
pub executed_signals: usize,
pub avg_win_duration: f64,
pub avg_loss_duration: f64,
pub time_in_market_pct: f64,
pub max_idle_period: i64,
pub total_dividend_income: f64,
pub kelly_criterion: f64,
pub sqn: f64,
pub expectancy: f64,
pub omega_ratio: f64,
pub tail_ratio: f64,
pub recovery_factor: f64,
pub ulcer_index: f64,
pub serenity_ratio: f64,
}Expand description
Performance metrics summary
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.total_return_pct: f64Total return percentage
annualized_return_pct: f64Annualized return percentage (assumes 252 trading days)
sharpe_ratio: f64Sharpe ratio (risk-free rate = 0)
sortino_ratio: f64Sortino ratio (downside deviation)
max_drawdown_pct: f64Maximum drawdown as a fraction (0.0–1.0, not a percentage).
A value of 0.2 means the equity fell 20% from its peak at most.
Multiply by 100 to get a conventional percentage. See also
max_drawdown_percentage for a
pre-scaled convenience accessor.
max_drawdown_duration: i64Maximum drawdown duration measured in bars (not calendar time).
Counts the number of consecutive bars from a peak until full recovery.
win_rate: f64Win rate: winning_trades / total_trades.
The denominator is total_trades, which includes break-even trades
(pnl == 0.0). Break-even trades are neither wins nor losses, so they
reduce the win rate without appearing in winning_trades or
losing_trades.
profit_factor: f64Profit factor: gross_profit / gross_loss.
Returns f64::MAX when there are no losing trades (zero denominator)
and at least one profitable trade. This avoids f64::INFINITY, which
is not representable in JSON.
avg_trade_return_pct: f64Average trade return percentage
avg_win_pct: f64Average winning trade return percentage
avg_loss_pct: f64Average losing trade return percentage
avg_trade_duration: f64Average trade duration in bars
total_trades: usizeTotal number of trades
winning_trades: usizeNumber of winning trades (pnl > 0.0).
Break-even trades (pnl == 0.0) are counted in neither winning_trades
nor losing_trades, so winning_trades + losing_trades <= total_trades.
losing_trades: usizeNumber of losing trades (pnl < 0.0).
Break-even trades (pnl == 0.0) are counted in neither winning_trades
nor losing_trades. See winning_trades.
largest_win: f64Largest winning trade P&L
largest_loss: f64Largest losing trade P&L
max_consecutive_wins: usizeMaximum consecutive wins
max_consecutive_losses: usizeMaximum consecutive losses
calmar_ratio: f64Calmar ratio: annualized_return_pct / max_drawdown_pct_scaled.
Returns f64::MAX when max drawdown is zero and the strategy is
profitable (avoids f64::INFINITY which cannot be serialized to JSON).
total_commission: f64Total commission paid
long_trades: usizeNumber of long trades
short_trades: usizeNumber of short trades
total_signals: usizeTotal signals generated
executed_signals: usizeSignals that were executed
avg_win_duration: f64Average duration of winning trades in seconds
avg_loss_duration: f64Average duration of losing trades in seconds
time_in_market_pct: f64Fraction of backtest time spent with an open position (0.0 - 1.0)
max_idle_period: i64Longest idle period between trades in seconds (0 if fewer than 2 trades)
total_dividend_income: f64Total dividend income received across all trades
kelly_criterion: f64Kelly Criterion: optimal fraction of capital to risk per trade.
Computed as W - (1 - W) / R where W is win rate and R is
avg_win_pct / abs(avg_loss_pct). A positive value suggests the
strategy has an edge; a negative value suggests it does not. Values
above 1 indicate extreme edge (rare in practice). Returns 0.0 when
there are no losing trades to compute a ratio.
sqn: f64Van Tharp’s System Quality Number.
SQN = (mean_R / std_R) * sqrt(n_trades) where R is the
distribution of per-trade return percentages. Interpretation:
>1.6 = below average, >2.0 = average, >2.5 = good,
>3.0 = excellent, >5.0 = superb, >7.0 = holy grail.
Returns 0.0 when fewer than 2 trades are available.
Note: Van Tharp’s original definition uses R-multiples
(profit/loss normalised by initial risk per trade, i.e. entry-to-stop
distance). Since the engine does not track per-trade initial risk,
this implementation uses return_pct as a proxy. Values will
therefore not match Van Tharp’s published benchmarks exactly.
At least 30 trades are recommended for statistical reliability.
expectancy: f64Expectancy: expected profit per trade in dollar terms.
P(win) × avg_win_dollar + P(loss) × avg_loss_dollar where each
probability is computed independently (winning_trades / total and
losing_trades / total). Unlike avg_trade_return_pct (which is a
percentage), this gives the expected monetary gain or loss per trade
in the same currency as initial_capital. A positive value means the
strategy has a statistical edge; e.g. +$25 means you expect to make
$25 on average per trade taken.
omega_ratio: f64Omega Ratio: probability-weighted ratio of gains to losses.
Σ max(r, 0) / Σ max(-r, 0) computed over bar-by-bar periodic
returns from the equity curve (consistent with Sharpe/Sortino),
using a threshold of 0.0. More general than Sharpe — considers the
full return distribution rather than only mean and standard deviation.
Returns f64::MAX when there are no negative-return bars.
tail_ratio: f64Tail Ratio: ratio of right tail to left tail of trade returns.
abs(p95) / abs(p5) of the trade return distribution using the
floor nearest-rank method (floor(p × n) as the 0-based index).
A value >1 means large wins are more extreme than large losses
(favourable asymmetry). Returns f64::MAX when the 5th-percentile
return is zero. Returns 0.0 when fewer than 2 trades exist.
Note: Reliable interpretation requires at least ~20 trades; with fewer trades the percentile estimates are dominated by individual outliers.
recovery_factor: f64Recovery Factor: net profit relative to maximum drawdown.
total_return_pct / (max_drawdown_pct * 100). Measures how
efficiently the strategy recovers from its worst drawdown. Returns
f64::MAX when there is no drawdown, 0.0 when unprofitable.
ulcer_index: f64Ulcer Index: root-mean-square of drawdown depth across all bars, expressed as a percentage (0–100), consistent with backtesting.py and Peter Martin’s original 1987 definition.
sqrt(mean((drawdown_pct × 100)²)) computed from the equity curve.
Unlike max drawdown, it penalises both depth and duration — a long
shallow drawdown scores higher than a brief deep one. A lower value
indicates a smoother equity curve.
serenity_ratio: f64Serenity Ratio (Martin Ratio / Ulcer Performance Index): excess annualised return per unit of Ulcer Index risk.
(annualized_return_pct - risk_free_rate_pct) / ulcer_index where
both numerator and denominator are in percentage units. Analogous to
the Sharpe Ratio but uses the Ulcer Index as the risk measure,
penalising prolonged drawdowns more heavily than short-term volatility.
Returns f64::MAX when Ulcer Index is zero and excess return is positive.
Implementations§
Source§impl PerformanceMetrics
impl PerformanceMetrics
Sourcepub fn max_drawdown_percentage(&self) -> f64
pub fn max_drawdown_percentage(&self) -> f64
Maximum drawdown as a conventional percentage (0–100).
Equivalent to self.max_drawdown_pct * 100.0. Provided because
max_drawdown_pct is stored as a fraction (0.0–1.0) while most other
return fields use true percentages.
Sourcepub fn calculate(
trades: &[Trade],
equity_curve: &[EquityPoint],
initial_capital: f64,
total_signals: usize,
executed_signals: usize,
risk_free_rate: f64,
bars_per_year: f64,
) -> Self
pub fn calculate( trades: &[Trade], equity_curve: &[EquityPoint], initial_capital: f64, total_signals: usize, executed_signals: usize, risk_free_rate: f64, bars_per_year: f64, ) -> Self
Calculate performance metrics from trades and equity curve.
risk_free_rate is the annual rate (e.g. 0.05 for 5%). It is
converted to a per-bar rate internally before computing Sharpe/Sortino.
bars_per_year controls annualisation (e.g. 252.0 for daily US equity
bars, 52.0 for weekly, 1638.0 for hourly). Affects annualised return,
Sharpe, Sortino, and Calmar calculations.
Trait Implementations§
Source§impl Clone for PerformanceMetrics
impl Clone for PerformanceMetrics
Source§fn clone(&self) -> PerformanceMetrics
fn clone(&self) -> PerformanceMetrics
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for PerformanceMetrics
impl Debug for PerformanceMetrics
Source§impl<'de> Deserialize<'de> for PerformanceMetrics
impl<'de> Deserialize<'de> for PerformanceMetrics
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for PerformanceMetrics
impl RefUnwindSafe for PerformanceMetrics
impl Send for PerformanceMetrics
impl Sync for PerformanceMetrics
impl Unpin for PerformanceMetrics
impl UnsafeUnpin for PerformanceMetrics
impl UnwindSafe for PerformanceMetrics
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more