mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
Merge pull request #892 from c9s/feature/pivot-right-window
feature: add pivot low right window support
This commit is contained in:
commit
066b0ca30e
3
pkg/bbgo/doc.go
Normal file
3
pkg/bbgo/doc.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Package bbgo provides the core BBGO API for strategies
|
||||||
|
|
||||||
|
package bbgo
|
|
@ -23,9 +23,8 @@ type StandardIndicatorSet struct {
|
||||||
|
|
||||||
// Standard indicators
|
// Standard indicators
|
||||||
// interval -> window
|
// interval -> window
|
||||||
boll map[types.IntervalWindowBandWidth]*indicator.BOLL
|
iwbIndicators map[types.IntervalWindowBandWidth]*indicator.BOLL
|
||||||
stoch map[types.IntervalWindow]*indicator.STOCH
|
iwIndicators map[types.IntervalWindow]indicator.KLinePusher
|
||||||
simples map[types.IntervalWindow]indicator.KLinePusher
|
|
||||||
|
|
||||||
stream types.Stream
|
stream types.Stream
|
||||||
store *MarketDataStore
|
store *MarketDataStore
|
||||||
|
@ -36,32 +35,30 @@ func NewStandardIndicatorSet(symbol string, stream types.Stream, store *MarketDa
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
store: store,
|
store: store,
|
||||||
stream: stream,
|
stream: stream,
|
||||||
simples: make(map[types.IntervalWindow]indicator.KLinePusher),
|
iwIndicators: make(map[types.IntervalWindow]indicator.KLinePusher),
|
||||||
|
iwbIndicators: make(map[types.IntervalWindowBandWidth]*indicator.BOLL),
|
||||||
boll: make(map[types.IntervalWindowBandWidth]*indicator.BOLL),
|
|
||||||
stoch: make(map[types.IntervalWindow]*indicator.STOCH),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StandardIndicatorSet) initAndBind(inc indicator.KLinePusher, iw types.IntervalWindow) {
|
func (s *StandardIndicatorSet) initAndBind(inc indicator.KLinePusher, interval types.Interval) {
|
||||||
if klines, ok := s.store.KLinesOfInterval(iw.Interval); ok {
|
if klines, ok := s.store.KLinesOfInterval(interval); ok {
|
||||||
for _, k := range *klines {
|
for _, k := range *klines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.stream.OnKLineClosed(types.KLineWith(s.Symbol, iw.Interval, inc.PushK))
|
s.stream.OnKLineClosed(types.KLineWith(s.Symbol, interval, inc.PushK))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StandardIndicatorSet) allocateSimpleIndicator(t indicator.KLinePusher, iw types.IntervalWindow) indicator.KLinePusher {
|
func (s *StandardIndicatorSet) allocateSimpleIndicator(t indicator.KLinePusher, iw types.IntervalWindow) indicator.KLinePusher {
|
||||||
inc, ok := s.simples[iw]
|
inc, ok := s.iwIndicators[iw]
|
||||||
if ok {
|
if ok {
|
||||||
return inc
|
return inc
|
||||||
}
|
}
|
||||||
|
|
||||||
inc = t
|
inc = t
|
||||||
s.initAndBind(inc, iw)
|
s.initAndBind(inc, iw.Interval)
|
||||||
s.simples[iw] = inc
|
s.iwIndicators[iw] = inc
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +74,13 @@ func (s *StandardIndicatorSet) EWMA(iw types.IntervalWindow) *indicator.EWMA {
|
||||||
return inc.(*indicator.EWMA)
|
return inc.(*indicator.EWMA)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VWMA
|
||||||
|
func (s *StandardIndicatorSet) VWMA(iw types.IntervalWindow) *indicator.VWMA {
|
||||||
|
inc := s.allocateSimpleIndicator(&indicator.VWMA{IntervalWindow: iw}, iw)
|
||||||
|
return inc.(*indicator.VWMA)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (s *StandardIndicatorSet) PivotLow(iw types.IntervalWindow) *indicator.PivotLow {
|
func (s *StandardIndicatorSet) PivotLow(iw types.IntervalWindow) *indicator.PivotLow {
|
||||||
inc := s.allocateSimpleIndicator(&indicator.PivotLow{IntervalWindow: iw}, iw)
|
inc := s.allocateSimpleIndicator(&indicator.PivotLow{IntervalWindow: iw}, iw)
|
||||||
return inc.(*indicator.PivotLow)
|
return inc.(*indicator.PivotLow)
|
||||||
|
@ -108,30 +112,24 @@ func (s *StandardIndicatorSet) HULL(iw types.IntervalWindow) *indicator.HULL {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StandardIndicatorSet) STOCH(iw types.IntervalWindow) *indicator.STOCH {
|
func (s *StandardIndicatorSet) STOCH(iw types.IntervalWindow) *indicator.STOCH {
|
||||||
inc, ok := s.stoch[iw]
|
inc := s.allocateSimpleIndicator(&indicator.STOCH{IntervalWindow: iw}, iw)
|
||||||
if !ok {
|
return inc.(*indicator.STOCH)
|
||||||
inc = &indicator.STOCH{IntervalWindow: iw}
|
|
||||||
s.initAndBind(inc, iw)
|
|
||||||
s.stoch[iw] = inc
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOLL returns the bollinger band indicator of the given interval, the window and bandwidth
|
// BOLL returns the bollinger band indicator of the given interval, the window and bandwidth
|
||||||
func (s *StandardIndicatorSet) BOLL(iw types.IntervalWindow, bandWidth float64) *indicator.BOLL {
|
func (s *StandardIndicatorSet) BOLL(iw types.IntervalWindow, bandWidth float64) *indicator.BOLL {
|
||||||
iwb := types.IntervalWindowBandWidth{IntervalWindow: iw, BandWidth: bandWidth}
|
iwb := types.IntervalWindowBandWidth{IntervalWindow: iw, BandWidth: bandWidth}
|
||||||
inc, ok := s.boll[iwb]
|
inc, ok := s.iwbIndicators[iwb]
|
||||||
if !ok {
|
if !ok {
|
||||||
inc = &indicator.BOLL{IntervalWindow: iw, K: bandWidth}
|
inc = &indicator.BOLL{IntervalWindow: iw, K: bandWidth}
|
||||||
s.initAndBind(inc, iw)
|
s.initAndBind(inc, iw.Interval)
|
||||||
|
|
||||||
if debugBOLL {
|
if debugBOLL {
|
||||||
inc.OnUpdate(func(sma float64, upBand float64, downBand float64) {
|
inc.OnUpdate(func(sma float64, upBand float64, downBand float64) {
|
||||||
logrus.Infof("%s BOLL %s: sma=%f up=%f down=%f", s.Symbol, iw.String(), sma, upBand, downBand)
|
logrus.Infof("%s BOLL %s: sma=%f up=%f down=%f", s.Symbol, iw.String(), sma, upBand, downBand)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
s.boll[iwb] = inc
|
s.iwbIndicators[iwb] = inc
|
||||||
}
|
}
|
||||||
|
|
||||||
return inc
|
return inc
|
||||||
|
|
|
@ -38,8 +38,8 @@ func (inc *DEMA) TestUpdate(value float64) *DEMA {
|
||||||
func (inc *DEMA) Update(value float64) {
|
func (inc *DEMA) Update(value float64) {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
inc.SeriesBase.Series = inc
|
inc.SeriesBase.Series = inc
|
||||||
inc.a1 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.a1 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.a2 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.a2 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.a1.Update(value)
|
inc.a1.Update(value)
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (inc *EWMA) PushK(k types.KLine) {
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last())
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateKLinesEMA(allKLines []types.KLine, priceF KLinePriceMapper, window int) float64 {
|
func CalculateKLinesEMA(allKLines []types.KLine, priceF KLineValueMapper, window int) float64 {
|
||||||
var multiplier = 2.0 / (float64(window) + 1)
|
var multiplier = 2.0 / (float64(window) + 1)
|
||||||
return ewma(MapKLinePrice(allKLines, priceF), multiplier)
|
return ewma(MapKLinePrice(allKLines, priceF), multiplier)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1027,7 +1027,7 @@ func buildKLines(prices []fixedpoint.Value) (klines []types.KLine) {
|
||||||
func Test_calculateEWMA(t *testing.T) {
|
func Test_calculateEWMA(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
allKLines []types.KLine
|
allKLines []types.KLine
|
||||||
priceF KLinePriceMapper
|
priceF KLineValueMapper
|
||||||
window int
|
window int
|
||||||
}
|
}
|
||||||
var input []fixedpoint.Value
|
var input []fixedpoint.Value
|
||||||
|
|
|
@ -24,9 +24,9 @@ var _ types.SeriesExtend = &HULL{}
|
||||||
func (inc *HULL) Update(value float64) {
|
func (inc *HULL) Update(value float64) {
|
||||||
if inc.result == nil {
|
if inc.result == nil {
|
||||||
inc.SeriesBase.Series = inc
|
inc.SeriesBase.Series = inc
|
||||||
inc.ma1 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window / 2}}
|
inc.ma1 = &EWMA{IntervalWindow: types.IntervalWindow{Interval: inc.Interval, Window: inc.Window / 2}}
|
||||||
inc.ma2 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.ma2 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.result = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, int(math.Sqrt(float64(inc.Window)))}}
|
inc.result = &EWMA{IntervalWindow: types.IntervalWindow{Interval: inc.Interval, Window: int(math.Sqrt(float64(inc.Window)))}}
|
||||||
}
|
}
|
||||||
inc.ma1.Update(value)
|
inc.ma1.Update(value)
|
||||||
inc.ma2.Update(value)
|
inc.ma2.Update(value)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package indicator
|
||||||
|
|
||||||
import "github.com/c9s/bbgo/pkg/types"
|
import "github.com/c9s/bbgo/pkg/types"
|
||||||
|
|
||||||
type KLinePriceMapper func(k types.KLine) float64
|
type KLineValueMapper func(k types.KLine) float64
|
||||||
|
|
||||||
func KLineOpenPriceMapper(k types.KLine) float64 {
|
func KLineOpenPriceMapper(k types.KLine) float64 {
|
||||||
return k.Open.Float64()
|
return k.Open.Float64()
|
||||||
|
@ -24,7 +24,7 @@ func KLineVolumeMapper(k types.KLine) float64 {
|
||||||
return k.Volume.Float64()
|
return k.Volume.Float64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapKLinePrice(kLines []types.KLine, f KLinePriceMapper) (prices []float64) {
|
func MapKLinePrice(kLines []types.KLine, f KLineValueMapper) (prices []float64) {
|
||||||
for _, k := range kLines {
|
for _, k := range kLines {
|
||||||
prices = append(prices, f(k))
|
prices = append(prices, f(k))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type KLineValueMapper func(k types.KLine) float64
|
|
||||||
|
|
||||||
//go:generate callbackgen -type Pivot
|
//go:generate callbackgen -type Pivot
|
||||||
type Pivot struct {
|
type Pivot struct {
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package indicator
|
package indicator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,9 +42,8 @@ func (inc *PivotLow) Update(value float64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
low, err := calculatePivotLow(inc.Lows, inc.Window)
|
low, ok := calculatePivotLow(inc.Lows, inc.Window, inc.RightWindow)
|
||||||
if err != nil {
|
if !ok {
|
||||||
log.WithError(err).Errorf("can not calculate pivot low")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,17 +62,43 @@ func (inc *PivotLow) PushK(k types.KLine) {
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last())
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculatePivotLow(lows types.Float64Slice, window int) (float64, error) {
|
func calculatePivotF(values types.Float64Slice, left, right int, f func(a, pivot float64) bool) (float64, bool) {
|
||||||
length := len(lows)
|
length := len(values)
|
||||||
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
|
end := length - 1
|
||||||
min := lows[end-(window-1):].Min()
|
index := end - right
|
||||||
if min == lows.Index(int(window/2.)-1) {
|
val := values[index]
|
||||||
return min, nil
|
|
||||||
|
for i := index - left; i <= index+right; i++ {
|
||||||
|
if i == index {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0., nil
|
// return if we found lower value
|
||||||
|
if !f(values[i], val) {
|
||||||
|
return 0.0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculatePivotHigh(highs types.Float64Slice, left, right int) (float64, bool) {
|
||||||
|
return calculatePivotF(highs, left, right, func(a, pivot float64) bool {
|
||||||
|
return a < pivot
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculatePivotLow(lows types.Float64Slice, left, right int) (float64, bool) {
|
||||||
|
return calculatePivotF(lows, left, right, func(a, pivot float64) bool {
|
||||||
|
return a > pivot
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
51
pkg/indicator/pivot_low_test.go
Normal file
51
pkg/indicator/pivot_low_test.go
Normal file
|
@ -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)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -86,7 +86,7 @@ func (inc *SMA) LoadK(allKLines []types.KLine) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateSMA(kLines []types.KLine, window int, priceF KLinePriceMapper) (float64, error) {
|
func calculateSMA(kLines []types.KLine, window int, priceF KLineValueMapper) (float64, error) {
|
||||||
length := len(kLines)
|
length := len(kLines)
|
||||||
if length == 0 || length < window {
|
if length == 0 || length < window {
|
||||||
return 0.0, fmt.Errorf("insufficient elements for calculating SMA with window = %d", window)
|
return 0.0, fmt.Errorf("insufficient elements for calculating SMA with window = %d", window)
|
||||||
|
|
|
@ -22,9 +22,9 @@ type TEMA struct {
|
||||||
func (inc *TEMA) Update(value float64) {
|
func (inc *TEMA) Update(value float64) {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
inc.SeriesBase.Series = inc
|
inc.SeriesBase.Series = inc
|
||||||
inc.A1 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.A1 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.A2 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.A2 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.A3 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.A3 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
}
|
}
|
||||||
inc.A1.Update(value)
|
inc.A1.Update(value)
|
||||||
a1 := inc.A1.Last()
|
a1 := inc.A1.Last()
|
||||||
|
|
|
@ -33,12 +33,12 @@ func (inc *TILL) Update(value float64) {
|
||||||
inc.VolumeFactor = defaultVolumeFactor
|
inc.VolumeFactor = defaultVolumeFactor
|
||||||
}
|
}
|
||||||
inc.SeriesBase.Series = inc
|
inc.SeriesBase.Series = inc
|
||||||
inc.e1 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.e1 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.e2 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.e2 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.e3 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.e3 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.e4 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.e4 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.e5 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.e5 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.e6 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.e6 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
square := inc.VolumeFactor * inc.VolumeFactor
|
square := inc.VolumeFactor * inc.VolumeFactor
|
||||||
cube := inc.VolumeFactor * square
|
cube := inc.VolumeFactor * square
|
||||||
inc.c1 = -cube
|
inc.c1 = -cube
|
||||||
|
|
|
@ -19,8 +19,8 @@ func (inc *TMA) Update(value float64) {
|
||||||
if inc.s1 == nil {
|
if inc.s1 == nil {
|
||||||
inc.SeriesBase.Series = inc
|
inc.SeriesBase.Series = inc
|
||||||
w := (inc.Window + 1) / 2
|
w := (inc.Window + 1) / 2
|
||||||
inc.s1 = &SMA{IntervalWindow: types.IntervalWindow{inc.Interval, w}}
|
inc.s1 = &SMA{IntervalWindow: types.IntervalWindow{Interval: inc.Interval, Window: w}}
|
||||||
inc.s2 = &SMA{IntervalWindow: types.IntervalWindow{inc.Interval, w}}
|
inc.s2 = &SMA{IntervalWindow: types.IntervalWindow{Interval: inc.Interval, Window: w}}
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.s1.Update(value)
|
inc.s1.Update(value)
|
||||||
|
|
|
@ -91,7 +91,7 @@ func (inc *Volatility) Bind(updater KLineWindowUpdater) {
|
||||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateVOLATILITY(klines []types.KLine, window int, priceF KLinePriceMapper) (float64, error) {
|
func calculateVOLATILITY(klines []types.KLine, window int, priceF KLineValueMapper) (float64, error) {
|
||||||
length := len(klines)
|
length := len(klines)
|
||||||
if length == 0 || length < window {
|
if length == 0 || length < window {
|
||||||
return 0.0, fmt.Errorf("insufficient elements for calculating VOL with window = %d", window)
|
return 0.0, fmt.Errorf("insufficient elements for calculating VOL with window = %d", window)
|
||||||
|
|
|
@ -100,7 +100,7 @@ func (inc *VWAP) Bind(updater KLineWindowUpdater) {
|
||||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateVWAP(klines []types.KLine, priceF KLinePriceMapper, window int) float64 {
|
func calculateVWAP(klines []types.KLine, priceF KLineValueMapper, window int) float64 {
|
||||||
vwap := VWAP{IntervalWindow: types.IntervalWindow{Window: window}}
|
vwap := VWAP{IntervalWindow: types.IntervalWindow{Window: window}}
|
||||||
for _, k := range klines {
|
for _, k := range klines {
|
||||||
vwap.Update(priceF(k), k.Volume.Float64())
|
vwap.Update(priceF(k), k.Volume.Float64())
|
||||||
|
|
|
@ -70,6 +70,15 @@ func (inc *VWMA) Update(price, volume float64) {
|
||||||
inc.Values.Push(vwma)
|
inc.Values.Push(vwma)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (inc *VWMA) PushK(k types.KLine) {
|
||||||
|
if inc.EndTime != zeroTime && k.EndTime.Before(inc.EndTime) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
inc.Update(k.Close.Float64(), k.Volume.Float64())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (inc *VWMA) CalculateAndUpdate(allKLines []types.KLine) {
|
func (inc *VWMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if len(allKLines) < inc.Window {
|
if len(allKLines) < inc.Window {
|
||||||
return
|
return
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (inc *ZLEMA) Length() int {
|
||||||
func (inc *ZLEMA) Update(value float64) {
|
func (inc *ZLEMA) Update(value float64) {
|
||||||
if inc.lag == 0 || inc.zlema == nil {
|
if inc.lag == 0 || inc.zlema == nil {
|
||||||
inc.SeriesBase.Series = inc
|
inc.SeriesBase.Series = inc
|
||||||
inc.zlema = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
|
inc.zlema = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
inc.lag = int((float64(inc.Window)-1.)/2. + 0.5)
|
inc.lag = int((float64(inc.Window)-1.)/2. + 0.5)
|
||||||
}
|
}
|
||||||
inc.data.Push(value)
|
inc.data.Push(value)
|
||||||
|
|
|
@ -434,8 +434,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
|
|
||||||
// Setup dynamic spread
|
// Setup dynamic spread
|
||||||
if s.DynamicSpread.Enabled {
|
if s.DynamicSpread.Enabled {
|
||||||
s.DynamicSpread.DynamicBidSpread = &indicator.SMA{IntervalWindow: types.IntervalWindow{s.Interval, s.DynamicSpread.Window}}
|
s.DynamicSpread.DynamicBidSpread = &indicator.SMA{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.DynamicSpread.Window}}
|
||||||
s.DynamicSpread.DynamicAskSpread = &indicator.SMA{IntervalWindow: types.IntervalWindow{s.Interval, s.DynamicSpread.Window}}
|
s.DynamicSpread.DynamicAskSpread = &indicator.SMA{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.DynamicSpread.Window}}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.DisableShort {
|
if s.DisableShort {
|
||||||
|
|
|
@ -77,8 +77,11 @@ type IntervalWindow struct {
|
||||||
// The interval of kline
|
// The interval of kline
|
||||||
Interval Interval `json:"interval"`
|
Interval Interval `json:"interval"`
|
||||||
|
|
||||||
// The windows size of the indicator (EWMA and SMA)
|
// The windows size of the indicator (for example, EWMA and SMA)
|
||||||
Window int `json:"window"`
|
Window int `json:"window"`
|
||||||
|
|
||||||
|
// RightWindow is used by the pivot indicator
|
||||||
|
RightWindow int `json:"rightWindow"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type IntervalWindowBandWidth struct {
|
type IntervalWindowBandWidth struct {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user