pub struct BacktestResult {Show 18 fields
pub symbol: String,
pub initial_cash: f64,
pub final_cash: f64,
pub net_pnl: f64,
pub total_fees: f64,
pub funding_received: f64,
pub funding_paid: f64,
pub candles_processed: usize,
pub signals_emitted: usize,
pub orders_filled: usize,
pub orders_blocked: usize,
pub trades: Vec<TradeOutcome>,
pub max_drawdown: f64,
pub equity_curve: Vec<f64>,
pub equity_times: Vec<i64>,
pub period_returns: Vec<f64>,
pub risk_free_rate: f64,
pub periods_per_year: u32,
}Expand description
Final outcome of a crate::Backtest::run call.
Fields§
§symbol: StringSymbol the backtest was configured for. For multi-symbol backtests this is a comma-separated list in config order.
initial_cash: f64Initial cash balance.
final_cash: f64Final cash balance (= initial + net realised PnL).
net_pnl: f64Total realised PnL net of fees and (when a
FundingModel is configured) funding
cashflows — the per-trade nets plus funding settled on positions
still open at the end of the run.
total_fees: f64Sum of fees charged across every fill.
funding_received: f64Total perp funding collected across the run, in quote currency
(>= 0). Includes settlements on positions still open at the end.
Always 0.0 with FundingModel::None
(the default). #[serde(default)] so previously serialized
results still deserialize.
funding_paid: f64Total perp funding paid across the run, in quote currency,
stored as a positive number. Always 0.0 with funding off.
candles_processed: usizeNumber of candles fed to the brain.
signals_emitted: usizeNumber of non-Hold decisions emitted by the brain.
orders_filled: usizeNumber of orders the engine placed (may be < signals_emitted
if the sizer returned 0 for some signals).
orders_blocked: usizeNumber of non-Hold decisions blocked by a risk gate (the
session-PnL halt or the circuit breaker — see
crate::BacktestConfig::session_pnl /
crate::BacktestConfig::circuit_breaker). Always 0 when no
gate is configured.
trades: Vec<TradeOutcome>Per-trade outcomes, in chronological order.
max_drawdown: f64Maximum peak-to-trough drawdown of equity (cash) over the run,
in quote currency. Always <= 0.
equity_curve: Vec<f64>Portfolio equity at each sample point. The first element is
Self::initial_cash; one additional sample is appended per
candle in the merged event stream.
equity_times: Vec<i64>Candle timestamp (epoch ms — the same unit as
Candle::time) of each
Self::equity_curve sample. The seed sample borrows the first
candle’s timestamp. Empty for empty runs and for results
serialized before this field shipped (#[serde(default)]) — see
Self::equity_points.
period_returns: Vec<f64>Per-period simple returns derived from Self::equity_curve.
Length is equity_curve.len() - 1 for any non-empty run.
risk_free_rate: f64Per-period risk-free rate used by Self::sharpe_ratio and
Self::sortino_ratio. See crate::BacktestConfig::risk_free_rate.
periods_per_year: u32Annualisation factor for the Sharpe and Sortino ratios. See
crate::BacktestConfig::periods_per_year.
Implementations§
Source§impl BacktestResult
impl BacktestResult
Sourcepub fn total_return_pct(&self) -> f64
pub fn total_return_pct(&self) -> f64
Total return as a percentage of initial cash.
Sourcepub fn net_funding(&self) -> f64
pub fn net_funding(&self) -> f64
Net funding cashflow over the run:
Self::funding_received − Self::funding_paid. Positive =
the run collected more funding than it paid. 0.0 with funding
modelling off.
Sourcepub fn equity_points(&self) -> Vec<EquityPoint>
pub fn equity_points(&self) -> Vec<EquityPoint>
The equity curve as timestamped points, zipping
Self::equity_times with Self::equity_curve. Empty for
empty runs and for results deserialized from versions that
predate Self::equity_times (the timestamps are unknowable
there — fall back to Self::equity_curve indices).
Sourcepub fn breakevens(&self) -> usize
pub fn breakevens(&self) -> usize
Count of trades with net PnL == 0.
Sourcepub fn expectancy(&self) -> Option<f64>
pub fn expectancy(&self) -> Option<f64>
Mean net PnL per trade across all trades (wins, losses and
breakevens) — the classic per-trade expectancy. None when the
run closed no trades.
Sourcepub fn avg_win(&self) -> Option<f64>
pub fn avg_win(&self) -> Option<f64>
Mean net PnL of winning trades (> 0). None when there are no
winning trades.
Sourcepub fn avg_loss(&self) -> Option<f64>
pub fn avg_loss(&self) -> Option<f64>
Mean net PnL of losing trades — a negative number (the sign
is kept so expectancy ≈ win_rate·avg_win + loss_rate·avg_loss
holds without juggling signs). None when there are no losing
trades.
Sourcepub fn profit_factor(&self) -> Option<f64>
pub fn profit_factor(&self) -> Option<f64>
Sum of winning trades’ net PnL / sum of losing trades’ net PnL
(positive). None if there are no losing trades.
Sourcepub fn sharpe_ratio(&self) -> Option<f64>
pub fn sharpe_ratio(&self) -> Option<f64>
Annualised Sharpe ratio of the per-period returns.
Computed as √P · (mean(rᵢ - rf) / stddev(rᵢ)) where rᵢ is
each entry in Self::period_returns, rf is
Self::risk_free_rate, stddev is the sample standard
deviation (N - 1 denominator), and P is
Self::periods_per_year.
Returns None when there are fewer than two return samples, or
when the sample stddev is zero (a perfectly flat equity curve —
Sharpe is undefined).
Sourcepub fn sortino_ratio(&self) -> Option<f64>
pub fn sortino_ratio(&self) -> Option<f64>
Annualised Sortino ratio of the per-period returns.
Same shape as Sharpe but only penalises downside deviation —
returns below rf contribute to the denominator, returns above
rf don’t. Specifically √P · mean(rᵢ - rf) / downside_dev
where downside_dev = √(Σ min(rᵢ - rf, 0)² / N). Returns
None if no returns are below rf (no downside to measure) or
fewer than two samples exist.