#[non_exhaustive]pub struct BacktestResult {Show 14 fields
pub symbol: String,
pub strategy_name: String,
pub config: BacktestConfig,
pub start_timestamp: i64,
pub end_timestamp: i64,
pub initial_capital: f64,
pub final_equity: f64,
pub metrics: PerformanceMetrics,
pub trades: Vec<Trade>,
pub equity_curve: Vec<EquityPoint>,
pub signals: Vec<SignalRecord>,
pub open_position: Option<Position>,
pub benchmark: Option<BenchmarkMetrics>,
pub diagnostics: Vec<String>,
}Expand description
Complete backtest result
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.symbol: StringSymbol that was backtested
strategy_name: StringStrategy name
config: BacktestConfigConfiguration used
start_timestamp: i64Start timestamp
end_timestamp: i64End timestamp
initial_capital: f64Initial capital
final_equity: f64Final equity
metrics: PerformanceMetricsPerformance metrics
trades: Vec<Trade>Complete trade log
equity_curve: Vec<EquityPoint>Equity curve (portfolio value at each bar)
signals: Vec<SignalRecord>All signals generated (including non-executed)
open_position: Option<Position>Current open position (if any at end)
benchmark: Option<BenchmarkMetrics>Benchmark comparison metrics (set when a benchmark is provided)
diagnostics: Vec<String>Diagnostic messages (e.g. why zero trades were produced).
Empty when the backtest ran without issues. Populated with actionable hints when the engine detects likely misconfiguration.
Implementations§
Source§impl BacktestResult
impl BacktestResult
Sourcepub fn is_profitable(&self) -> bool
pub fn is_profitable(&self) -> bool
Check if the backtest was profitable
Sourcepub fn rolling_sharpe(&self, window: usize) -> Vec<f64>
pub fn rolling_sharpe(&self, window: usize) -> Vec<f64>
Rolling Sharpe ratio over a sliding window of equity-curve bars.
For each window of window consecutive bar-to-bar returns, computes
the Sharpe ratio using the same risk_free_rate and bars_per_year
as the overall backtest. The first element corresponds to bars
0..window of the equity curve.
Returns an empty vector when window == 0 or when the equity curve
contains fewer than window + 1 bars (i.e. fewer than window
return periods).
§Statistical reliability
Sharpe and Sortino are computed from window return observations using
sample variance (n − 1 degrees of freedom). Very small windows
produce extreme and unreliable values — at least 30 bars is a
practical lower bound; 60–252 is typical for daily backtests.
Sourcepub fn drawdown_series(&self) -> Vec<f64>
pub fn drawdown_series(&self) -> Vec<f64>
Running drawdown fraction at each bar of the equity curve (0.0–1.0).
Each value is the fractional decline from the running all-time-high
equity up to that bar: 0.0 means the equity is at a new peak; 0.2
means it is 20% below the highest value seen so far.
This is not a sliding-window computation. Values are read directly
from the precomputed EquityPoint::drawdown_pct field, which tracks
the running-peak drawdown since the backtest began. To compute the
maximum drawdown within a rolling N-bar window (regime-change
detection), iterate over BacktestResult::equity_curve manually.
The returned vector has the same length as
BacktestResult::equity_curve.
Sourcepub fn rolling_win_rate(&self, window: usize) -> Vec<f64>
pub fn rolling_win_rate(&self, window: usize) -> Vec<f64>
Rolling win rate over a sliding window of consecutive closed trades.
For each window of window trades (ordered by exit timestamp as stored
in the trade log), returns the fraction of winning trades in that
window. The first element corresponds to trades 0..window.
This is a trade-count window, not a time window. To compute win
rate over a fixed calendar period, use by_year,
by_month, or filter BacktestResult::trades
directly by timestamp.
Returns an empty vector when window == 0 or when fewer than window
trades were closed.
Sourcepub fn by_year(&self) -> HashMap<i32, PerformanceMetrics>
pub fn by_year(&self) -> HashMap<i32, PerformanceMetrics>
Performance metrics broken down by calendar year.
Each trade is attributed to the year in which it closed
(exit_timestamp). The equity curve is sliced to the bars that fall
within that calendar year, and the equity at the first bar of the year
serves as initial_capital for the period metrics.
Years with no closed trades are omitted from the result.
§Caveats
- Open positions: a position that is open throughout the year
contributes to the equity-curve drawdown and Sharpe of that year but
does not appear in
total_tradesorwin_rate, because those are derived from closed trades only. Strategies with long holding periods will show systematically low trade counts per year. - Partial years: the first and last year of a backtest typically
cover fewer than 12 months.
annualized_return_pct,calmar_ratio, andserenity_ratioare set to0.0for slices shorter than half a year (< bars_per_year / 2bars) to prevent geometric-compounding distortion. total_signals/executed_signals: these fields are0in period breakdowns because signal records are not partitioned per period. UseBacktestResult::signalsdirectly if needed.
Sourcepub fn by_month(&self) -> HashMap<(i32, u32), PerformanceMetrics>
pub fn by_month(&self) -> HashMap<(i32, u32), PerformanceMetrics>
Performance metrics broken down by calendar month.
Each trade is attributed to the (year, month) in which it closed.
Uses the same equity-slicing approach as by_year;
the same caveats about open positions, partial periods, and signal
counts apply here as well.
Sourcepub fn by_day_of_week(&self) -> HashMap<Weekday, PerformanceMetrics>
pub fn by_day_of_week(&self) -> HashMap<Weekday, PerformanceMetrics>
Performance metrics broken down by day of week.
Each trade is attributed to the weekday on which it closed
(exit_timestamp). Only weekdays present in the trade log appear in
the result. Trades and equity-curve points with timestamps that cannot
be converted to a valid date are silently skipped.
§Sharpe / Sortino annualisation
The equity curve is filtered to bars that fall on each specific
weekday, so consecutive equity points in each slice are roughly one
week apart (for a daily-bar backtest). bars_per_year is inferred
from the calendar span of each slice so that annualisation matches the
actual sampling frequency — you do not need to adjust the config.
The inferred value is approximately 52 for daily bars, 12 for
weekly bars, and so on.
§Other caveats
The same open-position and signal-count caveats from
by_year apply here.
Sourcepub fn trades_by_tag(&self, tag: &str) -> Vec<&Trade>
pub fn trades_by_tag(&self, tag: &str) -> Vec<&Trade>
Return all trades that carry the given tag.
Tags are attached to a [Signal] at strategy time with .tag("name")
and propagated to Trade::tags when the position closes.
Tag comparison is exact and case-sensitive: "Breakout" and
"breakout" are distinct tags. Normalise tag strings at the call
site if case-insensitive matching is required.
Returns an empty Vec when no trades match or no trades have been
tagged at all.
Sourcepub fn metrics_by_tag(&self, tag: &str) -> PerformanceMetrics
pub fn metrics_by_tag(&self, tag: &str) -> PerformanceMetrics
Compute PerformanceMetrics for the subset of trades that carry tag.
A synthetic equity curve is built by replaying the tagged trades in
sequence starting from initial_capital, which gives an accurate
drawdown and return series for that trade subset.
§Capital base
All return metrics (total_return_pct, annualized_return_pct,
sharpe_ratio, calmar_ratio) are computed relative to
initial_capital — the full portfolio starting value — not the
capital actually deployed into tagged trades. A tag that fired 2
small trades on a $10,000 portfolio will show a lower total_return_pct
than a tag that deployed the same profit using more capital.
For capital-independent comparisons across tags prefer:
profit_factor, win_rate, avg_win_pct, avg_loss_pct.
§Sharpe / Sortino annualisation
bars_per_year is inferred from the calendar span of the synthetic
equity curve (same technique as by_day_of_week)
so that a sparsely-firing tag is not penalised by an inflated
annualisation factor.
Returns PerformanceMetrics::empty when no tagged trades exist.
Return a sorted, deduplicated list of all tags used across all trades.
Useful for discovering which tags are present in a result before
calling trades_by_tag or metrics_by_tag.
Trait Implementations§
Source§impl Clone for BacktestResult
impl Clone for BacktestResult
Source§fn clone(&self) -> BacktestResult
fn clone(&self) -> BacktestResult
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for BacktestResult
impl Debug for BacktestResult
Source§impl<'de> Deserialize<'de> for BacktestResult
impl<'de> Deserialize<'de> for BacktestResult
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 BacktestResult
impl !RefUnwindSafe for BacktestResult
impl Send for BacktestResult
impl Sync for BacktestResult
impl Unpin for BacktestResult
impl UnsafeUnpin for BacktestResult
impl !UnwindSafe for BacktestResult
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