From d847cb5c28e5664e1ff7c8d86dedebe26cea3b44 Mon Sep 17 00:00:00 2001 From: boris Date: Fri, 11 Sep 2026 15:00:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=9B=9E=E6=92=A4=E6=8C=87?= =?UTF-8?q?=E6=A0=87=E7=9A=84=E5=88=9D=E5=A7=8B=E5=87=80=E5=80=BC=E5=9F=BA?= =?UTF-8?q?=E7=BA=BF=E5=B9=B6=E8=A1=A5=E5=85=85=E5=9B=9E=E5=BD=92=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/fidc-core/src/metrics.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) 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,