Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换) - 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制) - 增量同步:支持 -incr/-since 参数按存档日期增量入库 - 策略回测:净值曲线/10指标/基准对比,含基本面过滤 - 用户认证:用户名密码 + 邮箱验证码双登录方式 - 前端:三Tab SPA(实时行情/策略回测/数据管理)
This commit is contained in:
98
internal/backtest/metrics_test.go
Normal file
98
internal/backtest/metrics_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package backtest
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestComputeMetrics_Empty(t *testing.T) {
|
||||
m := ComputeMetrics(nil)
|
||||
if m.TradeDays != 0 {
|
||||
t.Errorf("expected 0 trade days, got %d", m.TradeDays)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeMetrics_SinglePoint(t *testing.T) {
|
||||
curve := []EquityPoint{{Date: "2020-01-02", Value: 100000, Return: 0}}
|
||||
m := ComputeMetrics(curve)
|
||||
if m.TotalReturn != 0 {
|
||||
t.Errorf("expected 0 total return, got %f", m.TotalReturn)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeMetrics_SteadyGrowth(t *testing.T) {
|
||||
// 10 个交易日,每天涨 1%
|
||||
curve := make([]EquityPoint, 11)
|
||||
curve[0] = EquityPoint{Date: "2020-01-02", Value: 100000, Return: 0}
|
||||
for i := 1; i <= 10; i++ {
|
||||
val := curve[i-1].Value * 1.01
|
||||
curve[i] = EquityPoint{
|
||||
Date: "2020-01-02",
|
||||
Value: val,
|
||||
Return: val/100000 - 1,
|
||||
}
|
||||
}
|
||||
m := ComputeMetrics(curve)
|
||||
expectedReturn := 100000*math.Pow(1.01, 10)/100000 - 1
|
||||
if math.Abs(m.TotalReturn-expectedReturn) > 0.0001 {
|
||||
t.Errorf("total return: expected %f, got %f", expectedReturn, m.TotalReturn)
|
||||
}
|
||||
if m.WinRatio != 1.0 {
|
||||
t.Errorf("win ratio: expected 1.0, got %f", m.WinRatio)
|
||||
}
|
||||
if m.MaxDrawdown > 0.0001 {
|
||||
t.Errorf("max drawdown: expected 0, got %f", m.MaxDrawdown)
|
||||
}
|
||||
if m.TradeDays != 11 {
|
||||
t.Errorf("trade days: expected 11, got %d", m.TradeDays)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeMetrics_Drawdown(t *testing.T) {
|
||||
// 先涨后跌:100000 → 110000 → 99000
|
||||
curve := []EquityPoint{
|
||||
{Date: "2020-01-02", Value: 100000, Return: 0},
|
||||
{Date: "2020-01-03", Value: 110000, Return: 0.1},
|
||||
{Date: "2020-01-06", Value: 99000, Return: -0.01},
|
||||
}
|
||||
m := ComputeMetrics(curve)
|
||||
if m.TotalReturn != -0.01 {
|
||||
t.Errorf("total return: expected -0.01, got %f", m.TotalReturn)
|
||||
}
|
||||
// 峰值 110000, 谷 99000, 回撤 = (110000-99000)/110000 = 0.1
|
||||
expectedDD := 0.1
|
||||
if math.Abs(m.MaxDrawdown-expectedDD) > 0.0001 {
|
||||
t.Errorf("max drawdown: expected %f, got %f", expectedDD, m.MaxDrawdown)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeMetrics_MixedPerformance(t *testing.T) {
|
||||
// 模拟实际场景:混合涨跌
|
||||
curve := []EquityPoint{
|
||||
{Date: "2020-01-02", Value: 100000, Return: 0},
|
||||
{Date: "2020-01-03", Value: 101000, Return: 0.01}, // +1%
|
||||
{Date: "2020-01-06", Value: 100500, Return: 0.005}, // -0.5%
|
||||
{Date: "2020-01-07", Value: 102000, Return: 0.02}, // +1.5%
|
||||
{Date: "2020-01-08", Value: 101500, Return: 0.015}, // -0.5%
|
||||
{Date: "2020-01-09", Value: 103000, Return: 0.03}, // +1.5%
|
||||
}
|
||||
m := ComputeMetrics(curve)
|
||||
if m.TotalReturn != 0.03 {
|
||||
t.Errorf("total return: expected 0.03, got %f", m.TotalReturn)
|
||||
}
|
||||
if m.TradeDays != 6 {
|
||||
t.Errorf("trade days: expected 6, got %d", m.TradeDays)
|
||||
}
|
||||
// 3 个正收益日 / 5 个交易日
|
||||
if math.Abs(m.WinRatio-0.6) > 0.0001 {
|
||||
t.Errorf("win ratio: expected 0.6, got %f", m.WinRatio)
|
||||
}
|
||||
// 年化收益应 > 0
|
||||
if m.AnnualReturn <= 0 {
|
||||
t.Errorf("annual return should be positive, got %f", m.AnnualReturn)
|
||||
}
|
||||
// 波动率应 > 0
|
||||
if m.Volatility <= 0 {
|
||||
t.Errorf("volatility should be positive, got %f", m.Volatility)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user