72 lines
1.7 KiB
Go
72 lines
1.7 KiB
Go
package strategy
|
||
|
||
import (
|
||
"github.com/shopspring/decimal"
|
||
"testing"
|
||
)
|
||
|
||
func TestRSI_WarmupAndRange(t *testing.T) {
|
||
n := 2
|
||
r := NewRSI(n)
|
||
|
||
type step struct {
|
||
close float64
|
||
wantReady bool
|
||
}
|
||
steps := []step{
|
||
{10, false}, // 第一根,只建立 prevC
|
||
{11, false}, // 第二根,累積
|
||
{12, true}, // 第三根,完成 seed -> ready
|
||
{13, true},
|
||
{12, true},
|
||
{11, true},
|
||
{12, true},
|
||
{13, true},
|
||
}
|
||
|
||
for i, st := range steps {
|
||
val, ready := r.Push(CandleForStrategy{C: dv(st.close)})
|
||
if ready != st.wantReady {
|
||
t.Fatalf("step %d: ready got=%v want=%v", i, ready, st.wantReady)
|
||
}
|
||
if ready {
|
||
// RSI 應落在 0..100
|
||
if val.LessThan(decimal.Zero) || val.GreaterThan(decimal.NewFromInt(100)) {
|
||
t.Fatalf("step %d: RSI out of [0,100], got %s", i, val)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestRSI_PerfectUpAndDown(t *testing.T) {
|
||
n := 2
|
||
r := NewRSI(n)
|
||
|
||
// 先 seed
|
||
_, _ = r.Push(CandleForStrategy{C: d(10)})
|
||
_, _ = r.Push(CandleForStrategy{C: d(11)})
|
||
val, ready := r.Push(CandleForStrategy{C: d(12)})
|
||
if !ready {
|
||
t.Fatalf("should be ready after %d candles", n)
|
||
}
|
||
|
||
// 連續上漲:理想情況 avgLoss -> 0,RSI 逼近 100
|
||
val, ready = r.Push(CandleForStrategy{C: d(13)})
|
||
if !ready || !val.LessThanOrEqual(decimal.NewFromInt(100)) {
|
||
t.Fatalf("uptrend: ready=%v val=%s", ready, val)
|
||
}
|
||
|
||
// 連續下跌:理想情況 avgGain -> 0,RSI 逼近 0
|
||
r = NewRSI(n)
|
||
_, _ = r.Push(CandleForStrategy{C: d(13)})
|
||
_, _ = r.Push(CandleForStrategy{C: d(12)})
|
||
val, ready = r.Push(CandleForStrategy{C: d(11)})
|
||
if !ready {
|
||
t.Fatalf("should be ready after %d candles", n)
|
||
}
|
||
val, ready = r.Push(CandleForStrategy{C: d(10)})
|
||
if !ready || !val.GreaterThanOrEqual(decimal.Zero) {
|
||
t.Fatalf("downtrend: ready=%v val=%s", ready, val)
|
||
}
|
||
}
|