Files
stock-analysis/internal/backtest/metrics.go
WarrenZhou e28bfec957 Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换)
- 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制)
- 增量同步:支持 -incr/-since 参数按存档日期增量入库
- 策略回测:净值曲线/10指标/基准对比,含基本面过滤
- 用户认证:用户名密码 + 邮箱验证码双登录方式
- 前端:三Tab SPA(实时行情/策略回测/数据管理)
2026-08-17 20:23:22 +08:00

86 lines
1.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}