package backtest import "math" // Metrics 回测绩效指标。 type Metrics struct { TotalReturn float64 // 累计收益率 AnnualReturn float64 // 年化收益率 MaxDrawdown float64 // 最大回撤(正值) Sharpe float64 // 年化夏普比率(无风险利率为 0) Volatility float64 // 年化波动率 WinRatio float64 // 正收益交易日占比 TradeDays int // 回测交易日数 } const tradingDaysPerYear = 252.0 // ComputeMetrics 从净值曲线计算绩效指标。 func ComputeMetrics(curve []EquityPoint) Metrics { m := Metrics{TradeDays: len(curve)} if len(curve) == 0 { return m } m.TotalReturn = curve[len(curve)-1].Return // 日收益率序列。 rets := make([]float64, 0, len(curve)-1) for i := 1; i < len(curve); i++ { if curve[i-1].Value > 0 { rets = append(rets, curve[i].Value/curve[i-1].Value-1) } } // 最大回撤:遍历峰值。 var peak, maxDD float64 for _, p := range curve { if p.Value > peak { peak = p.Value } if dd := peak - p.Value; dd > maxDD { maxDD = dd } } if peak > 0 { m.MaxDrawdown = maxDD / peak } n := len(rets) if n == 0 { return m } // 年化收益率。 years := float64(n) / tradingDaysPerYear if years > 0 && 1+m.TotalReturn > 0 { m.AnnualReturn = math.Pow(1+m.TotalReturn, 1/years) - 1 } // 波动率与夏普。 mean := 0.0 for _, r := range rets { mean += r } mean /= float64(n) var variance float64 for _, r := range rets { variance += (r - mean) * (r - mean) } variance /= float64(n - 1) m.Volatility = math.Sqrt(variance) * math.Sqrt(tradingDaysPerYear) if m.Volatility > 0 { m.Sharpe = mean * tradingDaysPerYear / m.Volatility } // 正收益日占比。 win := 0 for _, r := range rets { if r > 0 { win++ } } m.WinRatio = float64(win) / float64(n) return m }