blockchain/internal/lib/strategy/ema_sma_talib_test.go

89 lines
2.2 KiB
Go
Raw Normal View History

2025-08-20 12:35:31 +00:00
package strategy
import (
"github.com/markcheno/go-talib"
"github.com/shopspring/decimal"
"math"
"testing"
)
func dv(v float64) decimal.Decimal { return decimal.NewFromFloat(v) }
func genPrices(n int) []float64 {
out := make([]float64, n)
base := 10.0
for i := 0; i < n; i++ {
out[i] = base + float64(i)*0.5 + math.Sin(float64(i)/3.0)*0.7
}
return out
}
func almostEqualDecFloat(dec decimal.Decimal, f float64, tol float64) bool {
df, _ := dec.Float64()
return math.Abs(df-f) <= tol
}
// 注意:使用 NewEMAForTalib(period)First-Price seed來比對 TA-Lib
func TestEMA_MatchesGoTalib(t *testing.T) {
prices := genPrices(300)
tol := 1e-7 // decimal<->float64 轉換微誤差
periods := []uint{3, 5, 12, 26}
for _, p := range periods {
t.Run("EMA_p="+decimal.NewFromInt(int64(p)).String(), func(t *testing.T) {
ema := NewEMA(p) // 重點:用 TA-Lib 兼容 seed
our := make([]decimal.Decimal, len(prices))
ready := make([]bool, len(prices))
for i, px := range prices {
out := ema.Update(dv(px))
our[i] = out.Value
ready[i] = out.Ready
}
ref := talib.Ema(prices, int(p))
start := p // talib 第一個有效值在 index = period-1
for i := start; i < uint(len(prices)); i++ {
if !ready[i] {
t.Fatalf("i=%d: our not ready but talib has value", i)
}
if !almostEqualDecFloat(our[i], ref[i], tol) {
t.Fatalf("i=%d: EMA mismatch: our=%s ref=%f", i, our[i], ref[i])
}
}
})
}
}
func TestSMA_MatchesGoTalib(t *testing.T) {
prices := genPrices(300)
tol := 1e-9
windows := []uint{3, 5, 20, 50}
for _, w := range windows {
t.Run("SMA_w="+decimal.NewFromInt(int64(w)).String(), func(t *testing.T) {
sma := NewSMA(w)
our := make([]decimal.Decimal, len(prices))
ready := make([]bool, len(prices))
for i, px := range prices {
out := sma.Update(dv(px))
our[i] = out.Value
ready[i] = out.Ready
}
ref := talib.Sma(prices, int(w))
start := w
for i := start; i < uint(len(prices)); i++ {
if !ready[i] {
t.Fatalf("i=%d: our not ready but talib has value", i)
}
if !almostEqualDecFloat(our[i], ref[i], tol) {
t.Fatalf("i=%d: SMA mismatch: our=%s ref=%f", i, our[i], ref[i])
}
}
})
}
}