Skip to main content

PerformanceMetrics

Struct PerformanceMetrics 

Source
#[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
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§total_return_pct: f64

Total return percentage

§annualized_return_pct: f64

Annualized return percentage (assumes 252 trading days)

§sharpe_ratio: f64

Sharpe ratio (risk-free rate = 0)

§sortino_ratio: f64

Sortino ratio (downside deviation)

§max_drawdown_pct: f64

Maximum 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: i64

Maximum drawdown duration measured in bars (not calendar time).

Counts the number of consecutive bars from a peak until full recovery.

§win_rate: f64

Win 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: f64

Profit 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: f64

Average trade return percentage

§avg_win_pct: f64

Average winning trade return percentage

§avg_loss_pct: f64

Average losing trade return percentage

§avg_trade_duration: f64

Average trade duration in bars

§total_trades: usize

Total number of trades

§winning_trades: usize

Number 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: usize

Number 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: f64

Largest winning trade P&L

§largest_loss: f64

Largest losing trade P&L

§max_consecutive_wins: usize

Maximum consecutive wins

§max_consecutive_losses: usize

Maximum consecutive losses

§calmar_ratio: f64

Calmar 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: f64

Total commission paid

§long_trades: usize

Number of long trades

§short_trades: usize

Number of short trades

§total_signals: usize

Total signals generated

§executed_signals: usize

Signals that were executed

§avg_win_duration: f64

Average duration of winning trades in seconds

§avg_loss_duration: f64

Average duration of losing trades in seconds

§time_in_market_pct: f64

Fraction of backtest time spent with an open position (0.0 - 1.0)

§max_idle_period: i64

Longest idle period between trades in seconds (0 if fewer than 2 trades)

§total_dividend_income: f64

Total dividend income received across all trades

§kelly_criterion: f64

Kelly 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: f64

Van 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: f64

Expectancy: 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: f64

Omega 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: f64

Tail 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: f64

Recovery 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: f64

Ulcer 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: f64

Serenity 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

Source

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.

Source

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

Source§

fn clone(&self) -> PerformanceMetrics

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PerformanceMetrics

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for PerformanceMetrics

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for PerformanceMetrics

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Key for T
where T: Clone,

Source§

fn align() -> usize

The alignment necessary for the key. Must return a power of two.
Source§

fn size(&self) -> usize

The size of the key in bytes.
Source§

unsafe fn init(&self, ptr: *mut u8)

Initialize the key in the given memory location. Read more
Source§

unsafe fn get<'a>(ptr: *const u8) -> &'a T

Get a reference to the key from the given memory location. Read more
Source§

unsafe fn drop_in_place(ptr: *mut u8)

Drop the key in place. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> PlanCallbackArgs for T

Source§

impl<T> PlanCallbackOut for T