mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
fix/drift_stoploss
This commit is contained in:
parent
294a58cbfd
commit
5c1d0f95e2
|
@ -26,7 +26,7 @@ exchangeStrategies:
|
||||||
|
|
||||||
- on: binance
|
- on: binance
|
||||||
drift:
|
drift:
|
||||||
limitOrder: false
|
limitOrder: true
|
||||||
#quantity: 0.0012
|
#quantity: 0.0012
|
||||||
canvasPath: "./output.png"
|
canvasPath: "./output.png"
|
||||||
symbol: BTCUSDT
|
symbol: BTCUSDT
|
||||||
|
@ -34,21 +34,21 @@ exchangeStrategies:
|
||||||
interval: 1m
|
interval: 1m
|
||||||
window: 6
|
window: 6
|
||||||
useAtr: true
|
useAtr: true
|
||||||
useStopLoss: false
|
useStopLoss: true
|
||||||
stoploss: 0.04%
|
stoploss: 0.05%
|
||||||
source: hl2
|
source: hl2
|
||||||
predictOffset: 2
|
predictOffset: 2
|
||||||
noTrailingStopLoss: false
|
noTrailingStopLoss: false
|
||||||
trailingStopLossType: realtime
|
trailingStopLossType: kline
|
||||||
# stddev on high/low-source
|
# stddev on high/low-source
|
||||||
hlVarianceMultiplier: 0.15
|
hlVarianceMultiplier: 0.14
|
||||||
hlRangeWindow: 4
|
hlRangeWindow: 4
|
||||||
smootherWindow: 3
|
smootherWindow: 3
|
||||||
fisherTransformWindow: 181
|
fisherTransformWindow: 125
|
||||||
#fisherTransformWindow: 117
|
#fisherTransformWindow: 117
|
||||||
atrWindow: 24
|
atrWindow: 24
|
||||||
# orders not been traded will be canceled after `pendingMinutes` minutes
|
# orders not been traded will be canceled after `pendingMinutes` minutes
|
||||||
pendingMinutes: 5
|
pendingMinutes: 10
|
||||||
noRebalance: true
|
noRebalance: true
|
||||||
trendWindow: 15
|
trendWindow: 15
|
||||||
rebalanceFilter: -0.1
|
rebalanceFilter: -0.1
|
||||||
|
@ -57,12 +57,12 @@ exchangeStrategies:
|
||||||
# when farest price from entry goes over that ratio, start using the callback ratio accordingly to do trailingstop
|
# when farest price from entry goes over that ratio, start using the callback ratio accordingly to do trailingstop
|
||||||
#trailingActivationRatio: [0.01, 0.016, 0.05]
|
#trailingActivationRatio: [0.01, 0.016, 0.05]
|
||||||
#trailingActivationRatio: [0.001, 0.0081, 0.022]
|
#trailingActivationRatio: [0.001, 0.0081, 0.022]
|
||||||
trailingActivationRatio: [0.0004, 0.008, 0.002, 0.01]
|
trailingActivationRatio: [0.0008, 0.002, 0.01]
|
||||||
#trailingActivationRatio: []
|
#trailingActivationRatio: []
|
||||||
#trailingCallbackRate: []
|
#trailingCallbackRate: []
|
||||||
#trailingCallbackRate: [0.002, 0.01, 0.1]
|
#trailingCallbackRate: [0.002, 0.01, 0.1]
|
||||||
#trailingCallbackRate: [0.0004, 0.0009, 0.018]
|
#trailingCallbackRate: [0.0004, 0.0009, 0.018]
|
||||||
trailingCallbackRate: [0.00005, 0.00012, 0.0008, 0.0016]
|
trailingCallbackRate: [0.00014, 0.0003, 0.0016]
|
||||||
|
|
||||||
generateGraph: true
|
generateGraph: true
|
||||||
graphPNLDeductFee: false
|
graphPNLDeductFee: false
|
||||||
|
@ -126,7 +126,7 @@ sync:
|
||||||
|
|
||||||
backtest:
|
backtest:
|
||||||
startTime: "2022-09-25"
|
startTime: "2022-09-25"
|
||||||
endTime: "2022-09-30"
|
endTime: "2022-10-30"
|
||||||
symbols:
|
symbols:
|
||||||
- BTCUSDT
|
- BTCUSDT
|
||||||
sessions: [binance]
|
sessions: [binance]
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
package drift
|
package drift
|
||||||
|
|
||||||
func (s *Strategy) CheckStopLoss() bool {
|
func (s *Strategy) CheckStopLoss() bool {
|
||||||
stoploss := s.StopLoss.Float64()
|
|
||||||
atr := s.atr.Last()
|
|
||||||
if s.UseStopLoss {
|
if s.UseStopLoss {
|
||||||
|
stoploss := s.StopLoss.Float64()
|
||||||
if s.sellPrice > 0 && s.sellPrice*(1.+stoploss) <= s.highestPrice ||
|
if s.sellPrice > 0 && s.sellPrice*(1.+stoploss) <= s.highestPrice ||
|
||||||
s.buyPrice > 0 && s.buyPrice*(1.-stoploss) >= s.lowestPrice {
|
s.buyPrice > 0 && s.buyPrice*(1.-stoploss) >= s.lowestPrice {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.UseAtr {
|
if s.UseAtr {
|
||||||
|
atr := s.atr.Last()
|
||||||
if s.sellPrice > 0 && s.sellPrice+atr <= s.highestPrice ||
|
if s.sellPrice > 0 && s.sellPrice+atr <= s.highestPrice ||
|
||||||
s.buyPrice > 0 && s.buyPrice-atr >= s.lowestPrice {
|
s.buyPrice > 0 && s.buyPrice-atr >= s.lowestPrice {
|
||||||
return true
|
return true
|
||||||
|
|
58
pkg/strategy/drift/stoploss_test.go
Normal file
58
pkg/strategy/drift/stoploss_test.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package drift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/datatype/floats"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/indicator"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_StopLossLong(t *testing.T) {
|
||||||
|
s := &Strategy{}
|
||||||
|
s.highestPrice = 30.
|
||||||
|
s.buyPrice = 30.
|
||||||
|
s.lowestPrice = 29.7
|
||||||
|
s.StopLoss = fixedpoint.NewFromFloat(0.01)
|
||||||
|
s.UseAtr = false
|
||||||
|
s.UseStopLoss = true
|
||||||
|
assert.True(t, s.CheckStopLoss())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_StopLossShort(t *testing.T) {
|
||||||
|
s := &Strategy{}
|
||||||
|
s.lowestPrice = 30.
|
||||||
|
s.sellPrice = 30.
|
||||||
|
s.highestPrice = 30.3
|
||||||
|
s.StopLoss = fixedpoint.NewFromFloat(0.01)
|
||||||
|
s.UseAtr = false
|
||||||
|
s.UseStopLoss = true
|
||||||
|
assert.True(t, s.CheckStopLoss())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ATRLong(t *testing.T) {
|
||||||
|
s := &Strategy{}
|
||||||
|
s.highestPrice = 30.
|
||||||
|
s.buyPrice = 30.
|
||||||
|
s.lowestPrice = 28.7
|
||||||
|
s.UseAtr = true
|
||||||
|
s.UseStopLoss = false
|
||||||
|
s.atr = &indicator.ATR{RMA: &indicator.RMA{
|
||||||
|
Values: floats.Slice{1., 1.2, 1.3},
|
||||||
|
}}
|
||||||
|
assert.True(t, s.CheckStopLoss())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ATRShort(t *testing.T) {
|
||||||
|
s := &Strategy{}
|
||||||
|
s.highestPrice = 31.3
|
||||||
|
s.sellPrice = 30.
|
||||||
|
s.lowestPrice = 30.
|
||||||
|
s.UseAtr = true
|
||||||
|
s.UseStopLoss = false
|
||||||
|
s.atr = &indicator.ATR{RMA: &indicator.RMA{
|
||||||
|
Values: floats.Slice{1., 1.2, 1.3},
|
||||||
|
}}
|
||||||
|
assert.True(t, s.CheckStopLoss())
|
||||||
|
}
|
|
@ -274,10 +274,8 @@ func (s *Strategy) smartCancel(ctx context.Context, pricef, atr float64) (int, e
|
||||||
if toCancel {
|
if toCancel {
|
||||||
err := s.GeneralOrderExecutor.GracefulCancel(ctx)
|
err := s.GeneralOrderExecutor.GracefulCancel(ctx)
|
||||||
// TODO: clean orderPendingCounter on cancel/trade
|
// TODO: clean orderPendingCounter on cancel/trade
|
||||||
if err == nil {
|
for _, order := range nonTraded {
|
||||||
for _, order := range nonTraded {
|
delete(s.orderPendingCounter, order.OrderID)
|
||||||
delete(s.orderPendingCounter, order.OrderID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
log.Warnf("cancel all %v", err)
|
log.Warnf("cancel all %v", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -306,7 +304,7 @@ func (s *Strategy) trailingCheck(price float64, direction string) bool {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (s.highestPrice-s.buyPrice)/s.buyPrice > trailingActivationRatio {
|
if (s.highestPrice-s.buyPrice)/s.buyPrice > trailingActivationRatio {
|
||||||
return (s.highestPrice-price)/price > trailingCallbackRate
|
return (s.highestPrice-price)/s.buyPrice > trailingCallbackRate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,14 +350,18 @@ func (s *Strategy) initTickerFunctions(ctx context.Context) {
|
||||||
if s.lowestPrice > 0 && s.lowestPrice > pricef {
|
if s.lowestPrice > 0 && s.lowestPrice > pricef {
|
||||||
s.lowestPrice = pricef
|
s.lowestPrice = pricef
|
||||||
}
|
}
|
||||||
|
if s.CheckStopLoss() {
|
||||||
|
s.positionLock.Unlock()
|
||||||
|
s.ClosePosition(ctx, fixedpoint.One)
|
||||||
|
return
|
||||||
|
}
|
||||||
// for trailing stoploss during the realtime
|
// for trailing stoploss during the realtime
|
||||||
if s.NoTrailingStopLoss || s.TrailingStopLossType == "kline" {
|
if s.NoTrailingStopLoss || s.TrailingStopLossType == "kline" {
|
||||||
s.positionLock.Unlock()
|
s.positionLock.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
exitCondition := s.CheckStopLoss() ||
|
exitCondition := s.trailingCheck(pricef, "short") || s.trailingCheck(pricef, "long")
|
||||||
s.trailingCheck(pricef, "short") || s.trailingCheck(pricef, "long")
|
|
||||||
|
|
||||||
s.positionLock.Unlock()
|
s.positionLock.Unlock()
|
||||||
if exitCondition {
|
if exitCondition {
|
||||||
|
@ -683,15 +685,15 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
s.positionLock.Unlock()
|
s.positionLock.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
/*source = source.Sub(fixedpoint.NewFromFloat(s.stdevLow.Last() * s.HighLowVarianceMultiplier))
|
source = source.Sub(fixedpoint.NewFromFloat(s.stdevLow.Last() * s.HighLowVarianceMultiplier))
|
||||||
if source.Compare(price) > 0 {
|
|
||||||
source = price
|
|
||||||
}*/
|
|
||||||
source = fixedpoint.NewFromFloat(s.ma.Last() - s.stdevLow.Last()*s.HighLowVarianceMultiplier)
|
|
||||||
if source.Compare(price) > 0 {
|
if source.Compare(price) > 0 {
|
||||||
source = price
|
source = price
|
||||||
}
|
}
|
||||||
sourcef = source.Float64()
|
/*source = fixedpoint.NewFromFloat(s.ma.Last() - s.stdevLow.Last()*s.HighLowVarianceMultiplier)
|
||||||
|
if source.Compare(price) > 0 {
|
||||||
|
source = price
|
||||||
|
}
|
||||||
|
sourcef = source.Float64()*/
|
||||||
log.Infof("source in long %v %v %f", source, price, s.stdevLow.Last())
|
log.Infof("source in long %v %v %f", source, price, s.stdevLow.Last())
|
||||||
|
|
||||||
s.positionLock.Unlock()
|
s.positionLock.Unlock()
|
||||||
|
@ -721,15 +723,15 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*source = source.Add(fixedpoint.NewFromFloat(s.stdevHigh.Last() * s.HighLowVarianceMultiplier))
|
source = source.Add(fixedpoint.NewFromFloat(s.stdevHigh.Last() * s.HighLowVarianceMultiplier))
|
||||||
if source.Compare(price) < 0 {
|
|
||||||
source = price
|
|
||||||
}*/
|
|
||||||
source = fixedpoint.NewFromFloat(s.ma.Last() + s.stdevHigh.Last()*s.HighLowVarianceMultiplier)
|
|
||||||
if source.Compare(price) < 0 {
|
if source.Compare(price) < 0 {
|
||||||
source = price
|
source = price
|
||||||
}
|
}
|
||||||
sourcef = source.Float64()
|
/*source = fixedpoint.NewFromFloat(s.ma.Last() + s.stdevHigh.Last()*s.HighLowVarianceMultiplier)
|
||||||
|
if source.Compare(price) < 0 {
|
||||||
|
source = price
|
||||||
|
}
|
||||||
|
sourcef = source.Float64()*/
|
||||||
|
|
||||||
log.Infof("source in short: %v", source)
|
log.Infof("source in short: %v", source)
|
||||||
|
|
||||||
|
@ -845,11 +847,11 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
s.buyPrice = s.p.ApproximateAverageCost.Float64() //trade.Price.Float64()
|
s.buyPrice = s.p.ApproximateAverageCost.Float64() //trade.Price.Float64()
|
||||||
s.sellPrice = 0
|
s.sellPrice = 0
|
||||||
s.highestPrice = math.Max(s.buyPrice, s.highestPrice)
|
s.highestPrice = math.Max(s.buyPrice, s.highestPrice)
|
||||||
s.lowestPrice = 0
|
s.lowestPrice = s.buyPrice
|
||||||
} else if s.p.IsShort() {
|
} else if s.p.IsShort() {
|
||||||
s.sellPrice = s.p.ApproximateAverageCost.Float64() //trade.Price.Float64()
|
s.sellPrice = s.p.ApproximateAverageCost.Float64() //trade.Price.Float64()
|
||||||
s.buyPrice = 0
|
s.buyPrice = 0
|
||||||
s.highestPrice = 0
|
s.highestPrice = s.sellPrice
|
||||||
if s.lowestPrice == 0 {
|
if s.lowestPrice == 0 {
|
||||||
s.lowestPrice = s.sellPrice
|
s.lowestPrice = s.sellPrice
|
||||||
} else {
|
} else {
|
||||||
|
|
37
pkg/strategy/drift/strategy_test.go
Normal file
37
pkg/strategy/drift/strategy_test.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package drift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_TrailingCheckLong(t *testing.T) {
|
||||||
|
s := &Strategy{}
|
||||||
|
s.highestPrice = 30.
|
||||||
|
s.buyPrice = 30.
|
||||||
|
s.TrailingActivationRatio = []float64{0.002, 0.01}
|
||||||
|
s.TrailingCallbackRate = []float64{0.0008, 0.0016}
|
||||||
|
assert.False(t, s.trailingCheck(31., "long"))
|
||||||
|
assert.False(t, s.trailingCheck(31., "short"))
|
||||||
|
assert.False(t, s.trailingCheck(30.96, "short"))
|
||||||
|
assert.False(t, s.trailingCheck(30.96, "long"))
|
||||||
|
assert.False(t, s.trailingCheck(30.95, "short"))
|
||||||
|
assert.True(t, s.trailingCheck(30.95, "long"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_TrailingCheckShort(t *testing.T) {
|
||||||
|
s := &Strategy{}
|
||||||
|
s.lowestPrice = 30.
|
||||||
|
s.sellPrice = 30.
|
||||||
|
s.TrailingActivationRatio = []float64{0.002, 0.01}
|
||||||
|
s.TrailingCallbackRate = []float64{0.0008, 0.0016}
|
||||||
|
assert.False(t, s.trailingCheck(29.96, "long"))
|
||||||
|
assert.False(t, s.trailingCheck(29.96, "short"))
|
||||||
|
assert.False(t, s.trailingCheck(29.99, "short"))
|
||||||
|
assert.False(t, s.trailingCheck(29.99, "long"))
|
||||||
|
assert.False(t, s.trailingCheck(29.93, "short"))
|
||||||
|
assert.False(t, s.trailingCheck(29.93, "long"))
|
||||||
|
assert.True(t, s.trailingCheck(29.96, "short"))
|
||||||
|
assert.False(t, s.trailingCheck(29.96, "long"))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user