mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
fix: drift minus weight, preloaded kline not enough
This commit is contained in:
parent
44de961ea1
commit
7044b0d8ea
|
@ -26,42 +26,42 @@ exchangeStrategies:
|
||||||
canvasPath: "./output.png"
|
canvasPath: "./output.png"
|
||||||
symbol: BTCUSDT
|
symbol: BTCUSDT
|
||||||
# kline interval for indicators
|
# kline interval for indicators
|
||||||
interval: 15m
|
interval: 4m
|
||||||
window: 1
|
window: 1
|
||||||
stoploss: 1.2%
|
stoploss: 0.22%
|
||||||
source: close
|
source: hl2
|
||||||
predictOffset: 2
|
predictOffset: 2
|
||||||
noTrailingStopLoss: false
|
noTrailingStopLoss: false
|
||||||
trailingStopLossType: kline
|
trailingStopLossType: realtime
|
||||||
# stddev on high/low-source
|
# stddev on high/low-source
|
||||||
hlVarianceMultiplier: 0.1
|
hlVarianceMultiplier: 0.01
|
||||||
hlRangeWindow: 5
|
hlRangeWindow: 5
|
||||||
smootherWindow: 1
|
smootherWindow: 2
|
||||||
fisherTransformWindow: 34
|
fisherTransformWindow: 27
|
||||||
window1m: 58
|
window1m: 58
|
||||||
smootherWindow1m: 118
|
smootherWindow1m: 118
|
||||||
fisherTransformWindow1m: 319
|
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: 10
|
pendingMinutes: 2
|
||||||
noRebalance: true
|
noRebalance: true
|
||||||
trendWindow: 576
|
trendWindow: 576
|
||||||
rebalanceFilter: 0
|
rebalanceFilter: 0
|
||||||
driftFilterPos: 1.8
|
driftFilterPos: 0.6
|
||||||
driftFilterNeg: -1.8
|
driftFilterNeg: -0.6
|
||||||
ddriftFilterPos: 0.5
|
ddriftFilterPos: 0.00008
|
||||||
ddriftFilterNeg: -0.5 #-1.6
|
ddriftFilterNeg: -0.00008
|
||||||
|
|
||||||
# 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.0012, 0.01]
|
trailingActivationRatio: [0.0012, 0.0016, 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.0006, 0.0049]
|
trailingCallbackRate: [0.0003, 0.0006, 0.0019]
|
||||||
|
|
||||||
generateGraph: true
|
generateGraph: true
|
||||||
graphPNLDeductFee: false
|
graphPNLDeductFee: false
|
||||||
|
@ -125,7 +125,7 @@ sync:
|
||||||
|
|
||||||
backtest:
|
backtest:
|
||||||
startTime: "2022-09-01"
|
startTime: "2022-09-01"
|
||||||
endTime: "2022-09-15"
|
endTime: "2022-09-30"
|
||||||
symbols:
|
symbols:
|
||||||
- BTCUSDT
|
- BTCUSDT
|
||||||
sessions: [binance]
|
sessions: [binance]
|
||||||
|
|
|
@ -23,19 +23,23 @@ type WeightedDrift struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WeightedDrift) Update(value float64, weight float64) {
|
func (inc *WeightedDrift) Update(value float64, weight float64) {
|
||||||
|
win := 10
|
||||||
|
if inc.Window > win {
|
||||||
|
win = inc.Window
|
||||||
|
}
|
||||||
if inc.chng == nil {
|
if inc.chng == nil {
|
||||||
inc.SeriesBase.Series = inc
|
inc.SeriesBase.Series = inc
|
||||||
if inc.MA == nil {
|
if inc.MA == nil {
|
||||||
inc.MA = &SMA{IntervalWindow: types.IntervalWindow{Interval: inc.Interval, Window: inc.Window}}
|
inc.MA = &SMA{IntervalWindow: types.IntervalWindow{Interval: inc.Interval, Window: inc.Window}}
|
||||||
}
|
}
|
||||||
inc.Weight = types.NewQueue(10)
|
inc.Weight = types.NewQueue(win)
|
||||||
inc.chng = types.NewQueue(inc.Window)
|
inc.chng = types.NewQueue(inc.Window)
|
||||||
inc.LastValue = value
|
inc.LastValue = value
|
||||||
inc.Weight.Update(weight)
|
inc.Weight.Update(weight)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
inc.Weight.Update(weight)
|
inc.Weight.Update(weight)
|
||||||
base := inc.Weight.Lowest(10)
|
base := inc.Weight.Lowest(win)
|
||||||
multiplier := int(weight / base)
|
multiplier := int(weight / base)
|
||||||
var chng float64
|
var chng float64
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
|
@ -119,7 +123,7 @@ func (inc *WeightedDrift) Length() int {
|
||||||
var _ types.SeriesExtend = &Drift{}
|
var _ types.SeriesExtend = &Drift{}
|
||||||
|
|
||||||
func (inc *WeightedDrift) PushK(k types.KLine) {
|
func (inc *WeightedDrift) PushK(k types.KLine) {
|
||||||
inc.Update(k.Close.Float64(), k.Volume.Float64())
|
inc.Update(k.Close.Float64(), k.Volume.Abs().Float64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WeightedDrift) CalculateAndUpdate(allKLines []types.KLine) {
|
func (inc *WeightedDrift) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
|
|
|
@ -126,7 +126,13 @@ func (s *Strategy) InstanceID() string {
|
||||||
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
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
|
||||||
bbgo.KLinePreloadLimit = int64((s.Interval.Minutes()*s.Window/1000 + 1) * 1000)
|
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)
|
||||||
|
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{
|
||||||
Interval: types.Interval1m,
|
Interval: types.Interval1m,
|
||||||
})
|
})
|
||||||
|
@ -218,7 +224,7 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
|
||||||
s.ma.Update(source)
|
s.ma.Update(source)
|
||||||
s.stdevHigh.Update(high - s.ma.Last())
|
s.stdevHigh.Update(high - s.ma.Last())
|
||||||
s.stdevLow.Update(s.ma.Last() - low)
|
s.stdevLow.Update(s.ma.Last() - low)
|
||||||
s.drift.Update(source, kline.Volume.Float64())
|
s.drift.Update(source, kline.Volume.Abs().Float64())
|
||||||
s.trendLine.Update(source)
|
s.trendLine.Update(source)
|
||||||
s.atr.PushK(kline)
|
s.atr.PushK(kline)
|
||||||
s.priceLines.Update(source)
|
s.priceLines.Update(source)
|
||||||
|
@ -233,7 +239,7 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
|
||||||
}
|
}
|
||||||
for _, kline := range *klines {
|
for _, kline := range *klines {
|
||||||
source := s.GetSource(&kline).Float64()
|
source := s.GetSource(&kline).Float64()
|
||||||
s.drift1m.Update(source, kline.Volume.Float64())
|
s.drift1m.Update(source, kline.Volume.Abs().Float64())
|
||||||
if s.drift1m.Last() != s.drift1m.Last() {
|
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))
|
panic(fmt.Sprintf("%f %v %f %f", source, s.drift1m.drift.Values.Index(1), s.drift1m.ma2.Last(), s.drift1m.drift.LastValue))
|
||||||
}
|
}
|
||||||
|
@ -411,9 +417,15 @@ func (s *Strategy) DrawIndicators(time types.Time) *types.Canvas {
|
||||||
h1m := s.drift1m.Abs().Highest(Length * s.Interval.Minutes())
|
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)
|
||||||
|
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("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("drift1m", s.drift1m.Mul(highestPrice/h1m).Add(mean), time, Length*s.Interval.Minutes(), types.Interval1m)
|
||||||
|
@ -556,7 +568,7 @@ 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.Float64())
|
s.drift1m.Update(s.GetSource(&kline).Float64(), kline.Volume.Abs().Float64())
|
||||||
if s.Status != types.StrategyStatusRunning {
|
if s.Status != types.StrategyStatusRunning {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -575,6 +587,11 @@ 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
|
||||||
|
@ -617,17 +634,10 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
s.priceLines.Update(sourcef)
|
s.priceLines.Update(sourcef)
|
||||||
s.ma.Update(sourcef)
|
s.ma.Update(sourcef)
|
||||||
s.trendLine.Update(sourcef)
|
s.trendLine.Update(sourcef)
|
||||||
s.drift.Update(sourcef, kline.Volume.Float64())
|
s.drift.Update(sourcef, kline.Volume.Abs().Float64())
|
||||||
|
|
||||||
s.atr.PushK(kline)
|
s.atr.PushK(kline)
|
||||||
drift = s.drift.Array(2)
|
|
||||||
if len(drift) < 2 || len(drift) < s.PredictOffset {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ddrift := s.drift.drift.Array(2)
|
|
||||||
if len(ddrift) < 2 || len(ddrift) < s.PredictOffset {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
driftPred = s.drift.Predict(s.PredictOffset)
|
driftPred = s.drift.Predict(s.PredictOffset)
|
||||||
ddriftPred := s.drift.drift.Predict(s.PredictOffset)
|
ddriftPred := s.drift.drift.Predict(s.PredictOffset)
|
||||||
atr = s.atr.Last()
|
atr = s.atr.Last()
|
||||||
|
@ -639,6 +649,14 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
s.stdevLow.Update(lowdiff)
|
s.stdevLow.Update(lowdiff)
|
||||||
highdiff := highf - s.ma.Last()
|
highdiff := highf - s.ma.Last()
|
||||||
s.stdevHigh.Update(highdiff)
|
s.stdevHigh.Update(highdiff)
|
||||||
|
drift = s.drift.Array(2)
|
||||||
|
if len(drift) < 2 || len(drift) < s.PredictOffset {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ddrift := s.drift.drift.Array(2)
|
||||||
|
if len(ddrift) < 2 || len(ddrift) < s.PredictOffset {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if s.Status != types.StrategyStatusRunning {
|
if s.Status != types.StrategyStatusRunning {
|
||||||
return
|
return
|
||||||
|
@ -646,7 +664,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
stoploss := s.StopLoss.Float64()
|
stoploss := s.StopLoss.Float64()
|
||||||
|
|
||||||
s.positionLock.Lock()
|
s.positionLock.Lock()
|
||||||
log.Errorf("highdiff: %3.2f ma: %.2f, close: %8v, high: %8v, low: %8v, time: %v %v", s.stdevHigh.Last(), s.ma.Last(), kline.Close, kline.High, kline.Low, kline.StartTime, kline.EndTime)
|
log.Infof("highdiff: %3.2f ma: %.2f, close: %8v, high: %8v, low: %8v, time: %v %v", s.stdevHigh.Last(), s.ma.Last(), kline.Close, kline.High, kline.Low, kline.StartTime, kline.EndTime)
|
||||||
if s.lowestPrice > 0 && lowf < s.lowestPrice {
|
if s.lowestPrice > 0 && lowf < s.lowestPrice {
|
||||||
s.lowestPrice = lowf
|
s.lowestPrice = lowf
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user