indicator: add adx

This commit is contained in:
anywhy 2024-03-21 19:18:55 +08:00
parent d58461d1cf
commit 474a8ab864
2 changed files with 130 additions and 0 deletions

69
pkg/indicator/v2/adx.go Normal file
View File

@ -0,0 +1,69 @@
package indicatorv2
import (
"math"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type ADXStream struct {
*types.Float64Series
Plus, Minus *types.Float64Series
window int
prevHigh, prevLow fixedpoint.Value
}
func ADX(source KLineSubscription, window int) *ADXStream {
var (
atr = ATR2(source, window)
dmp = types.NewFloat64Series()
dmm = types.NewFloat64Series()
adx = types.NewFloat64Series()
sdmp = RMA2(dmp, window, true)
sdmm = RMA2(dmm, window, true)
sadx = RMA2(adx, window, true)
s = &ADXStream{
window: window,
Plus: types.NewFloat64Series(),
Minus: types.NewFloat64Series(),
Float64Series: types.NewFloat64Series(),
prevHigh: fixedpoint.Zero,
prevLow: fixedpoint.Zero,
}
)
source.AddSubscriber(func(k types.KLine) {
up, down := k.High.Sub(s.prevHigh), -k.Low.Sub(s.prevLow)
if up.Compare(down) > 0 && up > 0 {
dmp.PushAndEmit(up.Float64())
} else {
dmp.PushAndEmit(0.0)
}
if down.Compare(up) > 0 && down > 0 {
dmm.PushAndEmit(down.Float64())
} else {
dmm.PushAndEmit(0.0)
}
s.Plus.PushAndEmit(sdmp.Last(0) / atr.Last(0) * 100)
s.Minus.PushAndEmit(sdmm.Last(0) / atr.Last(0) * 100)
sum := s.Plus.Last(0) + s.Minus.Last(0)
if sum == 0 {
sum = 1
}
adx.PushAndEmit(math.Abs(s.Plus.Last(0)-s.Minus.Last(0)) / sum)
s.PushAndEmit(sadx.Last(0) * 100)
s.prevHigh, s.prevLow = k.High, k.Low
s.Truncate()
})
return s
}
func (s *ADXStream) Truncate() {
s.Slice = s.Slice.Truncate(MaxNumOfRMA)
}

View File

@ -0,0 +1,61 @@
package indicatorv2
import (
"encoding/json"
"math"
"testing"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
func Test_ADX(t *testing.T) {
var bytes = []byte(`{
"high": [40145.0, 40186.36, 40196.39, 40344.6, 40245.48, 40273.24, 40464.0, 40699.0, 40627.48, 40436.31, 40370.0, 40376.8, 40227.03, 40056.52, 39721.7, 39597.94, 39750.15, 39927.0, 40289.02, 40189.0],
"low": [39870.71, 39834.98, 39866.31, 40108.31, 40016.09, 40094.66, 40105.0, 40196.48, 40154.99, 39800.0, 39959.21, 39922.98, 39940.02, 39632.0, 39261.39, 39254.63, 39473.91, 39555.51, 39819.0, 40006.84],
"close": [40105.78, 39935.23, 40183.97, 40182.03, 40212.26, 40149.99, 40378.0, 40618.37, 40401.03, 39990.39, 40179.13, 40097.23, 40014.72, 39667.85, 39303.1, 39519.99, 39693.79, 39827.96, 40074.94, 40059.84]
}`)
var buildKLines = func(bytes []byte) (kLines []types.KLine) {
var prices map[string][]fixedpoint.Value
_ = json.Unmarshal(bytes, &prices)
for i, h := range prices["high"] {
kLine := types.KLine{High: h, Low: prices["low"][i], Close: prices["close"][i]}
kLines = append(kLines, kLine)
}
return kLines
}
tests := []struct {
name string
kLines []types.KLine
window int
want float64
}{
{
name: "test_binance_btcusdt_1h",
kLines: buildKLines(bytes),
window: 7,
want: 31.895091,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stream := &types.StandardStream{}
kLines := KLines(stream, "", "")
adx := ADX(kLines, tt.window)
for _, k := range tt.kLines {
stream.EmitKLineClosed(k)
}
got := adx.Last(0)
diff := math.Trunc((got-tt.want)*100) / 100
if diff != 0 {
t.Errorf("ADX() = %v, want %v", got, tt.want)
}
})
}
}