2020-10-29 09:51:20 +00:00
|
|
|
package indicator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
2020-11-30 05:06:35 +00:00
|
|
|
boll implements the bollinger indicator:
|
2020-10-29 09:51:20 +00:00
|
|
|
|
|
|
|
The Basics of Bollinger Bands
|
|
|
|
- https://www.investopedia.com/articles/technical/102201.asp
|
|
|
|
|
|
|
|
Bollinger Bands
|
|
|
|
- https://www.investopedia.com/terms/b/bollingerbands.asp
|
|
|
|
|
|
|
|
Bollinger Bands Technical indicator guide:
|
|
|
|
- https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/bollinger-bands
|
|
|
|
*/
|
2020-10-29 11:09:45 +00:00
|
|
|
|
|
|
|
//go:generate callbackgen -type BOLL
|
2020-10-29 09:51:20 +00:00
|
|
|
type BOLL struct {
|
2022-07-14 04:51:27 +00:00
|
|
|
types.SeriesBase
|
2020-10-29 09:51:20 +00:00
|
|
|
types.IntervalWindow
|
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
// K is the multiplier of Std, generally it's 2
|
2020-10-29 09:51:20 +00:00
|
|
|
K float64
|
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
SMA *SMA
|
|
|
|
StdDev *StdDev
|
|
|
|
|
2021-05-22 12:20:48 +00:00
|
|
|
UpBand types.Float64Slice
|
|
|
|
DownBand types.Float64Slice
|
2020-10-29 09:51:20 +00:00
|
|
|
|
|
|
|
EndTime time.Time
|
2020-10-29 11:09:45 +00:00
|
|
|
|
|
|
|
updateCallbacks []func(sma, upBand, downBand float64)
|
|
|
|
}
|
|
|
|
|
2022-03-31 10:03:26 +00:00
|
|
|
type BandType int
|
|
|
|
|
2022-06-29 12:49:02 +00:00
|
|
|
func (inc *BOLL) GetUpBand() types.SeriesExtend {
|
|
|
|
return types.NewSeries(&inc.UpBand)
|
2022-03-31 10:03:26 +00:00
|
|
|
}
|
|
|
|
|
2022-06-29 12:49:02 +00:00
|
|
|
func (inc *BOLL) GetDownBand() types.SeriesExtend {
|
|
|
|
return types.NewSeries(&inc.DownBand)
|
2022-03-31 10:03:26 +00:00
|
|
|
}
|
|
|
|
|
2022-06-29 12:49:02 +00:00
|
|
|
func (inc *BOLL) GetSMA() types.SeriesExtend {
|
2022-07-14 04:51:27 +00:00
|
|
|
return types.NewSeries(inc.SMA)
|
2022-03-31 10:03:26 +00:00
|
|
|
}
|
|
|
|
|
2022-06-29 12:49:02 +00:00
|
|
|
func (inc *BOLL) GetStdDev() types.SeriesExtend {
|
2022-07-14 05:05:44 +00:00
|
|
|
return inc.StdDev
|
2022-03-31 10:03:26 +00:00
|
|
|
}
|
|
|
|
|
2020-10-29 11:09:45 +00:00
|
|
|
func (inc *BOLL) LastUpBand() float64 {
|
2020-10-31 12:36:58 +00:00
|
|
|
if len(inc.UpBand) == 0 {
|
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
2020-10-29 11:09:45 +00:00
|
|
|
return inc.UpBand[len(inc.UpBand)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (inc *BOLL) LastDownBand() float64 {
|
2020-10-31 12:36:58 +00:00
|
|
|
if len(inc.DownBand) == 0 {
|
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
2020-10-29 11:09:45 +00:00
|
|
|
return inc.DownBand[len(inc.DownBand)-1]
|
2020-10-29 09:51:20 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
func (inc *BOLL) Update(value float64) {
|
|
|
|
if inc.SMA == nil {
|
|
|
|
inc.SeriesBase.Series = inc
|
|
|
|
inc.SMA = &SMA{IntervalWindow: inc.IntervalWindow}
|
2020-10-29 09:51:20 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
if inc.StdDev == nil {
|
|
|
|
inc.StdDev = &StdDev{IntervalWindow: inc.IntervalWindow}
|
2020-12-05 05:04:32 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
inc.SMA.Update(value)
|
|
|
|
inc.StdDev.Update(value)
|
2020-10-29 09:51:20 +00:00
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
var sma = inc.SMA.Last()
|
|
|
|
var stdDev = inc.StdDev.Last()
|
|
|
|
var band = inc.K * stdDev
|
2020-10-29 11:47:53 +00:00
|
|
|
|
|
|
|
var upBand = sma + band
|
|
|
|
var downBand = sma - band
|
2022-07-14 04:51:27 +00:00
|
|
|
|
|
|
|
inc.UpBand.Push(upBand)
|
2020-10-29 09:51:20 +00:00
|
|
|
inc.DownBand.Push(downBand)
|
2022-07-14 04:51:27 +00:00
|
|
|
}
|
2020-10-29 09:51:20 +00:00
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
func (inc *BOLL) PushK(k types.KLine) {
|
|
|
|
inc.Update(k.Close.Float64())
|
|
|
|
}
|
2020-10-29 11:47:53 +00:00
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
func (inc *BOLL) CalculateAndUpdate(allKLines []types.KLine) {
|
|
|
|
var last = allKLines[len(allKLines)-1]
|
|
|
|
|
|
|
|
if inc.SMA == nil {
|
|
|
|
for _, k := range allKLines {
|
|
|
|
if inc.EndTime != zeroTime && k.EndTime.Before(inc.EndTime) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
inc.PushK(k)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
inc.PushK(last)
|
|
|
|
}
|
2020-10-31 12:36:58 +00:00
|
|
|
|
2022-07-14 04:51:27 +00:00
|
|
|
inc.EmitUpdate(inc.SMA.Last(), inc.UpBand.Last(), inc.DownBand.Last())
|
2020-10-29 09:51:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (inc *BOLL) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
|
|
|
if inc.Interval != interval {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if inc.EndTime != zeroTime && inc.EndTime.Before(inc.EndTime) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-13 16:41:20 +00:00
|
|
|
inc.CalculateAndUpdate(window)
|
2020-10-29 09:51:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (inc *BOLL) Bind(updater KLineWindowUpdater) {
|
|
|
|
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
|
|
|
}
|