mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 23:05:15 +00:00
Merge pull request #821 from c9s/refactor/indicator-api
refactor: refactor indicator api (canonicalize CalculateAndUpdate, PushK methods)
This commit is contained in:
commit
58d23845ea
|
@ -69,23 +69,43 @@ try to create new indicators in `pkg/indicator/` folder, and add compilation hin
|
|||
// go:generate callbackgen -type StructName
|
||||
type StructName struct {
|
||||
...
|
||||
UpdateCallbacks []func(value float64)
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
```
|
||||
And implement required interface methods:
|
||||
```go
|
||||
|
||||
func (inc *StructName) Update(value float64) {
|
||||
// indicator calculation here...
|
||||
// push value...
|
||||
}
|
||||
|
||||
func (inc *StructName) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
// custom function
|
||||
func (inc *StructName) calculateAndUpdate(kLines []types.KLine) {
|
||||
// calculation...
|
||||
// assign the result to calculatedValue
|
||||
inc.EmitUpdate(calculatedValue) // produce data, broadcast to the subscribers
|
||||
func (inc *StructName) CalculateAndUpdate(kLines []types.KLine) {
|
||||
if len(inc.Values) == 0 {
|
||||
// preload or initialization
|
||||
for _, k := range allKLines {
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
} else {
|
||||
// update new value only
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(calculatedValue) // produce data, broadcast to the subscribers
|
||||
}
|
||||
}
|
||||
|
||||
// custom function
|
||||
func (inc *StructName) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
// filter on interval
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
// required
|
||||
|
|
|
@ -43,7 +43,7 @@ type StandardIndicatorSet struct {
|
|||
ewma map[types.IntervalWindow]*indicator.EWMA
|
||||
boll map[types.IntervalWindowBandWidth]*indicator.BOLL
|
||||
stoch map[types.IntervalWindow]*indicator.STOCH
|
||||
volatility map[types.IntervalWindow]*indicator.VOLATILITY
|
||||
volatility map[types.IntervalWindow]*indicator.Volatility
|
||||
|
||||
store *MarketDataStore
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func NewStandardIndicatorSet(symbol string, store *MarketDataStore) *StandardInd
|
|||
ewma: make(map[types.IntervalWindow]*indicator.EWMA),
|
||||
boll: make(map[types.IntervalWindowBandWidth]*indicator.BOLL),
|
||||
stoch: make(map[types.IntervalWindow]*indicator.STOCH),
|
||||
volatility: make(map[types.IntervalWindow]*indicator.VOLATILITY),
|
||||
volatility: make(map[types.IntervalWindow]*indicator.Volatility),
|
||||
store: store,
|
||||
}
|
||||
|
||||
|
@ -146,10 +146,10 @@ func (set *StandardIndicatorSet) STOCH(iw types.IntervalWindow) *indicator.STOCH
|
|||
}
|
||||
|
||||
// VOLATILITY returns the volatility(stddev) indicator of the given interval and the window size.
|
||||
func (set *StandardIndicatorSet) VOLATILITY(iw types.IntervalWindow) *indicator.VOLATILITY {
|
||||
func (set *StandardIndicatorSet) VOLATILITY(iw types.IntervalWindow) *indicator.Volatility {
|
||||
inc, ok := set.volatility[iw]
|
||||
if !ok {
|
||||
inc = &indicator.VOLATILITY{IntervalWindow: iw}
|
||||
inc = &indicator.Volatility{IntervalWindow: iw}
|
||||
inc.Bind(set.store)
|
||||
set.volatility[iw] = inc
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func (inc *AD) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &AD{}
|
||||
|
||||
func (inc *AD) calculateAndUpdate(kLines []types.KLine) {
|
||||
func (inc *AD) CalculateAndUpdate(kLines []types.KLine) {
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
|
@ -70,12 +70,13 @@ func (inc *AD) calculateAndUpdate(kLines []types.KLine) {
|
|||
inc.EmitUpdate(inc.Last())
|
||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||
}
|
||||
|
||||
func (inc *AD) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *AD) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -75,7 +75,7 @@ func (inc *ALMA) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &ALMA{}
|
||||
|
||||
func (inc *ALMA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *ALMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.input == nil {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
|
@ -91,7 +91,7 @@ func (inc *ALMA) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *ALMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -52,7 +52,7 @@ func Test_ALMA(t *testing.T) {
|
|||
Offset: 0.9,
|
||||
Sigma: 6,
|
||||
}
|
||||
alma.calculateAndUpdate(tt.kLines)
|
||||
alma.CalculateAndUpdate(tt.kLines)
|
||||
assert.InDelta(t, tt.want, alma.Last(), Delta)
|
||||
assert.InDelta(t, tt.next, alma.Index(1), Delta)
|
||||
assert.Equal(t, tt.all, alma.Length())
|
||||
|
|
|
@ -20,6 +20,8 @@ type ATR struct {
|
|||
UpdateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
var _ types.SeriesExtend = &ATR{}
|
||||
|
||||
func (inc *ATR) Update(high, low, cloze float64) {
|
||||
if inc.Window <= 0 {
|
||||
panic("window must be greater than 0")
|
||||
|
@ -72,17 +74,20 @@ func (inc *ATR) Length() int {
|
|||
if inc.RMA == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return inc.RMA.Length()
|
||||
}
|
||||
|
||||
var _ types.SeriesExtend = &ATR{}
|
||||
func (inc *ATR) PushK(k types.KLine) {
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *ATR) CalculateAndUpdate(kLines []types.KLine) {
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
|
|
|
@ -87,12 +87,17 @@ func (inc *ATRP) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &ATRP{}
|
||||
|
||||
func (inc *ATRP) PushK(k types.KLine) {
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *ATRP) CalculateAndUpdate(kLines []types.KLine) {
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
|
|
|
@ -88,7 +88,7 @@ func (inc *BOLL) LastSMA() float64 {
|
|||
return 0.0
|
||||
}
|
||||
|
||||
func (inc *BOLL) Update(kLines []types.KLine) {
|
||||
func (inc *BOLL) CalculateAndUpdate(kLines []types.KLine) {
|
||||
if len(kLines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ func (inc *BOLL) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.Update(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *BOLL) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -59,7 +59,7 @@ func TestBOLL(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
boll := BOLL{IntervalWindow: types.IntervalWindow{Window: tt.window}, K: tt.k}
|
||||
boll.Update(tt.kLines)
|
||||
boll.CalculateAndUpdate(tt.kLines)
|
||||
assert.InDelta(t, tt.up, boll.LastUpBand(), Delta)
|
||||
assert.InDelta(t, tt.down, boll.LastDownBand(), Delta)
|
||||
})
|
||||
|
|
|
@ -3,7 +3,6 @@ package indicator
|
|||
import (
|
||||
"math"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -79,17 +78,20 @@ func (inc *CCI) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &CCI{}
|
||||
|
||||
var three = fixedpoint.NewFromInt(3)
|
||||
|
||||
func (inc *CCI) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *CCI) PushK(k types.KLine) {
|
||||
inc.Update(k.High.Add(k.Low).Add(k.Close).Div(three).Float64())
|
||||
}
|
||||
|
||||
func (inc *CCI) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.TypicalPrice.Length() == 0 {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.High.Add(k.Low).Add(k.Close).Div(three).Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
} else {
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.Update(k.High.Add(k.Low).Add(k.Close).Div(three).Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +101,7 @@ func (inc *CCI) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *CCI) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -48,9 +48,13 @@ func (inc *CA) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &CA{}
|
||||
|
||||
func (inc *CA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *CA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *CA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +64,7 @@ func (inc *CA) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *CA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
12
pkg/indicator/const.go
Normal file
12
pkg/indicator/const.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package indicator
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
var three = fixedpoint.NewFromInt(3)
|
||||
|
||||
var zeroTime = time.Time{}
|
||||
|
|
@ -50,14 +50,20 @@ func (inc *DEMA) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &DEMA{}
|
||||
|
||||
func (inc *DEMA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *DEMA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *DEMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.a1 == nil {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
} else {
|
||||
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
||||
// last k
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +73,7 @@ func (inc *DEMA) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *DEMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -45,7 +45,7 @@ func Test_DEMA(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dema := DEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||
dema.calculateAndUpdate(tt.kLines)
|
||||
dema.CalculateAndUpdate(tt.kLines)
|
||||
last := dema.Last()
|
||||
assert.InDelta(t, tt.want, last, Delta)
|
||||
assert.InDelta(t, tt.next, dema.Index(1), Delta)
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
//go:generate callbackgen -type DMI
|
||||
type DMI struct {
|
||||
types.IntervalWindow
|
||||
|
||||
ADXSmoothing int
|
||||
atr *ATR
|
||||
DMP types.UpdatableSeriesExtend
|
||||
|
@ -23,7 +24,8 @@ type DMI struct {
|
|||
DIMinus *types.Queue
|
||||
ADX types.UpdatableSeriesExtend
|
||||
PrevHigh, PrevLow float64
|
||||
UpdateCallbacks []func(diplus, diminus, adx float64)
|
||||
|
||||
updateCallbacks []func(diplus, diminus, adx float64)
|
||||
}
|
||||
|
||||
func (inc *DMI) Update(high, low, cloze float64) {
|
||||
|
@ -32,6 +34,7 @@ func (inc *DMI) Update(high, low, cloze float64) {
|
|||
inc.DMN = &RMA{IntervalWindow: inc.IntervalWindow, Adjust: true}
|
||||
inc.ADX = &RMA{IntervalWindow: types.IntervalWindow{Window: inc.ADXSmoothing}, Adjust: true}
|
||||
}
|
||||
|
||||
if inc.atr == nil {
|
||||
inc.atr = &ATR{IntervalWindow: inc.IntervalWindow}
|
||||
inc.atr.Update(high, low, cloze)
|
||||
|
@ -41,6 +44,7 @@ func (inc *DMI) Update(high, low, cloze float64) {
|
|||
inc.DIMinus = types.NewQueue(500)
|
||||
return
|
||||
}
|
||||
|
||||
inc.atr.Update(high, low, cloze)
|
||||
up := high - inc.PrevHigh
|
||||
dn := inc.PrevLow - low
|
||||
|
@ -87,15 +91,20 @@ func (inc *DMI) Length() int {
|
|||
return inc.ADX.Length()
|
||||
}
|
||||
|
||||
func (inc *DMI) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *DMI) PushK(k types.KLine) {
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *DMI) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
last := allKLines[len(allKLines)-1]
|
||||
|
||||
if inc.ADX == nil {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.DIPlus.Last(), inc.DIMinus.Last(), inc.ADX.Last())
|
||||
}
|
||||
} else {
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
inc.PushK(last)
|
||||
inc.EmitUpdate(inc.DIPlus.Last(), inc.DIMinus.Last(), inc.ADX.Last())
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +114,7 @@ func (inc *DMI) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *DMI) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *DMI) OnUpdate(cb func(diplus float64, diminus float64, adx float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *DMI) EmitUpdate(diplus float64, diminus float64, adx float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(diplus, diminus, adx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ func Test_DMI(t *testing.T) {
|
|||
IntervalWindow: types.IntervalWindow{Window: 5},
|
||||
ADXSmoothing: 14,
|
||||
}
|
||||
dmi.calculateAndUpdate(tt.klines)
|
||||
dmi.CalculateAndUpdate(tt.klines)
|
||||
assert.InDelta(t, dmi.GetDIPlus().Last(), tt.want.dip, Delta)
|
||||
assert.InDelta(t, dmi.GetDIMinus().Last(), tt.want.dim, Delta)
|
||||
assert.InDelta(t, dmi.GetADX().Last(), tt.want.adx, Delta)
|
||||
|
|
|
@ -68,14 +68,19 @@ func (inc *Drift) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &Drift{}
|
||||
|
||||
func (inc *Drift) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *Drift) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *Drift) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.chng == nil {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
} else {
|
||||
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +90,7 @@ func (inc *Drift) handleKLineWindowUpdate(interval types.Interval, window types.
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *Drift) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -30,7 +30,7 @@ func Test_Drift(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
drift := Drift{IntervalWindow: types.IntervalWindow{Window: 3}}
|
||||
drift.calculateAndUpdate(tt.kLines)
|
||||
drift.CalculateAndUpdate(tt.kLines)
|
||||
assert.Equal(t, drift.Length(), tt.all)
|
||||
for _, v := range drift.Values {
|
||||
assert.LessOrEqual(t, v, 1.0)
|
||||
|
|
|
@ -63,17 +63,21 @@ func (inc *EMV) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &EMV{}
|
||||
|
||||
func (inc *EMV) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *EMV) PushK(k types.KLine) {
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Volume.Float64())
|
||||
}
|
||||
|
||||
func (inc *EMV) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.Values == nil {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Volume.Float64())
|
||||
inc.PushK(k)
|
||||
if inc.Length() > 0 {
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Volume.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +86,7 @@ func (inc *EMV) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *EMV) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -16,7 +16,7 @@ func Test_EMV(t *testing.T) {
|
|||
}
|
||||
emv.Update(63.74, 62.63, 32178836)
|
||||
emv.Update(64.51, 63.85, 36461672)
|
||||
assert.InDelta(t, 1.8, emv.Values.Cache.Last(), Delta)
|
||||
assert.InDelta(t, 1.8, emv.Values.rawValues.Last(), Delta)
|
||||
emv.Update(64.57, 63.81, 51372680)
|
||||
emv.Update(64.31, 62.62, 42476356)
|
||||
emv.Update(63.43, 62.73, 29504176)
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package indicator
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -17,12 +14,15 @@ const MaxNumOfEWMATruncateSize = 100
|
|||
type EWMA struct {
|
||||
types.IntervalWindow
|
||||
types.SeriesBase
|
||||
|
||||
Values types.Float64Slice
|
||||
LastOpenTime time.Time
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
var _ types.SeriesExtend = &EWMA{}
|
||||
|
||||
func (inc *EWMA) Update(value float64) {
|
||||
var multiplier = 2.0 / float64(1+inc.Window)
|
||||
|
||||
|
@ -58,57 +58,35 @@ func (inc *EWMA) Length() int {
|
|||
return len(inc.Values)
|
||||
}
|
||||
|
||||
func (inc *EWMA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
if len(allKLines) < inc.Window {
|
||||
// we can't calculate
|
||||
func (inc *EWMA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.LastOpenTime = k.StartTime.Time()
|
||||
}
|
||||
|
||||
func (inc *EWMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if len(inc.Values) == 0 {
|
||||
for _, k := range allKLines {
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
} else {
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
||||
func (inc *EWMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
var priceF = KLineClosePriceMapper
|
||||
var dataLen = len(allKLines)
|
||||
var multiplier = 2.0 / (float64(inc.Window) + 1)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
// init the values fromNthK the kline data
|
||||
var fromNthK = 1
|
||||
if len(inc.Values) == 0 {
|
||||
// for the first value, we should use the close price
|
||||
inc.Values = []float64{priceF(allKLines[0])}
|
||||
} else {
|
||||
if len(inc.Values) >= MaxNumOfEWMA {
|
||||
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
||||
}
|
||||
|
||||
fromNthK = len(inc.Values)
|
||||
|
||||
// update ewma with the existing values
|
||||
for i := dataLen - 1; i > 0; i-- {
|
||||
var k = allKLines[i]
|
||||
if k.StartTime.After(inc.LastOpenTime) {
|
||||
fromNthK = i
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := fromNthK; i < dataLen; i++ {
|
||||
var k = allKLines[i]
|
||||
var ewma = priceF(k)*multiplier + (1-multiplier)*inc.Values[i-1]
|
||||
inc.Values.Push(ewma)
|
||||
inc.LastOpenTime = k.StartTime.Time()
|
||||
inc.EmitUpdate(ewma)
|
||||
}
|
||||
|
||||
if len(inc.Values) != dataLen {
|
||||
// check error
|
||||
log.Warnf("%s EMA (%d) value length (%d) != kline window length (%d)", inc.Interval, inc.Window, len(inc.Values), dataLen)
|
||||
}
|
||||
|
||||
v1 := math.Floor(inc.Values[len(inc.Values)-1]*100.0) / 100.0
|
||||
v2 := math.Floor(CalculateKLinesEMA(allKLines, priceF, inc.Window)*100.0) / 100.0
|
||||
if v1 != v2 {
|
||||
log.Warnf("ACCUMULATED %s EMA (%d) %f != EMA %f", inc.Interval, inc.Window, v1, v2)
|
||||
}
|
||||
func (inc *EWMA) Bind(updater KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func CalculateKLinesEMA(allKLines []types.KLine, priceF KLinePriceMapper, window int) float64 {
|
||||
|
@ -125,17 +103,3 @@ func ewma(prices []float64, multiplier float64) float64 {
|
|||
|
||||
return prices[end]*multiplier + (1-multiplier)*ewma(prices[:end], multiplier)
|
||||
}
|
||||
|
||||
func (inc *EWMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *EWMA) Bind(updater KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
var _ types.SeriesExtend = &EWMA{}
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *EWMA) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *EWMA) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ type HULL struct {
|
|||
ma2 *EWMA
|
||||
result *EWMA
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *HULL) Update(value float64) {
|
||||
|
@ -55,7 +55,7 @@ func (inc *HULL) Length() int {
|
|||
var _ types.SeriesExtend = &HULL{}
|
||||
|
||||
// TODO: should we just ignore the possible overlapping?
|
||||
func (inc *HULL) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *HULL) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
doable := false
|
||||
if inc.ma1 == nil || inc.ma1.Length() == 0 {
|
||||
doable = true
|
||||
|
@ -76,7 +76,7 @@ func (inc *HULL) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *HULL) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *HULL) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *HULL) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func Test_HULL(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hull := HULL{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||
hull.calculateAndUpdate(tt.kLines)
|
||||
hull.CalculateAndUpdate(tt.kLines)
|
||||
last := hull.Last()
|
||||
assert.InDelta(t, tt.want, last, Delta)
|
||||
assert.InDelta(t, tt.next, hull.Index(1), Delta)
|
||||
|
|
27
pkg/indicator/inf.go
Normal file
27
pkg/indicator/inf.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package indicator
|
||||
|
||||
import "github.com/c9s/bbgo/pkg/types"
|
||||
|
||||
type KLineWindowUpdater interface {
|
||||
OnKLineWindowUpdate(func(interval types.Interval, window types.KLineWindow))
|
||||
}
|
||||
|
||||
type KLineClosedEmitter interface {
|
||||
OnKLineClosed(func(k types.KLine))
|
||||
}
|
||||
|
||||
// KLinePusher provides an interface for API user to push kline value to the indicator.
|
||||
// The indicator implements its own way to calculate the value from the given kline object.
|
||||
type KLinePusher interface {
|
||||
PushK(k types.KLine)
|
||||
}
|
||||
|
||||
// KLineLoader provides an interface for API user to load history klines to the indicator.
|
||||
// The indicator implements its own way to calculate the values from the given history kline array.
|
||||
type KLineLoader interface {
|
||||
LoadK(allKLines []types.KLine)
|
||||
}
|
||||
|
||||
type KLineCalculateUpdater interface {
|
||||
CalculateAndUpdate(allKLines []types.KLine)
|
||||
}
|
|
@ -22,11 +22,12 @@ type Line struct {
|
|||
Interval types.Interval
|
||||
}
|
||||
|
||||
func (l *Line) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
func (l *Line) handleKLineWindowUpdate(interval types.Interval, allKLines types.KLineWindow) {
|
||||
if interval != l.Interval {
|
||||
return
|
||||
}
|
||||
newTime := window.Last().EndTime.Time()
|
||||
|
||||
newTime := allKLines.Last().EndTime.Time()
|
||||
delta := int(newTime.Sub(l.currentTime).Minutes()) / l.Interval.Minutes()
|
||||
l.startIndex += delta
|
||||
l.endIndex += delta
|
||||
|
|
|
@ -11,6 +11,7 @@ macd implements moving average convergence divergence indicator
|
|||
|
||||
Moving Average Convergence Divergence (MACD)
|
||||
- https://www.investopedia.com/terms/m/macd.asp
|
||||
- https://school.stockcharts.com/doku.php?id=technical_indicators:macd-histogram
|
||||
*/
|
||||
|
||||
//go:generate callbackgen -type MACD
|
||||
|
@ -19,21 +20,21 @@ type MACD struct {
|
|||
ShortPeriod int // 12
|
||||
LongPeriod int // 26
|
||||
Values types.Float64Slice
|
||||
FastEWMA EWMA
|
||||
SlowEWMA EWMA
|
||||
SignalLine EWMA
|
||||
FastEWMA *EWMA
|
||||
SlowEWMA *EWMA
|
||||
SignalLine *EWMA
|
||||
Histogram types.Float64Slice
|
||||
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *MACD) Update(x float64) {
|
||||
if len(inc.Values) == 0 {
|
||||
inc.FastEWMA = EWMA{IntervalWindow: types.IntervalWindow{Window: inc.ShortPeriod}}
|
||||
inc.SlowEWMA = EWMA{IntervalWindow: types.IntervalWindow{Window: inc.LongPeriod}}
|
||||
inc.SignalLine = EWMA{IntervalWindow: types.IntervalWindow{Window: inc.Window}}
|
||||
inc.FastEWMA = &EWMA{IntervalWindow: types.IntervalWindow{Window: inc.ShortPeriod}}
|
||||
inc.SlowEWMA = &EWMA{IntervalWindow: types.IntervalWindow{Window: inc.LongPeriod}}
|
||||
inc.SignalLine = &EWMA{IntervalWindow: types.IntervalWindow{Window: inc.Window}}
|
||||
}
|
||||
|
||||
// update fast and slow ema
|
||||
|
@ -51,27 +52,38 @@ func (inc *MACD) Update(x float64) {
|
|||
inc.Histogram.Push(macd - inc.SignalLine.Last())
|
||||
}
|
||||
|
||||
func (inc *MACD) calculateMACD(kLines []types.KLine, priceF KLinePriceMapper) float64 {
|
||||
for _, kline := range kLines {
|
||||
inc.Update(kline.Close.Float64())
|
||||
func (inc *MACD) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *MACD) calculateAndUpdate(kLines []types.KLine) {
|
||||
if len(kLines) == 0 {
|
||||
func (inc *MACD) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *MACD) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if len(allKLines) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
last := allKLines[len(allKLines)-1]
|
||||
if len(inc.Values) == 0 {
|
||||
for _, k := range allKLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
|
||||
inc.PushK(k)
|
||||
}
|
||||
inc.Update(k.Close.Float64())
|
||||
} else {
|
||||
inc.PushK(last)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Values[len(inc.Values)-1])
|
||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||
inc.EmitUpdate(inc.Last())
|
||||
inc.EndTime = last.EndTime.Time()
|
||||
}
|
||||
|
||||
func (inc *MACD) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
|
@ -79,7 +91,7 @@ func (inc *MACD) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *MACD) Bind(updater KLineWindowUpdater) {
|
||||
|
@ -95,6 +107,7 @@ func (inc *MACDValues) Last() float64 {
|
|||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
|
@ -103,6 +116,7 @@ func (inc *MACDValues) Index(i int) float64 {
|
|||
if length == 0 || length-1-i < 0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
return inc.Values[length-1+i]
|
||||
}
|
||||
|
||||
|
@ -117,5 +131,5 @@ func (inc *MACD) MACD() types.SeriesExtend {
|
|||
}
|
||||
|
||||
func (inc *MACD) Singals() types.SeriesExtend {
|
||||
return &inc.SignalLine
|
||||
return inc.SignalLine
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *MACD) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *MACD) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,9 @@ func Test_calculateMACD(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
iw := types.IntervalWindow{Window: 9}
|
||||
macd := MACD{IntervalWindow: iw, ShortPeriod: 12, LongPeriod: 26}
|
||||
priceF := KLineClosePriceMapper
|
||||
got := macd.calculateMACD(tt.kLines, priceF)
|
||||
macd.CalculateAndUpdate(tt.kLines)
|
||||
|
||||
got := macd.Last()
|
||||
diff := math.Trunc((got-tt.want)*100) / 100
|
||||
if diff != 0 {
|
||||
t.Errorf("calculateMACD() = %v, want %v", got, tt.want)
|
||||
|
|
33
pkg/indicator/mapper.go
Normal file
33
pkg/indicator/mapper.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package indicator
|
||||
|
||||
import "github.com/c9s/bbgo/pkg/types"
|
||||
|
||||
type KLinePriceMapper func(k types.KLine) float64
|
||||
|
||||
func KLineOpenPriceMapper(k types.KLine) float64 {
|
||||
return k.Open.Float64()
|
||||
}
|
||||
|
||||
func KLineClosePriceMapper(k types.KLine) float64 {
|
||||
return k.Close.Float64()
|
||||
}
|
||||
|
||||
func KLineTypicalPriceMapper(k types.KLine) float64 {
|
||||
return (k.High.Float64() + k.Low.Float64() + k.Close.Float64()) / 3.
|
||||
}
|
||||
|
||||
func KLinePriceVolumeMapper(k types.KLine) float64 {
|
||||
return k.Close.Mul(k.Volume).Float64()
|
||||
}
|
||||
|
||||
func KLineVolumeMapper(k types.KLine) float64 {
|
||||
return k.Volume.Float64()
|
||||
}
|
||||
|
||||
func MapKLinePrice(kLines []types.KLine, f KLinePriceMapper) (prices []float64) {
|
||||
for _, k := range kLines {
|
||||
prices = append(prices, f(k))
|
||||
}
|
||||
|
||||
return prices
|
||||
}
|
|
@ -18,9 +18,9 @@ type OBV struct {
|
|||
types.IntervalWindow
|
||||
Values types.Float64Slice
|
||||
PrePrice float64
|
||||
EndTime time.Time
|
||||
|
||||
EndTime time.Time
|
||||
UpdateCallbacks []func(value float64)
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *OBV) Update(price, volume float64) {
|
||||
|
@ -54,13 +54,19 @@ func (inc *OBV) Index(i int) float64 {
|
|||
|
||||
var _ types.SeriesExtend = &OBV{}
|
||||
|
||||
func (inc *OBV) calculateAndUpdate(kLines []types.KLine) {
|
||||
func (inc *OBV) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64(), k.Volume.Float64())
|
||||
}
|
||||
|
||||
func (inc *OBV) CalculateAndUpdate(kLines []types.KLine) {
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
inc.Update(k.Close.Float64(), k.Volume.Float64())
|
||||
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||
}
|
||||
|
@ -70,7 +76,7 @@ func (inc *OBV) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *OBV) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *OBV) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *OBV) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ func Test_calculateOBV(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obv := OBV{IntervalWindow: types.IntervalWindow{Window: tt.window}}
|
||||
obv.calculateAndUpdate(tt.kLines)
|
||||
obv.CalculateAndUpdate(tt.kLines)
|
||||
assert.Equal(t, len(obv.Values), len(tt.want))
|
||||
for i, v := range obv.Values {
|
||||
assert.InDelta(t, v, tt.want[i], Delta)
|
||||
|
|
|
@ -38,7 +38,7 @@ func (inc *Pivot) LastHigh() float64 {
|
|||
return inc.Highs[len(inc.Highs)-1]
|
||||
}
|
||||
|
||||
func (inc *Pivot) Update(klines []types.KLine) {
|
||||
func (inc *Pivot) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ func (inc *Pivot) handleKLineWindowUpdate(interval types.Interval, window types.
|
|||
return
|
||||
}
|
||||
|
||||
inc.Update(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *Pivot) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -13,13 +13,16 @@ import (
|
|||
type RMA struct {
|
||||
types.SeriesBase
|
||||
types.IntervalWindow
|
||||
Values types.Float64Slice
|
||||
counter int
|
||||
Adjust bool
|
||||
tmp float64
|
||||
sum float64
|
||||
EndTime time.Time
|
||||
UpdateCallbacks []func(value float64)
|
||||
|
||||
Values types.Float64Slice
|
||||
EndTime time.Time
|
||||
|
||||
counter int
|
||||
Adjust bool
|
||||
tmp float64
|
||||
sum float64
|
||||
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *RMA) Update(x float64) {
|
||||
|
@ -64,23 +67,35 @@ func (inc *RMA) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &RMA{}
|
||||
|
||||
func (inc *RMA) calculateAndUpdate(kLines []types.KLine) {
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
func (inc *RMA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *RMA) CalculateAndUpdate(kLines []types.KLine) {
|
||||
last := kLines[len(kLines)-1]
|
||||
|
||||
if len(inc.Values) == 0 {
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
|
||||
inc.PushK(k)
|
||||
}
|
||||
inc.Update(k.Close.Float64())
|
||||
} else {
|
||||
inc.PushK(last)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||
inc.EndTime = last.EndTime.Time()
|
||||
}
|
||||
|
||||
func (inc *RMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *RMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *RMA) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *RMA) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ type RSI struct {
|
|||
PreviousAvgGain float64
|
||||
|
||||
EndTime time.Time
|
||||
UpdateCallbacks []func(value float64)
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *RSI) Update(price float64) {
|
||||
|
@ -80,12 +80,17 @@ func (inc *RSI) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &RSI{}
|
||||
|
||||
func (inc *RSI) calculateAndUpdate(kLines []types.KLine) {
|
||||
func (inc *RSI) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *RSI) CalculateAndUpdate(kLines []types.KLine) {
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
inc.Update(k.Close.Float64())
|
||||
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
|
@ -97,7 +102,7 @@ func (inc *RSI) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *RSI) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *RSI) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *RSI) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func Test_calculateRSI(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rsi := RSI{IntervalWindow: types.IntervalWindow{Window: tt.window}}
|
||||
rsi.calculateAndUpdate(tt.kLines)
|
||||
rsi.CalculateAndUpdate(tt.kLines)
|
||||
assert.Equal(t, len(rsi.Values), len(tt.want))
|
||||
for i, v := range rsi.Values {
|
||||
assert.InDelta(t, v, tt.want[i], Delta)
|
||||
|
|
|
@ -10,15 +10,13 @@ import (
|
|||
const MaxNumOfSMA = 5_000
|
||||
const MaxNumOfSMATruncateSize = 100
|
||||
|
||||
var zeroTime time.Time
|
||||
|
||||
//go:generate callbackgen -type SMA
|
||||
type SMA struct {
|
||||
types.SeriesBase
|
||||
types.IntervalWindow
|
||||
Values types.Float64Slice
|
||||
Cache *types.Queue
|
||||
EndTime time.Time
|
||||
Values types.Float64Slice
|
||||
rawValues *types.Queue
|
||||
EndTime time.Time
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
}
|
||||
|
@ -45,37 +43,39 @@ func (inc *SMA) Length() int {
|
|||
var _ types.SeriesExtend = &SMA{}
|
||||
|
||||
func (inc *SMA) Update(value float64) {
|
||||
if inc.Cache == nil {
|
||||
inc.Cache = types.NewQueue(inc.Window)
|
||||
if inc.rawValues == nil {
|
||||
inc.rawValues = types.NewQueue(inc.Window)
|
||||
inc.SeriesBase.Series = inc
|
||||
}
|
||||
inc.Cache.Update(value)
|
||||
if inc.Cache.Length() < inc.Window {
|
||||
|
||||
inc.rawValues.Update(value)
|
||||
if inc.rawValues.Length() < inc.Window {
|
||||
return
|
||||
}
|
||||
inc.Values.Push(types.Mean(inc.Cache))
|
||||
if inc.Values.Length() > MaxNumOfSMA {
|
||||
inc.Values = inc.Values[MaxNumOfSMATruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.Values.Push(types.Mean(inc.rawValues))
|
||||
}
|
||||
|
||||
func (inc *SMA) calculateAndUpdate(kLines []types.KLine) {
|
||||
var index = len(kLines) - 1
|
||||
var kline = kLines[index]
|
||||
if inc.EndTime != zeroTime && kline.EndTime.Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
if inc.Cache == nil {
|
||||
for _, k := range kLines {
|
||||
inc.Update(KLineClosePriceMapper(k))
|
||||
inc.EndTime = k.EndTime.Time()
|
||||
inc.EmitUpdate(inc.Values.Last())
|
||||
func (inc *SMA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.EndTime = k.EndTime.Time()
|
||||
}
|
||||
|
||||
func (inc *SMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
var last = allKLines[len(allKLines)-1]
|
||||
|
||||
if inc.rawValues == nil {
|
||||
for _, k := range allKLines {
|
||||
if inc.EndTime != zeroTime && k.EndTime.Before(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
inc.PushK(k)
|
||||
}
|
||||
} else {
|
||||
inc.Update(KLineClosePriceMapper(kline))
|
||||
inc.EndTime = kline.EndTime.Time()
|
||||
inc.EmitUpdate(inc.Values.Last())
|
||||
inc.PushK(last)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Values.Last())
|
||||
}
|
||||
|
||||
func (inc *SMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
|
@ -83,7 +83,7 @@ func (inc *SMA) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *SMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -52,7 +52,7 @@ func Test_SMA(t *testing.T) {
|
|||
sma := SMA{
|
||||
IntervalWindow: types.IntervalWindow{Window: 5},
|
||||
}
|
||||
sma.calculateAndUpdate(tt.kLines)
|
||||
sma.CalculateAndUpdate(tt.kLines)
|
||||
assert.InDelta(t, tt.want, sma.Last(), Delta)
|
||||
assert.InDelta(t, tt.next, sma.Index(1), Delta)
|
||||
sma.Update(tt.update)
|
||||
|
|
|
@ -93,14 +93,19 @@ func (inc *SSF) Last() float64 {
|
|||
|
||||
var _ types.SeriesExtend = &SSF{}
|
||||
|
||||
func (inc *SSF) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *SSF) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *SSF) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.Values != nil {
|
||||
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
return
|
||||
}
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +114,7 @@ func (inc *SSF) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *SSF) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -62,7 +62,7 @@ func Test_SSF(t *testing.T) {
|
|||
IntervalWindow: types.IntervalWindow{Window: 5},
|
||||
Poles: tt.poles,
|
||||
}
|
||||
ssf.calculateAndUpdate(tt.kLines)
|
||||
ssf.CalculateAndUpdate(tt.kLines)
|
||||
assert.InDelta(t, tt.want, ssf.Last(), Delta)
|
||||
assert.InDelta(t, tt.next, ssf.Index(1), Delta)
|
||||
assert.Equal(t, tt.all, ssf.Length())
|
||||
|
|
|
@ -59,7 +59,11 @@ func (inc *STOCH) LastD() float64 {
|
|||
return inc.D[len(inc.D)-1]
|
||||
}
|
||||
|
||||
func (inc *STOCH) calculateAndUpdate(kLines []types.KLine) {
|
||||
func (inc *STOCH) PushK(k types.KLine) {
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *STOCH) CalculateAndUpdate(kLines []types.KLine) {
|
||||
if len(kLines) < inc.Window || len(kLines) < DPeriod {
|
||||
return
|
||||
}
|
||||
|
@ -68,7 +72,8 @@ func (inc *STOCH) calculateAndUpdate(kLines []types.KLine) {
|
|||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.LastK(), inc.LastD())
|
||||
|
@ -80,7 +85,7 @@ func (inc *STOCH) handleKLineWindowUpdate(interval types.Interval, window types.
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *STOCH) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -56,7 +56,7 @@ func TestSTOCH_update(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
kd := STOCH{IntervalWindow: types.IntervalWindow{Window: tt.window}}
|
||||
kd.calculateAndUpdate(tt.kLines)
|
||||
kd.CalculateAndUpdate(tt.kLines)
|
||||
|
||||
got_k := kd.LastK()
|
||||
diff_k := math.Trunc((got_k-tt.want_k)*100) / 100
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package indicator
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -50,6 +51,7 @@ func (inc *Supertrend) Index(i int) float64 {
|
|||
func (inc *Supertrend) Length() int {
|
||||
return len(inc.trendPrices)
|
||||
}
|
||||
|
||||
func (inc *Supertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||
if inc.Window <= 0 {
|
||||
panic("window must be greater than 0")
|
||||
|
@ -127,12 +129,17 @@ func (inc *Supertrend) GetSignal() types.Direction {
|
|||
|
||||
var _ types.SeriesExtend = &Supertrend{}
|
||||
|
||||
func (inc *Supertrend) calculateAndUpdate(kLines []types.KLine) {
|
||||
func (inc *Supertrend) PushK(k types.KLine) {
|
||||
inc.Update(k.GetHigh().Float64(), k.GetLow().Float64(), k.GetClose().Float64())
|
||||
}
|
||||
|
||||
func (inc *Supertrend) CalculateAndUpdate(kLines []types.KLine) {
|
||||
for _, k := range kLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
inc.Update(k.GetHigh().Float64(), k.GetLow().Float64(), k.GetClose().Float64())
|
||||
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
|
@ -144,7 +151,7 @@ func (inc *Supertrend) handleKLineWindowUpdate(interval types.Interval, window t
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *Supertrend) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -55,14 +55,19 @@ func (inc *TEMA) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &TEMA{}
|
||||
|
||||
func (inc *TEMA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *TEMA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *TEMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.A1 == nil {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
} else {
|
||||
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +77,7 @@ func (inc *TEMA) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *TEMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -46,7 +46,7 @@ func Test_TEMA(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tema := TEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||
tema.calculateAndUpdate(tt.kLines)
|
||||
tema.CalculateAndUpdate(tt.kLines)
|
||||
last := tema.Last()
|
||||
assert.InDelta(t, tt.want, last, Delta)
|
||||
assert.InDelta(t, tt.next, tema.Index(1), Delta)
|
||||
|
|
|
@ -12,18 +12,19 @@ const defaultVolumeFactor = 0.7
|
|||
type TILL struct {
|
||||
types.SeriesBase
|
||||
types.IntervalWindow
|
||||
VolumeFactor float64
|
||||
e1 *EWMA
|
||||
e2 *EWMA
|
||||
e3 *EWMA
|
||||
e4 *EWMA
|
||||
e5 *EWMA
|
||||
e6 *EWMA
|
||||
c1 float64
|
||||
c2 float64
|
||||
c3 float64
|
||||
c4 float64
|
||||
UpdateCallbacks []func(value float64)
|
||||
VolumeFactor float64
|
||||
e1 *EWMA
|
||||
e2 *EWMA
|
||||
e3 *EWMA
|
||||
e4 *EWMA
|
||||
e5 *EWMA
|
||||
e6 *EWMA
|
||||
c1 float64
|
||||
c2 float64
|
||||
c3 float64
|
||||
c4 float64
|
||||
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *TILL) Update(value float64) {
|
||||
|
@ -85,7 +86,11 @@ func (inc *TILL) Length() int {
|
|||
|
||||
var _ types.Series = &TILL{}
|
||||
|
||||
func (inc *TILL) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *TILL) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *TILL) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
doable := false
|
||||
if inc.e1 == nil {
|
||||
doable = true
|
||||
|
@ -94,8 +99,9 @@ func (inc *TILL) calculateAndUpdate(allKLines []types.KLine) {
|
|||
if !doable && k.StartTime.After(inc.e1.LastOpenTime) {
|
||||
doable = true
|
||||
}
|
||||
|
||||
if doable {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +112,7 @@ func (inc *TILL) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *TILL) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *TILL) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *TILL) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,10 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -55,7 +56,7 @@ func Test_TILL(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
till := TILL{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||
till.calculateAndUpdate(tt.kLines)
|
||||
till.CalculateAndUpdate(tt.kLines)
|
||||
last := till.Last()
|
||||
assert.InDelta(t, tt.want, last, Delta)
|
||||
assert.InDelta(t, tt.next, till.Index(1), Delta)
|
||||
|
|
|
@ -50,14 +50,19 @@ func (inc *TMA) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &TMA{}
|
||||
|
||||
func (inc *TMA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *TMA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *TMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.s1 == nil {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
} else {
|
||||
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +72,7 @@ func (inc *TMA) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *TMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -1,29 +1,2 @@
|
|||
package indicator
|
||||
|
||||
import "github.com/c9s/bbgo/pkg/types"
|
||||
|
||||
type KLinePriceMapper func(k types.KLine) float64
|
||||
|
||||
func KLineOpenPriceMapper(k types.KLine) float64 {
|
||||
return k.Open.Float64()
|
||||
}
|
||||
|
||||
func KLineClosePriceMapper(k types.KLine) float64 {
|
||||
return k.Close.Float64()
|
||||
}
|
||||
|
||||
func KLineTypicalPriceMapper(k types.KLine) float64 {
|
||||
return (k.High.Float64() + k.Low.Float64() + k.Close.Float64()) / 3.
|
||||
}
|
||||
|
||||
func MapKLinePrice(kLines []types.KLine, f KLinePriceMapper) (prices []float64) {
|
||||
for _, k := range kLines {
|
||||
prices = append(prices, f(k))
|
||||
}
|
||||
|
||||
return prices
|
||||
}
|
||||
|
||||
type KLineWindowUpdater interface {
|
||||
OnKLineWindowUpdate(func(interval types.Interval, window types.KLineWindow))
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ type VIDYA struct {
|
|||
Values types.Float64Slice
|
||||
input types.Float64Slice
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *VIDYA) Update(value float64) {
|
||||
|
@ -70,14 +70,19 @@ func (inc *VIDYA) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &VIDYA{}
|
||||
|
||||
func (inc *VIDYA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *VIDYA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *VIDYA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.input.Length() == 0 {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
} else {
|
||||
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +92,7 @@ func (inc *VIDYA) handleKLineWindowUpdate(interval types.Interval, window types.
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *VIDYA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *VIDYA) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *VIDYA) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ import (
|
|||
const MaxNumOfVOL = 5_000
|
||||
const MaxNumOfVOLTruncateSize = 100
|
||||
|
||||
//var zeroTime time.Time
|
||||
// var zeroTime time.Time
|
||||
|
||||
//go:generate callbackgen -type VOLATILITY
|
||||
type VOLATILITY struct {
|
||||
//go:generate callbackgen -type Volatility
|
||||
type Volatility struct {
|
||||
types.SeriesBase
|
||||
types.IntervalWindow
|
||||
Values types.Float64Slice
|
||||
|
@ -25,42 +25,43 @@ type VOLATILITY struct {
|
|||
UpdateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *VOLATILITY) Last() float64 {
|
||||
func (inc *Volatility) Last() float64 {
|
||||
if len(inc.Values) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *VOLATILITY) Index(i int) float64 {
|
||||
func (inc *Volatility) Index(i int) float64 {
|
||||
if len(inc.Values)-i <= 0 {
|
||||
return 0.0
|
||||
}
|
||||
return inc.Values[len(inc.Values)-i-1]
|
||||
}
|
||||
|
||||
func (inc *VOLATILITY) Length() int {
|
||||
func (inc *Volatility) Length() int {
|
||||
return len(inc.Values)
|
||||
}
|
||||
|
||||
var _ types.SeriesExtend = &VOLATILITY{}
|
||||
var _ types.SeriesExtend = &Volatility{}
|
||||
|
||||
func (inc *VOLATILITY) calculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
func (inc *Volatility) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if len(allKLines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var end = len(klines) - 1
|
||||
var lastKLine = klines[end]
|
||||
var end = len(allKLines) - 1
|
||||
var lastKLine = allKLines[end]
|
||||
|
||||
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(inc.Values) == 0 {
|
||||
inc.SeriesBase.Series = inc
|
||||
}
|
||||
|
||||
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||
var recentT = allKLines[end-(inc.Window-1) : end+1]
|
||||
|
||||
volatility, err := calculateVOLATILITY(recentT, inc.Window, KLineClosePriceMapper)
|
||||
if err != nil {
|
||||
|
@ -73,20 +74,20 @@ func (inc *VOLATILITY) calculateAndUpdate(klines []types.KLine) {
|
|||
inc.Values = inc.Values[MaxNumOfVOLTruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = klines[end].GetEndTime().Time()
|
||||
inc.EndTime = allKLines[end].GetEndTime().Time()
|
||||
|
||||
inc.EmitUpdate(volatility)
|
||||
}
|
||||
|
||||
func (inc *VOLATILITY) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
func (inc *Volatility) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if inc.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *VOLATILITY) Bind(updater KLineWindowUpdater) {
|
||||
func (inc *Volatility) Bind(updater KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// Code generated by "callbackgen -type VOLATILITY"; DO NOT EDIT.
|
||||
// Code generated by "callbackgen -type Volatility"; DO NOT EDIT.
|
||||
|
||||
package indicator
|
||||
|
||||
import ()
|
||||
|
||||
func (inc *VOLATILITY) OnUpdate(cb func(value float64)) {
|
||||
func (inc *Volatility) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *VOLATILITY) EmitUpdate(value float64) {
|
||||
func (inc *Volatility) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
|
|
|
@ -71,18 +71,21 @@ func (inc *VWAP) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &VWAP{}
|
||||
|
||||
func (inc *VWAP) calculateAndUpdate(kLines []types.KLine) {
|
||||
var priceF = KLineTypicalPriceMapper
|
||||
func (inc *VWAP) PushK(k types.KLine) {
|
||||
inc.Update(KLineTypicalPriceMapper(k), k.Volume.Float64())
|
||||
}
|
||||
|
||||
for _, k := range kLines {
|
||||
func (inc *VWAP) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
for _, k := range allKLines {
|
||||
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
|
||||
continue
|
||||
}
|
||||
inc.Update(priceF(k), k.Volume.Float64())
|
||||
|
||||
inc.PushK(k)
|
||||
}
|
||||
|
||||
inc.EmitUpdate(inc.Last())
|
||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||
inc.EndTime = allKLines[len(allKLines)-1].EndTime.Time()
|
||||
}
|
||||
|
||||
func (inc *VWAP) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
|
@ -90,14 +93,14 @@ func (inc *VWAP) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *VWAP) Bind(updater KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
func CalculateVWAP(klines []types.KLine, priceF KLinePriceMapper, window int) float64 {
|
||||
func calculateVWAP(klines []types.KLine, priceF KLinePriceMapper, window int) float64 {
|
||||
vwap := VWAP{IntervalWindow: types.IntervalWindow{Window: window}}
|
||||
for _, k := range klines {
|
||||
vwap.Update(priceF(k), k.Volume.Float64())
|
||||
|
|
|
@ -4,12 +4,12 @@ package indicator
|
|||
|
||||
import ()
|
||||
|
||||
func (V *VWAP) OnUpdate(cb func(value float64)) {
|
||||
V.UpdateCallbacks = append(V.UpdateCallbacks, cb)
|
||||
func (inc *VWAP) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (V *VWAP) EmitUpdate(value float64) {
|
||||
for _, cb := range V.UpdateCallbacks {
|
||||
func (inc *VWAP) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ func Test_calculateVWAP(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
priceF := KLineTypicalPriceMapper
|
||||
got := CalculateVWAP(tt.kLines, priceF, tt.window)
|
||||
got := calculateVWAP(tt.kLines, priceF, tt.window)
|
||||
diff := math.Trunc((got-tt.want)*100) / 100
|
||||
if diff != 0 {
|
||||
t.Errorf("calculateVWAP() = %v, want %v", got, tt.want)
|
||||
|
|
|
@ -49,27 +49,20 @@ func (inc *VWMA) Length() int {
|
|||
|
||||
var _ types.SeriesExtend = &VWMA{}
|
||||
|
||||
func KLinePriceVolumeMapper(k types.KLine) float64 {
|
||||
return k.Close.Mul(k.Volume).Float64()
|
||||
}
|
||||
|
||||
func KLineVolumeMapper(k types.KLine) float64 {
|
||||
return k.Volume.Float64()
|
||||
}
|
||||
|
||||
func (inc *VWMA) calculateAndUpdate(kLines []types.KLine) {
|
||||
if len(kLines) < inc.Window {
|
||||
func (inc *VWMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if len(allKLines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
||||
var index = len(kLines) - 1
|
||||
var kline = kLines[index]
|
||||
var index = len(allKLines) - 1
|
||||
var kline = allKLines[index]
|
||||
|
||||
if inc.EndTime != zeroTime && kline.EndTime.Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
var recentK = kLines[index-(inc.Window-1) : index+1]
|
||||
var recentK = allKLines[index-(inc.Window-1) : index+1]
|
||||
|
||||
pv, err := calculateSMA(recentK, inc.Window, KLinePriceVolumeMapper)
|
||||
if err != nil {
|
||||
|
@ -93,7 +86,7 @@ func (inc *VWMA) calculateAndUpdate(kLines []types.KLine) {
|
|||
inc.Values = inc.Values[MaxNumOfSMATruncateSize-1:]
|
||||
}
|
||||
|
||||
inc.EndTime = kLines[index].EndTime.Time()
|
||||
inc.EndTime = allKLines[index].EndTime.Time()
|
||||
|
||||
inc.EmitUpdate(vwma)
|
||||
}
|
||||
|
@ -103,7 +96,7 @@ func (inc *VWMA) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *VWMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package indicator
|
||||
|
||||
import (
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
// Refer: Welles Wilder's Moving Average
|
||||
|
@ -56,7 +57,11 @@ func (inc *WWMA) Length() int {
|
|||
return len(inc.Values)
|
||||
}
|
||||
|
||||
func (inc *WWMA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *WWMA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *WWMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if len(allKLines) < inc.Window {
|
||||
// we can't calculate
|
||||
return
|
||||
|
@ -68,7 +73,7 @@ func (inc *WWMA) calculateAndUpdate(allKLines []types.KLine) {
|
|||
doable = true
|
||||
}
|
||||
if doable {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.LastOpenTime = k.StartTime.Time()
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
|
@ -80,7 +85,7 @@ func (inc *WWMA) handleKLineWindowUpdate(interval types.Interval, window types.K
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *WWMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -16,7 +16,7 @@ type ZLEMA struct {
|
|||
zlema *EWMA
|
||||
lag int
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
updateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *ZLEMA) Index(i int) float64 {
|
||||
|
@ -59,14 +59,19 @@ func (inc *ZLEMA) Update(value float64) {
|
|||
|
||||
var _ types.SeriesExtend = &ZLEMA{}
|
||||
|
||||
func (inc *ZLEMA) calculateAndUpdate(allKLines []types.KLine) {
|
||||
func (inc *ZLEMA) PushK(k types.KLine) {
|
||||
inc.Update(k.Close.Float64())
|
||||
}
|
||||
|
||||
func (inc *ZLEMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||
if inc.zlema == nil {
|
||||
for _, k := range allKLines {
|
||||
inc.Update(k.Close.Float64())
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
} else {
|
||||
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
||||
k := allKLines[len(allKLines)-1]
|
||||
inc.PushK(k)
|
||||
inc.EmitUpdate(inc.Last())
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +81,7 @@ func (inc *ZLEMA) handleKLineWindowUpdate(interval types.Interval, window types.
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *ZLEMA) Bind(updater KLineWindowUpdater) {
|
||||
|
|
|
@ -5,11 +5,11 @@ package indicator
|
|||
import ()
|
||||
|
||||
func (inc *ZLEMA) OnUpdate(cb func(value float64)) {
|
||||
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||
inc.updateCallbacks = append(inc.updateCallbacks, cb)
|
||||
}
|
||||
|
||||
func (inc *ZLEMA) EmitUpdate(value float64) {
|
||||
for _, cb := range inc.UpdateCallbacks {
|
||||
for _, cb := range inc.updateCallbacks {
|
||||
cb(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func Test_ZLEMA(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
zlema := ZLEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||
zlema.calculateAndUpdate(tt.kLines)
|
||||
zlema.CalculateAndUpdate(tt.kLines)
|
||||
last := zlema.Last()
|
||||
assert.InDelta(t, tt.want, last, Delta)
|
||||
assert.InDelta(t, tt.next, zlema.Index(1), Delta)
|
||||
|
|
|
@ -29,7 +29,7 @@ func (inc *Correlation) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *Correlation) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *Correlation) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ func (inc *Correlation) handleKLineWindowUpdate(interval types.Interval, window
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *Correlation) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *A18) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *A18) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *A18) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *A18) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *A18) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *A2) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *A2) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *A2) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *A2) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *A2) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -27,7 +27,7 @@ func (inc *A3) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *A3) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *A3) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func (inc *A3) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *A3) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *A34) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *A34) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *A34) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *A34) handleKLineWindowUpdate(interval types.Interval, window types.KL
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *A34) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -30,7 +30,7 @@ func (inc *R) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *R) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *R) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func (inc *R) handleKLineWindowUpdate(interval types.Interval, window types.KLin
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *R) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *S0) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S0) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *S0) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *S0) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S0) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -25,7 +25,7 @@ func (inc *S1) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S1) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *S1) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func (inc *S1) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S1) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -25,7 +25,7 @@ func (inc *S2) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S2) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *S2) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func (inc *S2) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S2) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *S3) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S3) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *S3) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *S3) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S3) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *S4) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S4) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *S4) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *S4) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S4) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *S5) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S5) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *S5) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *S5) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S5) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *S6) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S6) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *S6) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *S6) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S6) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -26,7 +26,7 @@ func (inc *S7) Last() float64 {
|
|||
return inc.Values[len(inc.Values)-1]
|
||||
}
|
||||
|
||||
func (inc *S7) calculateAndUpdate(klines []types.KLine) {
|
||||
func (inc *S7) CalculateAndUpdate(klines []types.KLine) {
|
||||
if len(klines) < inc.Window {
|
||||
return
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func (inc *S7) handleKLineWindowUpdate(interval types.Interval, window types.KLi
|
|||
return
|
||||
}
|
||||
|
||||
inc.calculateAndUpdate(window)
|
||||
inc.CalculateAndUpdate(window)
|
||||
}
|
||||
|
||||
func (inc *S7) Bind(updater indicator.KLineWindowUpdater) {
|
||||
|
|
|
@ -296,7 +296,7 @@ func preloadPivot(pivot *indicator.Pivot, store *bbgo.MarketDataStore) *types.KL
|
|||
log.Debugf("updating pivot indicator: %d klines", len(*klines))
|
||||
|
||||
for i := pivot.Window; i < len(*klines); i++ {
|
||||
pivot.Update((*klines)[0 : i+1])
|
||||
pivot.CalculateAndUpdate((*klines)[0 : i+1])
|
||||
}
|
||||
|
||||
log.Debugf("found %v previous lows: %v", pivot.IntervalWindow, pivot.Lows)
|
||||
|
|
|
@ -693,7 +693,9 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
|
|||
|
||||
if store, ok := s.sourceSession.MarketDataStore(s.Symbol); ok {
|
||||
if klines, ok2 := store.KLinesOfInterval(s.BollBandInterval); ok2 {
|
||||
s.boll.Update(*klines)
|
||||
for i := 0; i < len(*klines); i++ {
|
||||
s.boll.CalculateAndUpdate((*klines)[0 : i+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user