diff --git a/crates/fidc-core/src/metrics.rs b/crates/fidc-core/src/metrics.rs index 8af7255..47990e4 100644 --- a/crates/fidc-core/src/metrics.rs +++ b/crates/fidc-core/src/metrics.rs @@ -558,7 +558,9 @@ fn alpha_beta( } fn drawdown_stats(nav: &[f64]) -> (f64, usize) { - let mut peak = 0.0_f64; + // NAV is measured against the pre-period capital. The first real loss + // must not become a new zero-drawdown baseline. + let mut peak = 1.0_f64; let mut max_drawdown = 0.0_f64; let mut duration = 0_usize; let mut max_duration = 0_usize; @@ -767,6 +769,28 @@ fn safe_div(numerator: f64, denominator: f64, fallback: f64) -> f64 { mod tests { use super::*; + #[test] + fn drawdown_includes_initial_nav_without_adding_a_trading_day() { + let (drawdown, duration) = drawdown_stats(&[0.9, 0.99]); + assert!((drawdown + 0.1).abs() < 1e-12); + assert_eq!(duration, 2); + assert_eq!(drawdown_stats(&[1.0, 1.1, 1.1]), (0.0, 0)); + assert_eq!(drawdown_stats(&[0.0]), (-1.0, 1)); + assert_eq!(drawdown_stats(&[]), (0.0, 0)); + } + + #[test] + fn first_day_loss_is_preserved_in_shared_backtest_metrics() { + let curve = vec![ + equity_point("2025-01-03", 99.16648349337, 98.81608059815, 100.0), + equity_point("2025-01-06", 99.68551588547, 98.65392198168, 98.81608059815), + ]; + let metrics = compute_backtest_metrics(&curve, &[], &[], &[], 100.0, None).unwrap(); + assert!((metrics.max_drawdown + 0.0083351650663).abs() < 1e-12); + assert_eq!(metrics.total_trade_days, 2); + assert_eq!(metrics.max_drawdown_duration_days, 2); + } + fn equity_point( date: &str, total_equity: f64,