diff --git a/pkg/indicator/pivot_low.go b/pkg/indicator/pivot_low.go index 8f605dfe0..c1e630249 100644 --- a/pkg/indicator/pivot_low.go +++ b/pkg/indicator/pivot_low.go @@ -1,11 +1,8 @@ package indicator import ( - "fmt" "time" - log "github.com/sirupsen/logrus" - "github.com/c9s/bbgo/pkg/types" ) @@ -15,6 +12,8 @@ type PivotLow struct { types.IntervalWindow + RightWindow int `json:"rightWindow"` + Lows types.Float64Slice Values types.Float64Slice EndTime time.Time @@ -45,9 +44,8 @@ func (inc *PivotLow) Update(value float64) { return } - low, err := calculatePivotLow(inc.Lows, inc.Window) - if err != nil { - log.WithError(err).Errorf("can not calculate pivot low") + low, ok := calculatePivotLow(inc.Lows, inc.Window, inc.RightWindow) + if !ok { return } @@ -66,17 +64,31 @@ func (inc *PivotLow) PushK(k types.KLine) { inc.EmitUpdate(inc.Last()) } -func calculatePivotLow(lows types.Float64Slice, window int) (float64, error) { +func calculatePivotLow(lows types.Float64Slice, left, right int) (float64, bool) { length := len(lows) - if length == 0 || length < window { - return 0., fmt.Errorf("insufficient elements for calculating with window = %d", window) + + if right == 0 { + right = left + } + + if length == 0 || length < left+right+1 { + return 0.0, false } end := length - 1 - min := lows[end-(window-1):].Min() - if min == lows.Index(int(window/2.)-1) { - return min, nil + index := end - right + val := lows[index] + + for i := index - left; i <= index+right; i++ { + if i == index { + continue + } + + // return if we found lower value + if lows[i] < val { + return 0.0, false + } } - return 0., nil + return val, true } diff --git a/pkg/indicator/pivot_low_test.go b/pkg/indicator/pivot_low_test.go new file mode 100644 index 000000000..318df37a7 --- /dev/null +++ b/pkg/indicator/pivot_low_test.go @@ -0,0 +1,51 @@ +package indicator + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_calculatePivotLow(t *testing.T) { + t.Run("normal", func(t *testing.T) { + low, ok := calculatePivotLow([]float64{15.0, 13.0, 12.0, 10.0, 14.0, 15.0}, 2, 2) + // ^left ----- ^pivot ---- ^right + assert.True(t, ok) + assert.Equal(t, 10.0, low) + + low, ok = calculatePivotLow([]float64{15.0, 13.0, 12.0, 10.0, 14.0, 9.0}, 2, 2) + // ^left ----- ^pivot ---- ^right + assert.False(t, ok) + + low, ok = calculatePivotLow([]float64{15.0, 9.0, 12.0, 10.0, 14.0, 15.0}, 2, 2) + // ^left ----- ^pivot ---- ^right + assert.False(t, ok) + }) + + t.Run("different left and right", func(t *testing.T) { + low, ok := calculatePivotLow([]float64{11.0, 12.0, 16.0, 15.0, 13.0, 12.0, 10.0, 14.0, 15.0}, 5, 2) + // ^left ---------------------- ^pivot ---- ^right + + assert.True(t, ok) + assert.Equal(t, 10.0, low) + + low, ok = calculatePivotLow([]float64{9.0, 8.0, 16.0, 15.0, 13.0, 12.0, 10.0, 14.0, 15.0}, 5, 2) + // ^left ---------------------- ^pivot ---- ^right + // 8.0 < 10.0 + assert.False(t, ok) + assert.Equal(t, 0.0, low) + }) + + t.Run("right window 0", func(t *testing.T) { + low, ok := calculatePivotLow([]float64{15.0, 13.0, 12.0, 10.0, 14.0, 15.0}, 2, 0) + assert.True(t, ok) + assert.Equal(t, 10.0, low) + }) + + t.Run("insufficient length", func(t *testing.T) { + low, ok := calculatePivotLow([]float64{15.0, 13.0, 12.0, 10.0, 14.0, 15.0}, 3, 3) + assert.False(t, ok) + assert.Equal(t, 0.0, low) + }) + +}