blockchain/internal/lib/strategy/ema_test.go

103 lines
2.7 KiB
Go
Raw Permalink Normal View History

2025-08-15 01:36:36 +00:00
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())
}
})
}
}