-
-
Notifications
You must be signed in to change notification settings - Fork 301
/
Copy pathsupertrendPivot.go
213 lines (170 loc) · 6.17 KB
/
supertrendPivot.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package indicator
import (
"math"
"time"
"github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
)
// based on "Pivot Point Supertrend by LonesomeTheBlue" from tradingview
var logpst = logrus.WithField("indicator", "pivotSupertrend")
//go:generate callbackgen -type PivotSupertrend
type PivotSupertrend struct {
types.SeriesBase
types.IntervalWindow
ATRMultiplier float64 `json:"atrMultiplier"`
PivotWindow int `json:"pivotWindow"`
AverageTrueRange *ATR // Value must be set when initialized in strategy
PivotLow *PivotLow // Value must be set when initialized in strategy
PivotHigh *PivotHigh // Value must be set when initialized in strategy
trendPrices floats.Slice // Tsl: value of the trend line (buy or sell)
supportLine floats.Slice // The support line in an uptrend (green)
resistanceLine floats.Slice // The resistance line in a downtrend (red)
closePrice float64
previousClosePrice float64
uptrendPrice float64
previousUptrendPrice float64
downtrendPrice float64
previousDowntrendPrice float64
lastPp float64
src float64 // center
previousPivotHigh float64 // temp variable to save the last value
previousPivotLow float64 // temp variable to save the last value
trend types.Direction
previousTrend types.Direction
tradeSignal types.Direction
EndTime time.Time
UpdateCallbacks []func(value float64)
}
func (inc *PivotSupertrend) Last(i int) float64 {
return inc.trendPrices.Last(i)
}
func (inc *PivotSupertrend) Index(i int) float64 {
return inc.Last(i)
}
func (inc *PivotSupertrend) Length() int {
return len(inc.trendPrices)
}
func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
if inc.Window <= 0 {
panic("window must be greater than 0")
}
if inc.AverageTrueRange == nil {
inc.SeriesBase.Series = inc
}
// Start with DirectionUp
if inc.trend != types.DirectionUp && inc.trend != types.DirectionDown {
inc.trend = types.DirectionUp
}
inc.previousPivotLow = inc.PivotLow.Last(0)
inc.previousPivotHigh = inc.PivotHigh.Last(0)
// Update High / Low pivots
inc.PivotLow.Update(lowPrice)
inc.PivotHigh.Update(highPrice)
// Update ATR
inc.AverageTrueRange.Update(highPrice, lowPrice, closePrice)
// Update last prices
inc.previousUptrendPrice = inc.uptrendPrice
inc.previousDowntrendPrice = inc.downtrendPrice
inc.previousClosePrice = inc.closePrice
inc.previousTrend = inc.trend
inc.closePrice = closePrice
// Initialize lastPp as soon as pivots are made
if inc.lastPp == 0 || math.IsNaN(inc.lastPp) {
if inc.PivotHigh.Length() > 0 {
inc.lastPp = inc.PivotHigh.Last(0)
} else if inc.PivotLow.Length() > 0 {
inc.lastPp = inc.PivotLow.Last(0)
} else {
inc.lastPp = math.NaN()
return
}
}
// Set lastPp to the latest pivotPoint (only changed when new pivot is found)
if inc.PivotHigh.Last(0) != inc.previousPivotHigh {
inc.lastPp = inc.PivotHigh.Last(0)
} else if inc.PivotLow.Last(0) != inc.previousPivotLow {
inc.lastPp = inc.PivotLow.Last(0)
}
// calculate the Center line using pivot points
if inc.src == 0 || math.IsNaN(inc.src) {
inc.src = inc.lastPp
} else {
// weighted calculation
inc.src = (inc.src*2 + inc.lastPp) / 3
}
// Update uptrend
inc.uptrendPrice = inc.src - inc.AverageTrueRange.Last(0)*inc.ATRMultiplier
if inc.previousClosePrice > inc.previousUptrendPrice {
inc.uptrendPrice = math.Max(inc.uptrendPrice, inc.previousUptrendPrice)
}
// Update downtrend
inc.downtrendPrice = inc.src + inc.AverageTrueRange.Last(0)*inc.ATRMultiplier
if inc.previousClosePrice < inc.previousDowntrendPrice {
inc.downtrendPrice = math.Min(inc.downtrendPrice, inc.previousDowntrendPrice)
}
// Update trend
if inc.previousTrend == types.DirectionUp && inc.closePrice < inc.previousUptrendPrice {
inc.trend = types.DirectionDown
} else if inc.previousTrend == types.DirectionDown && inc.closePrice > inc.previousDowntrendPrice {
inc.trend = types.DirectionUp
} else {
inc.trend = inc.previousTrend
}
// Update signal
if inc.AverageTrueRange.Last(0) <= 0 {
inc.tradeSignal = types.DirectionNone
} else if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown {
inc.tradeSignal = types.DirectionUp
} else if inc.trend == types.DirectionDown && inc.previousTrend == types.DirectionUp {
inc.tradeSignal = types.DirectionDown
} else {
inc.tradeSignal = types.DirectionNone
}
// Update trend price
if inc.trend == types.DirectionDown {
inc.trendPrices.Push(inc.downtrendPrice)
} else {
inc.trendPrices.Push(inc.uptrendPrice)
}
// Save the trend lines
inc.supportLine.Push(inc.uptrendPrice)
inc.resistanceLine.Push(inc.downtrendPrice)
logpst.Debugf("Update pivot point supertrend result: closePrice: %v, uptrendPrice: %v, downtrendPrice: %v, trend: %v,"+
" tradeSignal: %v, AverageTrueRange.Last(): %v", inc.closePrice, inc.uptrendPrice, inc.downtrendPrice,
inc.trend, inc.tradeSignal, inc.AverageTrueRange.Last(0))
}
// GetSignal returns signal (Down, None or Up)
func (inc *PivotSupertrend) GetSignal() types.Direction {
return inc.tradeSignal
}
// GetDirection return the current trend
func (inc *PivotSupertrend) Direction() types.Direction {
return inc.trend
}
// LastSupertrendSupport return the current supertrend support value
func (inc *PivotSupertrend) LastSupertrendSupport() float64 {
return inc.supportLine.Last(0)
}
// LastSupertrendResistance return the current supertrend resistance value
func (inc *PivotSupertrend) LastSupertrendResistance() float64 {
return inc.resistanceLine.Last(0)
}
var _ types.SeriesExtend = &PivotSupertrend{}
func (inc *PivotSupertrend) PushK(k types.KLine) {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
return
}
inc.Update(k.GetHigh().Float64(), k.GetLow().Float64(), k.GetClose().Float64())
inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last(0))
}
func (inc *PivotSupertrend) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
target.OnKLineClosed(types.KLineWith(symbol, interval, inc.PushK))
}
func (inc *PivotSupertrend) LoadK(allKLines []types.KLine) {
inc.SeriesBase.Series = inc
for _, k := range allKLines {
inc.PushK(k)
}
}