mod benchmark;
#[cfg(test)]
mod fixtures;
mod metrics;
mod periods;
mod rolling;
mod stats;
mod tags;
pub use benchmark::BenchmarkMetrics;
pub use metrics::PerformanceMetrics;
use serde::{Deserialize, Serialize};
use super::config::BacktestConfig;
use super::position::{Position, Trade};
use super::signal::SignalDirection;
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EquityPoint {
pub timestamp: i64,
pub equity: f64,
pub drawdown_pct: f64,
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignalRecord {
pub timestamp: i64,
pub price: f64,
pub direction: SignalDirection,
pub strength: f64,
pub reason: Option<String>,
pub executed: bool,
#[serde(default)]
pub tags: Vec<String>,
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BacktestResult {
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>,
#[serde(default)]
pub diagnostics: Vec<String>,
#[serde(default)]
pub max_leverage_used: f64,
}
impl BacktestResult {
pub fn summary(&self) -> String {
format!(
"Backtest: {} on {}\n\
Period: {} bars\n\
Initial: ${:.2} -> Final: ${:.2}\n\
Return: {:.2}% | Sharpe: {:.2} | Max DD: {:.2}%\n\
Trades: {} | Win Rate: {:.1}% | Profit Factor: {:.2}",
self.strategy_name,
self.symbol,
self.equity_curve.len(),
self.initial_capital,
self.final_equity,
self.metrics.total_return_pct,
self.metrics.sharpe_ratio,
self.metrics.max_drawdown_pct * 100.0,
self.metrics.total_trades,
self.metrics.win_rate * 100.0,
self.metrics.profit_factor,
)
}
pub fn is_profitable(&self) -> bool {
self.final_equity > self.initial_capital
}
pub fn total_pnl(&self) -> f64 {
self.final_equity - self.initial_capital
}
pub fn num_bars(&self) -> usize {
self.equity_curve.len()
}
}