Files
Blizzard 92329ddce2
Checks / verify (push) Failing after 9m52s
feat: 完成模拟交易闭环、App 与 A 股行情接入
统一单入口 Go 工程与前端依赖目录,增加后台供应商配置、候选审核、计划预算、成交冲正、持仓复盘和站内通知。接入策略版本、Python 回测、可配置 A 股公开行情及 zap 文件轮转日志,补充迁移、契约与验证。
2026-10-08 22:58:52 +08:00

56 lines
2.1 KiB
Go

package research
import (
"context"
"encoding/json"
"git.sundynix.cn/Blizzard/Veyra/internal/model"
"os/exec"
"testing"
)
func TestNoLookaheadAndExactCash(t *testing.T) {
python, e := exec.LookPath("python3")
if e != nil {
t.Fatal("Python 3 is required for research tests")
}
values := []string{"10.00", "9.00", "8.00", "20.00", "21.00", "7.00", "6.00", "5.00"}
bars := make([]model.HistoricalBar, len(values))
for i, p := range values {
bars[i] = model.HistoricalBar{Date: []string{"2025-01-01", "2025-01-02", "2025-01-03", "2025-01-04", "2025-01-05", "2025-01-06", "2025-01-07", "2025-01-08"}[i], Open: p, High: p, Low: p, Close: p}
}
input := model.ResearchInput{BacktestInput: model.BacktestInput{InitialCash: "10000.00", Bars: bars}, Parameters: model.StrategyParameters{ShortWindow: 2, LongWindow: 3, FeeBPS: 10, SlippageBPS: 0, LotSize: 100}}
data, _ := json.Marshal(input)
result, e := New(python).Execute(context.Background(), data)
if e != nil {
t.Fatal(e)
}
var got struct {
FinalEquity string `json:"final_equity"`
Trades []struct{ Date, Side, Quantity, Price, Fee string }
}
if e := json.Unmarshal(result, &got); e != nil {
t.Fatal(e)
}
if len(got.Trades) != 2 || got.Trades[0].Date != "2025-01-05" || got.Trades[0].Side != "BUY" || got.Trades[0].Quantity != "400" || got.Trades[0].Fee != "8.40" || got.Trades[1].Date != "2025-01-07" || got.FinalEquity != "3989.20" {
t.Fatalf("unexpected result: %s", result)
}
// Changing the current close cannot change a trade at that day's open.
input.Bars[4].Close = "1.00"
data, _ = json.Marshal(input)
result, e = New(python).Execute(context.Background(), data)
if e != nil {
t.Fatal(e)
}
var changed struct {
Trades []struct{ Date, Side, Quantity, Price, Fee string }
}
if json.Unmarshal(result, &changed) != nil || len(changed.Trades) == 0 || changed.Trades[0] != got.Trades[0] {
t.Fatal("current close leaked into open execution")
}
}
func TestMissingInterpreter(t *testing.T) {
if _, e := New("/not/an/interpreter").Execute(context.Background(), json.RawMessage(`{}`)); e == nil {
t.Fatal("missing Python must fail")
}
}