89 lines
2.2 KiB
Go
89 lines
2.2 KiB
Go
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])
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|