bollmaker: add shadow protection

This commit is contained in:
c9s 2022-01-16 04:06:19 +08:00
parent 71e660571d
commit a68ad20ddc
2 changed files with 42 additions and 10 deletions

View File

@ -114,6 +114,10 @@ type Strategy struct {
// less than 1.0 means when placing sell order, place buy order with less quantity // less than 1.0 means when placing sell order, place buy order with less quantity
UptrendSkew fixedpoint.Value `json:"uptrendSkew"` UptrendSkew fixedpoint.Value `json:"uptrendSkew"`
// ShadowProtection is used to avoid placing bid order when price goes down strongly (without shadow)
ShadowProtection *bool `json:"shadowProtection"`
ShadowProtectionRatio fixedpoint.Value `json:"shadowProtectionRatio"`
session *bbgo.ExchangeSession session *bbgo.ExchangeSession
book *types.StreamOrderBook book *types.StreamOrderBook
market types.Market market types.Market
@ -247,7 +251,7 @@ func (s *Strategy) LoadState() error {
return nil return nil
} }
func (s *Strategy) placeOrders(ctx context.Context, orderExecutor bbgo.OrderExecutor, midPrice fixedpoint.Value) { func (s *Strategy) placeOrders(ctx context.Context, orderExecutor bbgo.OrderExecutor, midPrice fixedpoint.Value, kline *types.KLine) {
askPrice := midPrice.Mul(one + s.Spread) askPrice := midPrice.Mul(one + s.Spread)
bidPrice := midPrice.Mul(one - s.Spread) bidPrice := midPrice.Mul(one - s.Spread)
base := s.state.Position.GetBase() base := s.state.Position.GetBase()
@ -354,17 +358,33 @@ func (s *Strategy) placeOrders(ctx context.Context, orderExecutor bbgo.OrderExec
// submitOrders = append(submitOrders, buyOrder) // submitOrders = append(submitOrders, buyOrder)
} }
buyOrder = s.adjustOrderQuantity(buyOrder)
sellOrder = s.adjustOrderQuantity(sellOrder)
if canBuy { if canBuy {
submitOrders = append(submitOrders, buyOrder) if s.ShadowProtection != nil && *s.ShadowProtection && kline != nil {
switch kline.Direction() {
case types.DirectionUp:
case types.DirectionDown:
lowerShadowRatio := kline.GetLowerShadowRatio()
if lowerShadowRatio < s.ShadowProtectionRatio.Float64() {
log.Infof("%s shadow protection enabled, lower shadow ratio %f < %f", s.Symbol, lowerShadowRatio, s.ShadowProtectionRatio.Float64())
} else {
submitOrders = append(submitOrders, buyOrder)
}
}
} else {
submitOrders = append(submitOrders, buyOrder)
}
} }
if len(submitOrders) == 0 { if len(submitOrders) == 0 {
return return
} }
for i := range submitOrders {
submitOrders[i] = s.adjustOrderQuantity(submitOrders[i])
}
createdOrders, err := orderExecutor.SubmitOrders(ctx, submitOrders...) createdOrders, err := orderExecutor.SubmitOrders(ctx, submitOrders...)
if err != nil { if err != nil {
log.WithError(err).Errorf("can not place ping pong orders") log.WithError(err).Errorf("can not place ping pong orders")
@ -406,6 +426,16 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
s.DowntrendSkew = fixedpoint.NewFromFloat(1.2) s.DowntrendSkew = fixedpoint.NewFromFloat(1.2)
} }
// enable shadow protection by default
if s.ShadowProtection == nil {
s.ShadowProtection = &[]bool{true}[0]
}
if s.ShadowProtectionRatio == 0 {
// 1%
s.ShadowProtectionRatio = fixedpoint.NewFromFloat(0.02)
}
// initial required information // initial required information
s.session = session s.session = session
@ -475,10 +505,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
midPrice := fixedpoint.NewFromFloat((ticker.Buy + ticker.Sell) / 2) midPrice := fixedpoint.NewFromFloat((ticker.Buy + ticker.Sell) / 2)
s.placeOrders(ctx, orderExecutor, midPrice) s.placeOrders(ctx, orderExecutor, midPrice, nil)
} else { } else {
if price, ok := session.LastPrice(s.Symbol); ok { if price, ok := session.LastPrice(s.Symbol); ok {
s.placeOrders(ctx, orderExecutor, fixedpoint.NewFromFloat(price)) s.placeOrders(ctx, orderExecutor, fixedpoint.NewFromFloat(price), nil)
} }
} }
}) })
@ -504,9 +534,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
midPrice := fixedpoint.NewFromFloat((ticker.Buy + ticker.Sell) / 2) midPrice := fixedpoint.NewFromFloat((ticker.Buy + ticker.Sell) / 2)
s.placeOrders(ctx, orderExecutor, midPrice) s.placeOrders(ctx, orderExecutor, midPrice, &kline)
} else { } else {
s.placeOrders(ctx, orderExecutor, fixedpoint.NewFromFloat(kline.Close)) s.placeOrders(ctx, orderExecutor, fixedpoint.NewFromFloat(kline.Close), &kline)
} }
}) })

View File

@ -154,9 +154,11 @@ func (k KLine) GetLowerShadowRatio() float64 {
func (k KLine) GetLowerShadowHeight() float64 { func (k KLine) GetLowerShadowHeight() float64 {
low := k.Low low := k.Low
if k.Open < k.Close { if k.Open < k.Close { // uptrend
return k.Open - low return k.Open - low
} }
// downtrend
return k.Close - low return k.Close - low
} }