Merge pull request #953 from zenixls2/fix/drift

fix: drift minus weight, preloaded kline not enough
This commit is contained in:
Yo-An Lin 2022-09-17 18:15:44 +08:00 committed by GitHub
commit 3230088f9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 34 deletions

View File

@ -26,42 +26,42 @@ exchangeStrategies:
canvasPath: "./output.png"
symbol: BTCUSDT
# kline interval for indicators
interval: 15m
interval: 4m
window: 1
stoploss: 1.2%
source: close
stoploss: 0.22%
source: hl2
predictOffset: 2
noTrailingStopLoss: false
trailingStopLossType: kline
trailingStopLossType: realtime
# stddev on high/low-source
hlVarianceMultiplier: 0.1
hlVarianceMultiplier: 0.01
hlRangeWindow: 5
smootherWindow: 1
fisherTransformWindow: 34
smootherWindow: 2
fisherTransformWindow: 27
window1m: 58
smootherWindow1m: 118
fisherTransformWindow1m: 319
atrWindow: 14
# orders not been traded will be canceled after `pendingMinutes` minutes
pendingMinutes: 10
pendingMinutes: 2
noRebalance: true
trendWindow: 576
rebalanceFilter: 0
driftFilterPos: 1.8
driftFilterNeg: -1.8
ddriftFilterPos: 0.5
ddriftFilterNeg: -0.5 #-1.6
driftFilterPos: 0.6
driftFilterNeg: -0.6
ddriftFilterPos: 0.00008
ddriftFilterNeg: -0.00008
# ActivationRatio should be increasing order
# 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.001, 0.0081, 0.022]
trailingActivationRatio: [0.0012, 0.01]
trailingActivationRatio: [0.0012, 0.0016, 0.01]
#trailingActivationRatio: []
#trailingCallbackRate: []
#trailingCallbackRate: [0.002, 0.01, 0.1]
#trailingCallbackRate: [0.0004, 0.0009, 0.018]
trailingCallbackRate: [0.0006, 0.0049]
trailingCallbackRate: [0.0003, 0.0006, 0.0019]
generateGraph: true
graphPNLDeductFee: false
@ -125,7 +125,7 @@ sync:
backtest:
startTime: "2022-09-01"
endTime: "2022-09-15"
endTime: "2022-09-30"
symbols:
- BTCUSDT
sessions: [binance]

View File

@ -23,19 +23,23 @@ type WeightedDrift struct {
}
func (inc *WeightedDrift) Update(value float64, weight float64) {
win := 10
if inc.Window > win {
win = inc.Window
}
if inc.chng == nil {
inc.SeriesBase.Series = inc
if inc.MA == nil {
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.LastValue = value
inc.Weight.Update(weight)
return
}
inc.Weight.Update(weight)
base := inc.Weight.Lowest(10)
base := inc.Weight.Lowest(win)
multiplier := int(weight / base)
var chng float64
if value == 0 {
@ -119,7 +123,7 @@ func (inc *WeightedDrift) Length() int {
var _ types.SeriesExtend = &Drift{}
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) {

View File

@ -126,7 +126,13 @@ func (s *Strategy) InstanceID() string {
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
// by default, bbgo only pre-subscribe 1000 klines.
// 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{
Interval: types.Interval1m,
})
@ -218,7 +224,7 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
s.ma.Update(source)
s.stdevHigh.Update(high - s.ma.Last())
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.atr.PushK(kline)
s.priceLines.Update(source)
@ -233,7 +239,7 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
}
for _, kline := range *klines {
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() {
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())
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("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("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)
@ -556,7 +568,7 @@ func (s *Strategy) CalcAssetValue(price fixedpoint.Value) fixedpoint.Value {
func (s *Strategy) klineHandler1m(ctx context.Context, kline types.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 {
return
}
@ -575,6 +587,11 @@ func (s *Strategy) klineHandler1m(ctx context.Context, kline types.KLine) {
if s.highestPrice > 0 && highf > s.highestPrice {
s.highestPrice = highf
}
drift := s.drift1m.Array(2)
if len(drift) < 2 {
s.positionLock.Unlock()
return
}
numPending := 0
var err error
@ -617,17 +634,10 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
s.priceLines.Update(sourcef)
s.ma.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)
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)
ddriftPred := s.drift.drift.Predict(s.PredictOffset)
atr = s.atr.Last()
@ -639,6 +649,14 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
s.stdevLow.Update(lowdiff)
highdiff := highf - s.ma.Last()
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 {
return
@ -646,7 +664,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
stoploss := s.StopLoss.Float64()
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 {
s.lowestPrice = lowf
}