mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
refactor: extract stoploss, fix highest/lowest in trailingExit
This commit is contained in:
parent
ffdf2a69a4
commit
58736b1b2d
|
@ -26,35 +26,31 @@ exchangeStrategies:
|
||||||
canvasPath: "./output.png"
|
canvasPath: "./output.png"
|
||||||
symbol: ETHBUSD
|
symbol: ETHBUSD
|
||||||
limitOrder: false
|
limitOrder: false
|
||||||
|
quantity: 0.01
|
||||||
# kline interval for indicators
|
# kline interval for indicators
|
||||||
interval: 2m
|
interval: 1m
|
||||||
window: 6
|
window: 1
|
||||||
|
useAtr: true
|
||||||
|
useStopLoss: true
|
||||||
stoploss: 0.23%
|
stoploss: 0.23%
|
||||||
source: ohlc4
|
source: ohlc4
|
||||||
predictOffset: 2
|
predictOffset: 2
|
||||||
noTrailingStopLoss: false
|
noTrailingStopLoss: false
|
||||||
trailingStopLossType: kline
|
trailingStopLossType: kline
|
||||||
# stddev on high/low-source
|
# stddev on high/low-source
|
||||||
hlVarianceMultiplier: 0.03
|
hlVarianceMultiplier: 0.13
|
||||||
hlRangeWindow: 4
|
hlRangeWindow: 4
|
||||||
smootherWindow: 3
|
smootherWindow: 19
|
||||||
fisherTransformWindow: 117
|
fisherTransformWindow: 73
|
||||||
window1m: 42
|
|
||||||
smootherWindow1m: 118
|
|
||||||
fisherTransformWindow1m: 319
|
|
||||||
atrWindow: 14
|
atrWindow: 14
|
||||||
# orders not been traded will be canceled after `pendingMinutes` minutes
|
# orders not been traded will be canceled after `pendingMinutes` minutes
|
||||||
pendingMinutes: 3
|
pendingMinutes: 5
|
||||||
noRebalance: true
|
noRebalance: true
|
||||||
trendWindow: 12
|
trendWindow: 12
|
||||||
rebalanceFilter: 2
|
rebalanceFilter: 2
|
||||||
|
|
||||||
trailingActivationRatio: [0.0015, 0.002, 0.004, 0.01]
|
trailingActivationRatio: [0.0015, 0.002, 0.004, 0.01]
|
||||||
trailingCallbackRate: [0.0001, 0.00012, 0.001, 0.002]
|
trailingCallbackRate: [0.0001, 0.00012, 0.001, 0.002]
|
||||||
#driftFilterPos: 0.4
|
|
||||||
#driftFilterNeg: -0.42
|
|
||||||
#ddriftFilterPos: 0
|
|
||||||
#ddriftFilterNeg: 0
|
|
||||||
|
|
||||||
generateGraph: true
|
generateGraph: true
|
||||||
graphPNLDeductFee: true
|
graphPNLDeductFee: true
|
||||||
|
@ -92,7 +88,7 @@ sync:
|
||||||
- ETHBUSD
|
- ETHBUSD
|
||||||
|
|
||||||
backtest:
|
backtest:
|
||||||
startTime: "2022-09-01"
|
startTime: "2022-09-25"
|
||||||
endTime: "2022-09-30"
|
endTime: "2022-09-30"
|
||||||
symbols:
|
symbols:
|
||||||
- ETHBUSD
|
- ETHBUSD
|
||||||
|
@ -102,5 +98,5 @@ backtest:
|
||||||
makerFeeRate: 0.0000
|
makerFeeRate: 0.0000
|
||||||
takerFeeRate: 0.0000
|
takerFeeRate: 0.0000
|
||||||
balances:
|
balances:
|
||||||
ETH: 0
|
ETH: 0.03
|
||||||
BUSD: 1000.0
|
BUSD: 0
|
||||||
|
|
|
@ -27,46 +27,42 @@ exchangeStrategies:
|
||||||
- on: binance
|
- on: binance
|
||||||
drift:
|
drift:
|
||||||
limitOrder: false
|
limitOrder: false
|
||||||
quantity: 0.001
|
#quantity: 0.0012
|
||||||
canvasPath: "./output.png"
|
canvasPath: "./output.png"
|
||||||
symbol: BTCUSDT
|
symbol: BTCUSDT
|
||||||
# kline interval for indicators
|
# kline interval for indicators
|
||||||
interval: 1m
|
interval: 1m
|
||||||
window: 4
|
window: 6
|
||||||
stoploss: 0.02%
|
useAtr: true
|
||||||
|
useStopLoss: false
|
||||||
|
stoploss: 0.04%
|
||||||
source: hl2
|
source: hl2
|
||||||
predictOffset: 2
|
predictOffset: 2
|
||||||
noTrailingStopLoss: false
|
noTrailingStopLoss: false
|
||||||
trailingStopLossType: realtime
|
trailingStopLossType: realtime
|
||||||
# stddev on high/low-source
|
# stddev on high/low-source
|
||||||
hlVarianceMultiplier: 0.2
|
hlVarianceMultiplier: 0.15
|
||||||
hlRangeWindow: 4
|
hlRangeWindow: 4
|
||||||
smootherWindow: 31
|
smootherWindow: 3
|
||||||
fisherTransformWindow: 80
|
fisherTransformWindow: 181
|
||||||
window1m: 8
|
#fisherTransformWindow: 117
|
||||||
smootherWindow1m: 4
|
atrWindow: 24
|
||||||
fisherTransformWindow1m: 320
|
|
||||||
atrWindow: 14
|
|
||||||
# orders not been traded will be canceled after `pendingMinutes` minutes
|
# orders not been traded will be canceled after `pendingMinutes` minutes
|
||||||
pendingMinutes: 3
|
pendingMinutes: 5
|
||||||
noRebalance: true
|
noRebalance: true
|
||||||
trendWindow: 185
|
trendWindow: 15
|
||||||
rebalanceFilter: 1.2
|
rebalanceFilter: -0.1
|
||||||
#driftFilterPos: 0.5
|
|
||||||
#driftFilterNeg: -0.5
|
|
||||||
#ddriftFilterPos: 0.0008
|
|
||||||
#ddriftFilterNeg: -0.0008
|
|
||||||
|
|
||||||
# ActivationRatio should be increasing order
|
# ActivationRatio should be increasing order
|
||||||
# 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.0007, 0.0016, 0.008, 0.01]
|
trailingActivationRatio: [0.0004, 0.008, 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.0003, 0.0005, 0.0010, 0.0016]
|
trailingCallbackRate: [0.00005, 0.00012, 0.0008, 0.0016]
|
||||||
|
|
||||||
generateGraph: true
|
generateGraph: true
|
||||||
graphPNLDeductFee: false
|
graphPNLDeductFee: false
|
||||||
|
@ -129,7 +125,7 @@ sync:
|
||||||
- BTCUSDT
|
- BTCUSDT
|
||||||
|
|
||||||
backtest:
|
backtest:
|
||||||
startTime: "2022-09-26"
|
startTime: "2022-09-25"
|
||||||
endTime: "2022-09-30"
|
endTime: "2022-09-30"
|
||||||
symbols:
|
symbols:
|
||||||
- BTCUSDT
|
- BTCUSDT
|
||||||
|
@ -140,4 +136,4 @@ backtest:
|
||||||
takerFeeRate: 0.000
|
takerFeeRate: 0.000
|
||||||
balances:
|
balances:
|
||||||
BTC: 0
|
BTC: 0
|
||||||
USDT: 50
|
USDT: 49
|
||||||
|
|
|
@ -207,7 +207,7 @@ type OpenPositionOptions struct {
|
||||||
Leverage fixedpoint.Value `json:"leverage,omitempty" modifiable:"true"`
|
Leverage fixedpoint.Value `json:"leverage,omitempty" modifiable:"true"`
|
||||||
|
|
||||||
// Quantity will be used first, it will override the leverage if it's given
|
// Quantity will be used first, it will override the leverage if it's given
|
||||||
Quantity fixedpoint.Value `json:"quantity,omitempty"`
|
Quantity fixedpoint.Value `json:"quantity,omitempty" modifiable:"true"`
|
||||||
|
|
||||||
// LimitOrder set to true to open a position with a limit order
|
// LimitOrder set to true to open a position with a limit order
|
||||||
// default is false, and will send MarketOrder
|
// default is false, and will send MarketOrder
|
||||||
|
|
19
pkg/strategy/drift/stoploss.go
Normal file
19
pkg/strategy/drift/stoploss.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package drift
|
||||||
|
|
||||||
|
func (s *Strategy) CheckStopLoss() bool {
|
||||||
|
stoploss := s.StopLoss.Float64()
|
||||||
|
atr := s.atr.Last()
|
||||||
|
if s.UseStopLoss {
|
||||||
|
if s.sellPrice > 0 && s.sellPrice*(1.+stoploss) <= s.highestPrice ||
|
||||||
|
s.buyPrice > 0 && s.buyPrice*(1.-stoploss) >= s.lowestPrice {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.UseAtr {
|
||||||
|
if s.sellPrice > 0 && s.sellPrice+atr <= s.highestPrice ||
|
||||||
|
s.buyPrice > 0 && s.buyPrice-atr >= s.lowestPrice {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -73,7 +73,6 @@ type Strategy struct {
|
||||||
stdevHigh *indicator.StdDev
|
stdevHigh *indicator.StdDev
|
||||||
stdevLow *indicator.StdDev
|
stdevLow *indicator.StdDev
|
||||||
drift *DriftMA
|
drift *DriftMA
|
||||||
drift1m *DriftMA
|
|
||||||
atr *indicator.ATR
|
atr *indicator.ATR
|
||||||
midPrice fixedpoint.Value
|
midPrice fixedpoint.Value
|
||||||
lock sync.RWMutex `ignore:"true"`
|
lock sync.RWMutex `ignore:"true"`
|
||||||
|
@ -86,6 +85,8 @@ type Strategy struct {
|
||||||
|
|
||||||
beta float64
|
beta float64
|
||||||
|
|
||||||
|
UseStopLoss bool `json:"useStopLoss" modifiable:"true"`
|
||||||
|
UseAtr bool `json:"useAtr" modifiable:"true"`
|
||||||
StopLoss fixedpoint.Value `json:"stoploss" modifiable:"true"`
|
StopLoss fixedpoint.Value `json:"stoploss" modifiable:"true"`
|
||||||
CanvasPath string `json:"canvasPath"`
|
CanvasPath string `json:"canvasPath"`
|
||||||
PredictOffset int `json:"predictOffset"`
|
PredictOffset int `json:"predictOffset"`
|
||||||
|
@ -93,9 +94,6 @@ type Strategy struct {
|
||||||
NoTrailingStopLoss bool `json:"noTrailingStopLoss" modifiable:"true"`
|
NoTrailingStopLoss bool `json:"noTrailingStopLoss" modifiable:"true"`
|
||||||
TrailingStopLossType string `json:"trailingStopLossType" modifiable:"true"` // trailing stop sources. Possible options are `kline` for 1m kline and `realtime` from order updates
|
TrailingStopLossType string `json:"trailingStopLossType" modifiable:"true"` // trailing stop sources. Possible options are `kline` for 1m kline and `realtime` from order updates
|
||||||
HLRangeWindow int `json:"hlRangeWindow"`
|
HLRangeWindow int `json:"hlRangeWindow"`
|
||||||
Window1m int `json:"window1m"`
|
|
||||||
FisherTransformWindow1m int `json:"fisherTransformWindow1m"`
|
|
||||||
SmootherWindow1m int `json:"smootherWindow1m"`
|
|
||||||
SmootherWindow int `json:"smootherWindow"`
|
SmootherWindow int `json:"smootherWindow"`
|
||||||
FisherTransformWindow int `json:"fisherTransformWindow"`
|
FisherTransformWindow int `json:"fisherTransformWindow"`
|
||||||
ATRWindow int `json:"atrWindow"`
|
ATRWindow int `json:"atrWindow"`
|
||||||
|
@ -106,11 +104,6 @@ type Strategy struct {
|
||||||
TrailingCallbackRate []float64 `json:"trailingCallbackRate" modifiable:"true"`
|
TrailingCallbackRate []float64 `json:"trailingCallbackRate" modifiable:"true"`
|
||||||
TrailingActivationRatio []float64 `json:"trailingActivationRatio" modifiable:"true"`
|
TrailingActivationRatio []float64 `json:"trailingActivationRatio" modifiable:"true"`
|
||||||
|
|
||||||
DriftFilterNeg float64 //`json:"driftFilterNeg" modifiable:"true"`
|
|
||||||
DriftFilterPos float64 //`json:"driftFilterPos" modifiable:"true"`
|
|
||||||
DDriftFilterNeg float64 //`json:"ddriftFilterNeg" modifiable:"true"`
|
|
||||||
DDriftFilterPos float64 //`json:"ddriftFilterPos" modifiable:"true"`
|
|
||||||
|
|
||||||
buyPrice float64 `persistence:"buy_price"`
|
buyPrice float64 `persistence:"buy_price"`
|
||||||
sellPrice float64 `persistence:"sell_price"`
|
sellPrice float64 `persistence:"sell_price"`
|
||||||
highestPrice float64 `persistence:"highest_price"`
|
highestPrice float64 `persistence:"highest_price"`
|
||||||
|
@ -143,10 +136,6 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||||
// by default, bbgo only pre-subscribe 1000 klines.
|
// by default, bbgo only pre-subscribe 1000 klines.
|
||||||
// this is not enough if we're subscribing 30m intervals using SerialMarketDataStore
|
// this is not enough if we're subscribing 30m intervals using SerialMarketDataStore
|
||||||
maxWindow := (s.Window + s.SmootherWindow + s.FisherTransformWindow) * s.Interval.Minutes()
|
maxWindow := (s.Window + s.SmootherWindow + s.FisherTransformWindow) * s.Interval.Minutes()
|
||||||
maxWindow1m := s.Window1m + s.SmootherWindow1m + s.FisherTransformWindow1m
|
|
||||||
if maxWindow < maxWindow1m {
|
|
||||||
maxWindow = maxWindow1m
|
|
||||||
}
|
|
||||||
bbgo.KLinePreloadLimit = int64((maxWindow/1000 + 1) * 1000)
|
bbgo.KLinePreloadLimit = int64((maxWindow/1000 + 1) * 1000)
|
||||||
log.Errorf("set kLinePreloadLimit to %d, %d %d", bbgo.KLinePreloadLimit, s.Interval.Minutes(), maxWindow)
|
log.Errorf("set kLinePreloadLimit to %d, %d %d", bbgo.KLinePreloadLimit, s.Interval.Minutes(), maxWindow)
|
||||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{
|
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{
|
||||||
|
@ -212,20 +201,6 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
s.drift.SeriesBase.Series = s.drift
|
s.drift.SeriesBase.Series = s.drift
|
||||||
s.drift1m = &DriftMA{
|
|
||||||
drift: &indicator.WeightedDrift{
|
|
||||||
MA: &indicator.SMA{IntervalWindow: types.IntervalWindow{Interval: types.Interval1m, Window: s.Window1m}},
|
|
||||||
IntervalWindow: types.IntervalWindow{Interval: types.Interval1m, Window: s.Window1m},
|
|
||||||
},
|
|
||||||
ma1: &indicator.EWMA{
|
|
||||||
IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.SmootherWindow1m},
|
|
||||||
},
|
|
||||||
|
|
||||||
ma2: &indicator.FisherTransform{
|
|
||||||
IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.FisherTransformWindow1m},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
s.drift1m.SeriesBase.Series = s.drift1m
|
|
||||||
s.atr = &indicator.ATR{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.ATRWindow}}
|
s.atr = &indicator.ATR{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.ATRWindow}}
|
||||||
s.trendLine = &indicator.EWMA{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.TrendWindow}}
|
s.trendLine = &indicator.EWMA{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.TrendWindow}}
|
||||||
|
|
||||||
|
@ -256,13 +231,6 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
|
||||||
return errors.New("klines not exists")
|
return errors.New("klines not exists")
|
||||||
}
|
}
|
||||||
log.Infof("loaded %d klines1m", klinesLength)
|
log.Infof("loaded %d klines1m", klinesLength)
|
||||||
for _, kline := range *klines {
|
|
||||||
source := s.GetSource(&kline).Float64()
|
|
||||||
s.drift1m.Update(source, kline.Volume.Abs().Float64())
|
|
||||||
if s.drift1m.Last() != s.drift1m.Last() {
|
|
||||||
panic(fmt.Sprintf("%f %v %f %f", source, s.drift1m.drift.Values.Index(1), s.drift1m.ma2.Last(), s.drift1m.drift.LastValue))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if s.kline1m != nil && klines != nil {
|
if s.kline1m != nil && klines != nil {
|
||||||
s.kline1m.Set(&(*klines)[len(*klines)-1])
|
s.kline1m.Set(&(*klines)[len(*klines)-1])
|
||||||
}
|
}
|
||||||
|
@ -278,18 +246,12 @@ func (s *Strategy) smartCancel(ctx context.Context, pricef, atr float64) (int, e
|
||||||
}
|
}
|
||||||
toCancel := false
|
toCancel := false
|
||||||
|
|
||||||
drift := s.drift1m.Array(2)
|
|
||||||
for _, order := range nonTraded {
|
for _, order := range nonTraded {
|
||||||
if order.Status != types.OrderStatusNew && order.Status != types.OrderStatusPartiallyFilled {
|
if order.Status != types.OrderStatusNew && order.Status != types.OrderStatusPartiallyFilled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Warnf("%v | counter: %d, system: %d", order, s.orderPendingCounter[order.OrderID], s.minutesCounter)
|
log.Warnf("%v | counter: %d, system: %d", order, s.orderPendingCounter[order.OrderID], s.minutesCounter)
|
||||||
if s.minutesCounter-s.orderPendingCounter[order.OrderID] > s.PendingMinutes {
|
if s.minutesCounter-s.orderPendingCounter[order.OrderID] > s.PendingMinutes {
|
||||||
if order.Side == types.SideTypeBuy && drift[1] < drift[0] {
|
|
||||||
continue
|
|
||||||
} else if order.Side == types.SideTypeSell && drift[1] > drift[0] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
toCancel = true
|
toCancel = true
|
||||||
} else if order.Side == types.SideTypeBuy {
|
} else if order.Side == types.SideTypeBuy {
|
||||||
// 75% of the probability
|
// 75% of the probability
|
||||||
|
@ -328,6 +290,9 @@ func (s *Strategy) trailingCheck(price float64, direction string) bool {
|
||||||
s.lowestPrice = price
|
s.lowestPrice = price
|
||||||
}
|
}
|
||||||
isShort := direction == "short"
|
isShort := direction == "short"
|
||||||
|
if isShort && s.sellPrice == 0 || !isShort && s.buyPrice == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
for i := len(s.TrailingCallbackRate) - 1; i >= 0; i-- {
|
for i := len(s.TrailingCallbackRate) - 1; i >= 0; i-- {
|
||||||
trailingCallbackRate := s.TrailingCallbackRate[i]
|
trailingCallbackRate := s.TrailingCallbackRate[i]
|
||||||
trailingActivationRatio := s.TrailingActivationRatio[i]
|
trailingActivationRatio := s.TrailingActivationRatio[i]
|
||||||
|
@ -389,15 +354,11 @@ func (s *Strategy) initTickerFunctions(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
stoploss := s.StopLoss.Float64()
|
exitCondition := s.CheckStopLoss() ||
|
||||||
|
s.trailingCheck(pricef, "short") || s.trailingCheck(pricef, "long")
|
||||||
exitShortCondition := s.sellPrice > 0 && (s.sellPrice*(1.+stoploss) <= pricef ||
|
|
||||||
s.trailingCheck(pricef, "short"))
|
|
||||||
exitLongCondition := s.buyPrice > 0 && (s.buyPrice*(1.-stoploss) >= pricef ||
|
|
||||||
s.trailingCheck(pricef, "long"))
|
|
||||||
|
|
||||||
s.positionLock.Unlock()
|
s.positionLock.Unlock()
|
||||||
if exitShortCondition || exitLongCondition {
|
if exitCondition {
|
||||||
s.ClosePosition(ctx, fixedpoint.One)
|
s.ClosePosition(ctx, fixedpoint.One)
|
||||||
log.Infof("close position by orderbook changes")
|
log.Infof("close position by orderbook changes")
|
||||||
}
|
}
|
||||||
|
@ -432,21 +393,16 @@ func (s *Strategy) DrawIndicators(time types.Time) *types.Canvas {
|
||||||
highestPrice := s.priceLines.Minus(mean).Abs().Highest(Length)
|
highestPrice := s.priceLines.Minus(mean).Abs().Highest(Length)
|
||||||
highestDrift := s.drift.Abs().Highest(Length)
|
highestDrift := s.drift.Abs().Highest(Length)
|
||||||
hi := s.drift.drift.Abs().Highest(Length)
|
hi := s.drift.drift.Abs().Highest(Length)
|
||||||
h1m := s.drift1m.Abs().Highest(Length * s.Interval.Minutes())
|
|
||||||
ratio := highestPrice / highestDrift
|
ratio := highestPrice / highestDrift
|
||||||
|
|
||||||
//canvas.Plot("upband", s.ma.Add(s.stdevHigh), time, Length)
|
//canvas.Plot("upband", s.ma.Add(s.stdevHigh), time, Length)
|
||||||
canvas.Plot("ma", s.ma, time, Length)
|
canvas.Plot("ma", s.ma, time, Length)
|
||||||
//canvas.Plot("downband", s.ma.Minus(s.stdevLow), time, Length)
|
//canvas.Plot("downband", s.ma.Minus(s.stdevLow), time, Length)
|
||||||
canvas.Plot("pos", types.NumberSeries(s.DriftFilterPos*ratio+mean), time, Length)
|
|
||||||
canvas.Plot("neg", types.NumberSeries(s.DriftFilterNeg*ratio+mean), time, Length)
|
|
||||||
fmt.Printf("%f %f\n", highestPrice, hi)
|
fmt.Printf("%f %f\n", highestPrice, hi)
|
||||||
canvas.Plot("ppos", types.NumberSeries(s.DDriftFilterPos*(highestPrice/hi)+mean), time, Length)
|
|
||||||
canvas.Plot("nneg", types.NumberSeries(s.DDriftFilterNeg*(highestPrice/hi)+mean), time, Length)
|
|
||||||
|
|
||||||
|
canvas.Plot("trend", s.trendLine, time, Length)
|
||||||
canvas.Plot("drift", s.drift.Mul(ratio).Add(mean), time, Length)
|
canvas.Plot("drift", s.drift.Mul(ratio).Add(mean), time, Length)
|
||||||
canvas.Plot("driftOrig", s.drift.drift.Mul(highestPrice/hi).Add(mean), time, Length)
|
canvas.Plot("driftOrig", s.drift.drift.Mul(highestPrice/hi).Add(mean), time, Length)
|
||||||
canvas.Plot("drift1m", s.drift1m.Mul(highestPrice/h1m).Add(mean), time, Length*s.Interval.Minutes(), types.Interval1m)
|
|
||||||
canvas.Plot("zero", types.NumberSeries(mean), time, Length)
|
canvas.Plot("zero", types.NumberSeries(mean), time, Length)
|
||||||
canvas.Plot("price", s.priceLines, time, Length)
|
canvas.Plot("price", s.priceLines, time, Length)
|
||||||
return canvas
|
return canvas
|
||||||
|
@ -586,7 +542,6 @@ func (s *Strategy) CalcAssetValue(price fixedpoint.Value) fixedpoint.Value {
|
||||||
|
|
||||||
func (s *Strategy) klineHandler1m(ctx context.Context, kline types.KLine) {
|
func (s *Strategy) klineHandler1m(ctx context.Context, kline types.KLine) {
|
||||||
s.kline1m.Set(&kline)
|
s.kline1m.Set(&kline)
|
||||||
s.drift1m.Update(s.GetSource(&kline).Float64(), kline.Volume.Abs().Float64())
|
|
||||||
if s.Status != types.StrategyStatusRunning {
|
if s.Status != types.StrategyStatusRunning {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -594,7 +549,6 @@ func (s *Strategy) klineHandler1m(ctx context.Context, kline types.KLine) {
|
||||||
atr := s.atr.Last()
|
atr := s.atr.Last()
|
||||||
price := s.getLastPrice()
|
price := s.getLastPrice()
|
||||||
pricef := price.Float64()
|
pricef := price.Float64()
|
||||||
stoploss := s.StopLoss.Float64()
|
|
||||||
|
|
||||||
lowf := math.Min(kline.Low.Float64(), pricef)
|
lowf := math.Min(kline.Low.Float64(), pricef)
|
||||||
highf := math.Max(kline.High.Float64(), pricef)
|
highf := math.Max(kline.High.Float64(), pricef)
|
||||||
|
@ -605,11 +559,6 @@ func (s *Strategy) klineHandler1m(ctx context.Context, kline types.KLine) {
|
||||||
if s.highestPrice > 0 && highf > s.highestPrice {
|
if s.highestPrice > 0 && highf > s.highestPrice {
|
||||||
s.highestPrice = highf
|
s.highestPrice = highf
|
||||||
}
|
}
|
||||||
drift := s.drift1m.Array(2)
|
|
||||||
if len(drift) < 2 {
|
|
||||||
s.positionLock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
numPending := 0
|
numPending := 0
|
||||||
var err error
|
var err error
|
||||||
|
@ -628,13 +577,9 @@ func (s *Strategy) klineHandler1m(ctx context.Context, kline types.KLine) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Infof("d1m: %f, hf: %f, lf: %f", s.drift1m.Last(), highf, lowf)
|
exitCondition := s.CheckStopLoss() || s.trailingCheck(highf, "short") || s.trailingCheck(lowf, "long")
|
||||||
exitShortCondition := s.sellPrice > 0 && (s.sellPrice*(1.+stoploss) <= highf ||
|
|
||||||
s.trailingCheck(highf, "short") /* || s.drift1m.Last() > 0*/)
|
|
||||||
exitLongCondition := s.buyPrice > 0 && (s.buyPrice*(1.-stoploss) >= lowf ||
|
|
||||||
s.trailingCheck(lowf, "long") /* || s.drift1m.Last() < 0*/)
|
|
||||||
s.positionLock.Unlock()
|
s.positionLock.Unlock()
|
||||||
if exitShortCondition || exitLongCondition {
|
if exitCondition {
|
||||||
_ = s.ClosePosition(ctx, fixedpoint.One)
|
_ = s.ClosePosition(ctx, fixedpoint.One)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -677,7 +622,6 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
if s.Status != types.StrategyStatusRunning {
|
if s.Status != types.StrategyStatusRunning {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stoploss := s.StopLoss.Float64()
|
|
||||||
|
|
||||||
s.positionLock.Lock()
|
s.positionLock.Lock()
|
||||||
log.Infof("highdiff: %3.2f ma: %.2f, open: %8v, close: %8v, high: %8v, low: %8v, time: %v %v", s.stdevHigh.Last(), s.ma.Last(), kline.Open, kline.Close, kline.High, kline.Low, kline.StartTime, kline.EndTime)
|
log.Infof("highdiff: %3.2f ma: %.2f, open: %8v, close: %8v, high: %8v, low: %8v, time: %v %v", s.stdevHigh.Last(), s.ma.Last(), kline.Open, kline.Close, kline.High, kline.Low, kline.StartTime, kline.EndTime)
|
||||||
|
@ -704,18 +648,6 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
s.Market.QuoteCurrency,
|
s.Market.QuoteCurrency,
|
||||||
balances[s.Market.QuoteCurrency].String(),
|
balances[s.Market.QuoteCurrency].String(),
|
||||||
)
|
)
|
||||||
s.DriftFilterPos = s.drift.Filter(func(i int, v float64) bool {
|
|
||||||
return v >= 0
|
|
||||||
}, 50).Mean(50)
|
|
||||||
s.DriftFilterNeg = s.drift.Filter(func(i int, v float64) bool {
|
|
||||||
return v <= 0
|
|
||||||
}, 50).Mean(50)
|
|
||||||
s.DDriftFilterPos = s.drift.drift.Filter(func(i int, v float64) bool {
|
|
||||||
return v >= 0
|
|
||||||
}, 50).Mean(50)
|
|
||||||
s.DDriftFilterNeg = s.drift.drift.Filter(func(i int, v float64) bool {
|
|
||||||
return v <= 0
|
|
||||||
}, 50).Mean(50)
|
|
||||||
|
|
||||||
shortCondition := drift[1] >= 0 && drift[0] <= 0 || (drift[1] >= drift[0] && drift[1] <= 0) || ddrift[1] >= 0 && ddrift[0] <= 0 || (ddrift[1] >= ddrift[0] && ddrift[1] <= 0)
|
shortCondition := drift[1] >= 0 && drift[0] <= 0 || (drift[1] >= drift[0] && drift[1] <= 0) || ddrift[1] >= 0 && ddrift[0] <= 0 || (ddrift[1] >= ddrift[0] && ddrift[1] <= 0)
|
||||||
longCondition := drift[1] <= 0 && drift[0] >= 0 || (drift[1] <= drift[0] && drift[1] >= 0) || ddrift[1] <= 0 && ddrift[0] >= 0 || (ddrift[1] <= ddrift[0] && ddrift[1] >= 0)
|
longCondition := drift[1] <= 0 && drift[0] >= 0 || (drift[1] <= drift[0] && drift[1] >= 0) || ddrift[1] <= 0 && ddrift[0] >= 0 || (ddrift[1] <= ddrift[0] && ddrift[1] >= 0)
|
||||||
|
@ -726,12 +658,9 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
shortCondition = false
|
shortCondition = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exitShortCondition := s.sellPrice > 0 && (s.sellPrice*(1.+stoploss) <= highf ||
|
exitCondition := s.CheckStopLoss() || s.trailingCheck(pricef, "short") || s.trailingCheck(pricef, "long")
|
||||||
s.trailingCheck(pricef, "short"))
|
|
||||||
exitLongCondition := s.buyPrice > 0 && (s.buyPrice*(1.-stoploss) >= lowf ||
|
|
||||||
s.trailingCheck(pricef, "long"))
|
|
||||||
|
|
||||||
if exitShortCondition || exitLongCondition {
|
if exitCondition {
|
||||||
s.positionLock.Unlock()
|
s.positionLock.Unlock()
|
||||||
if err := s.GeneralOrderExecutor.GracefulCancel(ctx); err != nil {
|
if err := s.GeneralOrderExecutor.GracefulCancel(ctx); err != nil {
|
||||||
log.WithError(err).Errorf("cannot cancel orders")
|
log.WithError(err).Errorf("cannot cancel orders")
|
||||||
|
@ -918,8 +847,12 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
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 = 0
|
||||||
|
if s.lowestPrice == 0 {
|
||||||
|
s.lowestPrice = s.sellPrice
|
||||||
|
} else {
|
||||||
s.lowestPrice = math.Min(s.lowestPrice, s.sellPrice)
|
s.lowestPrice = math.Min(s.lowestPrice, s.sellPrice)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
bbgo.Notify("tag: %s, sp: %.4f bp: %.4f hp: %.4f lp: %.4f, trade: %s, pos: %s", tag, s.sellPrice, s.buyPrice, s.highestPrice, s.lowestPrice, trade.String(), s.p.String())
|
bbgo.Notify("tag: %s, sp: %.4f bp: %.4f hp: %.4f lp: %.4f, trade: %s, pos: %s", tag, s.sellPrice, s.buyPrice, s.highestPrice, s.lowestPrice, trade.String(), s.p.String())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user