package strategy import ( "github.com/shopspring/decimal" "testing" ) // --- EMA 的表格式驅動測試 (新增) --- func TestEMA(t *testing.T) { d10 := decimal.NewFromInt(10) d11 := decimal.NewFromInt(11) d12 := decimal.NewFromInt(12) d13 := decimal.NewFromInt(13) d20 := decimal.NewFromInt(20) type pushCheck struct { wantEMA decimal.Decimal wantOK bool } testCases := []struct { name string n uint inputs []decimal.Decimal pushChecks []pushCheck wantFinalEMA decimal.Decimal wantFinalOK bool }{ { name: "EMA-3 標準計算", n: 3, // α = 2 / (3 + 1) = 0.5 inputs: []decimal.Decimal{d10, d11, d12}, pushChecks: []pushCheck{ {d10, true}, // 第一次, EMA = 10 {decimal.NewFromFloat(10.5), true}, // 第二次, 0.5*11 + (1-0.5)*10 = 5.5 + 5 = 10.5 {decimal.NewFromFloat(11.25), true}, // 第三次, 0.5*12 + (1-0.5)*10.5 = 6 + 5.25 = 11.25 }, wantFinalEMA: decimal.NewFromFloat(11.25), wantFinalOK: true, }, { name: "EMA-1 邊界情況", n: 1, // α = 2 / (1 + 1) = 1 inputs: []decimal.Decimal{d10, d13, d11}, pushChecks: []pushCheck{ {d10, true}, // 第一次, EMA = 10 {d13, true}, // 第二次, 1*13 + 0*10 = 13 {d11, true}, // 第三次, 1*11 + 0*13 = 11 }, wantFinalEMA: d11, wantFinalOK: true, }, { name: "EMA-0 無效情況", n: 0, inputs: []decimal.Decimal{d10, d20}, pushChecks: []pushCheck{ {decimal.Zero, false}, {decimal.Zero, false}, }, wantFinalEMA: decimal.Zero, wantFinalOK: false, }, { name: "在空實例上呼叫 GetEMA", n: 5, inputs: []decimal.Decimal{}, pushChecks: []pushCheck{}, wantFinalEMA: decimal.Zero, wantFinalOK: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ema := NewEMA(tc.n) for i, input := range tc.inputs { gotEMA, gotOK := ema.Push(input) if i < len(tc.pushChecks) { check := tc.pushChecks[i] if gotOK != check.wantOK { t.Errorf("Push #%d 的 OK 狀態錯誤: got %v, want %v", i+1, gotOK, check.wantOK) } // 使用 String() 進行比較,避免浮點數精度問題 if gotEMA.String() != check.wantEMA.String() { t.Errorf("Push #%d 的 EMA 值錯誤: got %s, want %s", i+1, gotEMA.String(), check.wantEMA.String()) } } } finalEMA, finalOK := ema.GetEMA() if finalOK != tc.wantFinalOK { t.Errorf("最終 GetEMA 的 OK 狀態錯誤: got %v, want %v", finalOK, tc.wantFinalOK) } if finalEMA.String() != tc.wantFinalEMA.String() { t.Errorf("最終 GetEMA 的 EMA 值錯誤: got %s, want %s", finalEMA.String(), tc.wantFinalEMA.String()) } }) } }