Skip to content

Commit 72f18c1

Browse files
authored
Merge pull request #799 from andycheng123/improve/supertrend-strategy
Improve supertrend strategy
2 parents e778db1 + f877775 commit 72f18c1

File tree

5 files changed

+320
-111
lines changed

5 files changed

+320
-111
lines changed

config/supertrend.yaml

+24-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ backtest:
1919
# see here for more details
2020
# https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp
2121
startTime: "2022-01-01"
22-
endTime: "2022-06-18"
22+
endTime: "2022-06-30"
2323
symbols:
2424
- BTCUSDT
2525
accounts:
@@ -36,7 +36,7 @@ exchangeStrategies:
3636
symbol: BTCUSDT
3737

3838
# interval is how long do you want to update your order price and quantity
39-
interval: 1h
39+
interval: 5m
4040

4141
# leverage is the leverage of the orders
4242
leverage: 1.0
@@ -48,15 +48,31 @@ exchangeStrategies:
4848
# Supertrend indicator parameters
4949
superTrend:
5050
# ATR window used by Supertrend
51-
averageTrueRangeWindow: 39
51+
averageTrueRangeWindow: 49
5252
# ATR Multiplier for calculating super trend prices, the higher, the stronger the trends are
53-
averageTrueRangeMultiplier: 3
53+
averageTrueRangeMultiplier: 4
54+
55+
# Use linear regression as trend confirmation
56+
linearRegression:
57+
interval: 5m
58+
window: 80
5459

5560
# TP according to ATR multiple, 0 to disable this
56-
takeProfitMultiplier: 3
61+
TakeProfitAtrMultiplier: 0
5762

5863
# Set SL price to the low of the triggering Kline
59-
stopLossByTriggeringK: true
64+
stopLossByTriggeringK: false
65+
66+
# TP/SL by reversed supertrend signal
67+
stopByReversedSupertrend: false
68+
69+
# TP/SL by reversed DEMA signal
70+
stopByReversedDema: false
71+
72+
# TP/SL by reversed linear regression signal
73+
stopByReversedLinGre: false
6074

61-
# TP/SL by reversed signals
62-
tpslBySignal: true
75+
exits:
76+
# roiStopLoss is the stop loss percentage of the position ROI (currently the price change)
77+
- roiStopLoss:
78+
percentage: 4%

doc/strategy/supertrend.md

+19-4
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,27 @@ Supertrend strategy needs margin enabled in order to submit short orders, and yo
2323
- The MA window of the ATR indicator used by Supertrend.
2424
- `averageTrueRangeMultiplier`
2525
- Multiplier for calculating upper and lower bond prices, the higher, the stronger the trends are, but also makes it less sensitive.
26-
- `takeProfitMultiplier`
26+
- `linearRegression`
27+
- Use linear regression as trend confirmation
28+
- `interval`
29+
- Time interval of linear regression
30+
- `window`
31+
- Window of linear regression
32+
- `takeProfitAtrMultiplier`
2733
- TP according to ATR multiple, 0 to disable this.
2834
- `stopLossByTriggeringK`
29-
- Set SL price to the low of the triggering Kline.
30-
- `tpslBySignal`
31-
- TP/SL by reversed signals.
35+
- Set SL price to the low/high of the triggering Kline.
36+
- `stopByReversedSupertrend`
37+
- TP/SL by reversed supertrend signal.
38+
- `stopByReversedDema`
39+
- TP/SL by reversed DEMA signal.
40+
- `stopByReversedLinGre`
41+
- TP/SL by reversed linear regression signal.
42+
- `exits`
43+
- Exit methods to TP/SL
44+
- `roiStopLoss`
45+
- The stop loss percentage of the position ROI (currently the price change)
46+
- `percentage`
3247

3348

3449
#### Examples
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package supertrend
2+
3+
import (
4+
"github.com/c9s/bbgo/pkg/bbgo"
5+
"github.com/c9s/bbgo/pkg/indicator"
6+
"github.com/c9s/bbgo/pkg/types"
7+
)
8+
9+
type DoubleDema struct {
10+
Interval types.Interval `json:"interval"`
11+
12+
// FastDEMAWindow DEMA window for checking breakout
13+
FastDEMAWindow int `json:"fastDEMAWindow"`
14+
// SlowDEMAWindow DEMA window for checking breakout
15+
SlowDEMAWindow int `json:"slowDEMAWindow"`
16+
fastDEMA *indicator.DEMA
17+
slowDEMA *indicator.DEMA
18+
}
19+
20+
// getDemaSignal get current DEMA signal
21+
func (dd *DoubleDema) getDemaSignal(openPrice float64, closePrice float64) types.Direction {
22+
var demaSignal types.Direction = types.DirectionNone
23+
24+
if closePrice > dd.fastDEMA.Last() && closePrice > dd.slowDEMA.Last() && !(openPrice > dd.fastDEMA.Last() && openPrice > dd.slowDEMA.Last()) {
25+
demaSignal = types.DirectionUp
26+
} else if closePrice < dd.fastDEMA.Last() && closePrice < dd.slowDEMA.Last() && !(openPrice < dd.fastDEMA.Last() && openPrice < dd.slowDEMA.Last()) {
27+
demaSignal = types.DirectionDown
28+
}
29+
30+
return demaSignal
31+
}
32+
33+
// preloadDema preloads DEMA indicators
34+
func (dd *DoubleDema) preloadDema(kLineStore *bbgo.MarketDataStore) {
35+
if klines, ok := kLineStore.KLinesOfInterval(dd.fastDEMA.Interval); ok {
36+
for i := 0; i < len(*klines); i++ {
37+
dd.fastDEMA.Update((*klines)[i].GetClose().Float64())
38+
}
39+
}
40+
if klines, ok := kLineStore.KLinesOfInterval(dd.slowDEMA.Interval); ok {
41+
for i := 0; i < len(*klines); i++ {
42+
dd.slowDEMA.Update((*klines)[i].GetClose().Float64())
43+
}
44+
}
45+
}
46+
47+
// setupDoubleDema initializes double DEMA indicators
48+
func (dd *DoubleDema) setupDoubleDema(kLineStore *bbgo.MarketDataStore, interval types.Interval) {
49+
dd.Interval = interval
50+
51+
// DEMA
52+
if dd.FastDEMAWindow == 0 {
53+
dd.FastDEMAWindow = 144
54+
}
55+
dd.fastDEMA = &indicator.DEMA{IntervalWindow: types.IntervalWindow{Interval: dd.Interval, Window: dd.FastDEMAWindow}}
56+
dd.fastDEMA.Bind(kLineStore)
57+
58+
if dd.SlowDEMAWindow == 0 {
59+
dd.SlowDEMAWindow = 169
60+
}
61+
dd.slowDEMA = &indicator.DEMA{IntervalWindow: types.IntervalWindow{Interval: dd.Interval, Window: dd.SlowDEMAWindow}}
62+
dd.slowDEMA.Bind(kLineStore)
63+
64+
dd.preloadDema(kLineStore)
65+
}

pkg/strategy/supertrend/lingre.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package supertrend
2+
3+
import (
4+
"github.com/c9s/bbgo/pkg/bbgo"
5+
"github.com/c9s/bbgo/pkg/indicator"
6+
"github.com/c9s/bbgo/pkg/types"
7+
)
8+
9+
// LinGre is Linear Regression baseline
10+
type LinGre struct {
11+
types.IntervalWindow
12+
baseLineSlope float64
13+
}
14+
15+
// Update Linear Regression baseline slope
16+
func (lg *LinGre) Update(klines []types.KLine) {
17+
if len(klines) < lg.Window {
18+
lg.baseLineSlope = 0
19+
return
20+
}
21+
22+
var sumX, sumY, sumXSqr, sumXY float64 = 0, 0, 0, 0
23+
end := len(klines) - 1 // The last kline
24+
for i := end; i >= end-lg.Window+1; i-- {
25+
val := klines[i].GetClose().Float64()
26+
per := float64(end - i + 1)
27+
sumX += per
28+
sumY += val
29+
sumXSqr += per * per
30+
sumXY += val * per
31+
}
32+
length := float64(lg.Window)
33+
slope := (length*sumXY - sumX*sumY) / (length*sumXSqr - sumX*sumX)
34+
average := sumY / length
35+
endPrice := average - slope*sumX/length + slope
36+
startPrice := endPrice + slope*(length-1)
37+
lg.baseLineSlope = (length - 1) / (endPrice - startPrice)
38+
39+
log.Debugf("linear regression baseline slope: %f", lg.baseLineSlope)
40+
}
41+
42+
func (lg *LinGre) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
43+
if lg.Interval != interval {
44+
return
45+
}
46+
47+
lg.Update(window)
48+
}
49+
50+
func (lg *LinGre) Bind(updater indicator.KLineWindowUpdater) {
51+
updater.OnKLineWindowUpdate(lg.handleKLineWindowUpdate)
52+
}
53+
54+
// GetSignal get linear regression signal
55+
func (lg *LinGre) GetSignal() types.Direction {
56+
var lgSignal types.Direction = types.DirectionNone
57+
58+
switch {
59+
case lg.baseLineSlope > 0:
60+
lgSignal = types.DirectionUp
61+
case lg.baseLineSlope < 0:
62+
lgSignal = types.DirectionDown
63+
}
64+
65+
return lgSignal
66+
}
67+
68+
// preloadLinGre preloads linear regression indicator
69+
func (lg *LinGre) preload(kLineStore *bbgo.MarketDataStore) {
70+
if klines, ok := kLineStore.KLinesOfInterval(lg.Interval); ok {
71+
lg.Update((*klines)[0:])
72+
}
73+
}

0 commit comments

Comments
 (0)