-
-
Notifications
You must be signed in to change notification settings - Fork 301
/
boll.go
143 lines (106 loc) · 2.93 KB
/
boll.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package indicator
import (
"time"
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
)
/*
boll implements the bollinger indicator:
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
*/
//go:generate callbackgen -type BOLL
type BOLL struct {
types.IntervalWindow
// K is the multiplier of Std, generally it's 2
K float64
SMA *SMA
StdDev *StdDev
UpBand floats.Slice
DownBand floats.Slice
EndTime time.Time
updateCallbacks []func(sma, upBand, downBand float64)
}
type BandType int
func (inc *BOLL) GetUpBand() types.SeriesExtend {
return types.NewSeries(&inc.UpBand)
}
func (inc *BOLL) GetDownBand() types.SeriesExtend {
return types.NewSeries(&inc.DownBand)
}
func (inc *BOLL) GetSMA() types.SeriesExtend {
return types.NewSeries(inc.SMA)
}
func (inc *BOLL) GetStdDev() types.SeriesExtend {
return inc.StdDev
}
func (inc *BOLL) LastUpBand() float64 {
if len(inc.UpBand) == 0 {
return 0.0
}
return inc.UpBand[len(inc.UpBand)-1]
}
func (inc *BOLL) LastDownBand() float64 {
if len(inc.DownBand) == 0 {
return 0.0
}
return inc.DownBand[len(inc.DownBand)-1]
}
func (inc *BOLL) Update(value float64) {
if inc.SMA == nil {
inc.SMA = &SMA{IntervalWindow: inc.IntervalWindow}
}
if inc.StdDev == nil {
inc.StdDev = &StdDev{IntervalWindow: inc.IntervalWindow}
}
inc.SMA.Update(value)
inc.StdDev.Update(value)
var sma = inc.SMA.Last(0)
var stdDev = inc.StdDev.Last(0)
var band = inc.K * stdDev
var upBand = sma + band
var downBand = sma - band
inc.UpBand.Push(upBand)
inc.DownBand.Push(downBand)
}
func (inc *BOLL) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
target.OnKLineClosed(types.KLineWith(symbol, interval, inc.PushK))
}
func (inc *BOLL) PushK(k types.KLine) {
if inc.EndTime != zeroTime && k.EndTime.Before(inc.EndTime) {
return
}
inc.Update(k.Close.Float64())
inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.SMA.Last(0), inc.UpBand.Last(0), inc.DownBand.Last(0))
}
func (inc *BOLL) LoadK(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
}
inc.EmitUpdate(inc.SMA.Last(0), inc.UpBand.Last(0), inc.DownBand.Last(0))
}
func (inc *BOLL) CalculateAndUpdate(allKLines []types.KLine) {
if inc.SMA == nil {
inc.LoadK(allKLines)
return
}
var last = allKLines[len(allKLines)-1]
inc.PushK(last)
}
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
}
inc.CalculateAndUpdate(window)
}
func (inc *BOLL) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
}