103 lines
2.7 KiB
Go
103 lines
2.7 KiB
Go
|
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())
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
}
|