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") } }