mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 16:25:16 +00:00
bollmaker: add shadow protection config
This commit is contained in:
parent
a68ad20ddc
commit
5c0e3a1254
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
const CancelOrderWaitTime = 10 * time.Millisecond
|
||||
const SentOrderWaitTime = 10 * time.Millisecond
|
||||
const SentOrderWaitTime = 50 * time.Millisecond
|
||||
const CancelOrderWaitTime = 20 * time.Millisecond
|
||||
|
||||
// LocalActiveOrderBook manages the local active order books.
|
||||
//go:generate callbackgen -type LocalActiveOrderBook
|
||||
|
@ -81,7 +81,7 @@ func (b *LocalActiveOrderBook) GracefulCancel(ctx context.Context, ex types.Exch
|
|||
// Some orders in the variable are not created on the server side yet,
|
||||
// If we cancel these orders directly, we will get an unsent order error
|
||||
// We wait here for a while for server to create these orders.
|
||||
time.Sleep(SentOrderWaitTime)
|
||||
// time.Sleep(SentOrderWaitTime)
|
||||
|
||||
// since ctx might be canceled, we should use background context here
|
||||
if err := ex.CancelOrders(context.Background(), orders...); err != nil {
|
||||
|
|
|
@ -115,7 +115,7 @@ type Strategy struct {
|
|||
UptrendSkew fixedpoint.Value `json:"uptrendSkew"`
|
||||
|
||||
// ShadowProtection is used to avoid placing bid order when price goes down strongly (without shadow)
|
||||
ShadowProtection *bool `json:"shadowProtection"`
|
||||
ShadowProtection bool `json:"shadowProtection"`
|
||||
ShadowProtectionRatio fixedpoint.Value `json:"shadowProtectionRatio"`
|
||||
|
||||
session *bbgo.ExchangeSession
|
||||
|
@ -293,6 +293,26 @@ func (s *Strategy) placeOrders(ctx context.Context, orderExecutor bbgo.OrderExec
|
|||
canBuy := hasQuoteBalance && quoteBalance.Available > s.Quantity.Mul(midPrice) && (s.MaxExposurePosition > 0 && base < s.MaxExposurePosition)
|
||||
canSell := hasBaseBalance && baseBalance.Available > s.Quantity && (s.MaxExposurePosition > 0 && base > -s.MaxExposurePosition)
|
||||
|
||||
if s.ShadowProtection && kline != nil {
|
||||
|
||||
switch kline.Direction() {
|
||||
case types.DirectionDown:
|
||||
shadowHeight := kline.GetLowerShadowHeight()
|
||||
shadowRatio := kline.GetLowerShadowRatio()
|
||||
if shadowHeight == 0.0 && shadowRatio < s.ShadowProtectionRatio.Float64() {
|
||||
log.Infof("%s shadow protection enabled, lower shadow ratio %f < %f", s.Symbol, shadowRatio, s.ShadowProtectionRatio.Float64())
|
||||
canBuy = false
|
||||
}
|
||||
case types.DirectionUp:
|
||||
shadowHeight := kline.GetUpperShadowHeight()
|
||||
shadowRatio := kline.GetUpperShadowRatio()
|
||||
if shadowHeight == 0.0 || shadowRatio < s.ShadowProtectionRatio.Float64() {
|
||||
log.Infof("%s shadow protection enabled, upper shadow ratio %f < %f", s.Symbol, shadowRatio, s.ShadowProtectionRatio.Float64())
|
||||
canSell = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjust quantity for closing position if we over sold or over bought
|
||||
if s.MaxExposurePosition > 0 && base.Abs() > s.MaxExposurePosition {
|
||||
scale := &bbgo.ExponentialScale{
|
||||
|
@ -315,67 +335,39 @@ func (s *Strategy) placeOrders(ctx context.Context, orderExecutor bbgo.OrderExec
|
|||
|
||||
if midPrice.Float64() > s.neutralBoll.LastDownBand() && midPrice.Float64() < s.neutralBoll.LastUpBand() {
|
||||
// we don't have position yet
|
||||
if base == 0 || base.Abs() < minQuantity {
|
||||
// place orders on both side if it's in oscillating band
|
||||
if canBuy {
|
||||
submitOrders = append(submitOrders, buyOrder)
|
||||
}
|
||||
if !s.DisableShort && canSell {
|
||||
submitOrders = append(submitOrders, sellOrder)
|
||||
}
|
||||
}
|
||||
// place orders on both side if it's in oscillating band
|
||||
// if base == 0 || base.Abs() < minQuantity { }
|
||||
} else if midPrice.Float64() > s.defaultBoll.LastDownBand() && midPrice.Float64() < s.neutralBoll.LastDownBand() { // downtrend, might bounce back
|
||||
|
||||
skew := s.DowntrendSkew.Float64()
|
||||
ratio := 1.0 / skew
|
||||
sellOrder.Quantity = math.Max(s.market.MinQuantity, buyOrder.Quantity*ratio)
|
||||
|
||||
} else if midPrice.Float64() < s.defaultBoll.LastUpBand() && midPrice.Float64() > s.neutralBoll.LastUpBand() { // uptrend, might bounce back
|
||||
|
||||
skew := s.UptrendSkew.Float64()
|
||||
buyOrder.Quantity = math.Max(s.market.MinQuantity, sellOrder.Quantity*skew)
|
||||
|
||||
} else if midPrice.Float64() < s.defaultBoll.LastDownBand() { // strong downtrend
|
||||
|
||||
skew := s.StrongDowntrendSkew.Float64()
|
||||
ratio := 1.0 / skew
|
||||
sellOrder.Quantity = math.Max(s.market.MinQuantity, buyOrder.Quantity*ratio)
|
||||
|
||||
} else if midPrice.Float64() > s.defaultBoll.LastUpBand() { // strong uptrend
|
||||
|
||||
skew := s.StrongUptrendSkew.Float64()
|
||||
buyOrder.Quantity = math.Max(s.market.MinQuantity, sellOrder.Quantity*skew)
|
||||
|
||||
}
|
||||
|
||||
if midPrice > s.state.Position.AverageCost.MulFloat64(1.0+s.MinProfitSpread.Float64()) && canSell {
|
||||
if canSell && midPrice > s.state.Position.AverageCost.MulFloat64(1.0+s.MinProfitSpread.Float64()) {
|
||||
if !(s.DisableShort && (base.Float64()-sellOrder.Quantity < 0)) {
|
||||
submitOrders = append(submitOrders, sellOrder)
|
||||
}
|
||||
}
|
||||
|
||||
if midPrice < s.state.Position.AverageCost.MulFloat64(1.0-s.MinProfitSpread.Float64()) && canBuy {
|
||||
// submitOrders = append(submitOrders, buyOrder)
|
||||
if canBuy {
|
||||
submitOrders = append(submitOrders, buyOrder)
|
||||
}
|
||||
|
||||
if canBuy {
|
||||
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 {
|
||||
// condition for lower the average cost
|
||||
/*
|
||||
if midPrice < s.state.Position.AverageCost.MulFloat64(1.0-s.MinProfitSpread.Float64()) && canBuy {
|
||||
submitOrders = append(submitOrders, buyOrder)
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
if len(submitOrders) == 0 {
|
||||
return
|
||||
|
@ -426,14 +418,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
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)
|
||||
s.ShadowProtectionRatio = fixedpoint.NewFromFloat(0.01)
|
||||
}
|
||||
|
||||
// initial required information
|
||||
|
|
|
@ -131,6 +131,11 @@ func (k KLine) GetMaxChange() float64 {
|
|||
return k.GetHigh() - k.GetLow()
|
||||
}
|
||||
|
||||
func (k KLine) GetAmplification() float64 {
|
||||
return k.GetMaxChange() / k.GetLow()
|
||||
}
|
||||
|
||||
|
||||
// GetThickness returns the thickness of the kline. 1 => thick, 0.1 => thin
|
||||
func (k KLine) GetThickness() float64 {
|
||||
return math.Abs(k.GetChange()) / math.Abs(k.GetMaxChange())
|
||||
|
@ -292,6 +297,10 @@ func (k KLineWindow) GetMaxChange() float64 {
|
|||
return k.GetHigh() - k.GetLow()
|
||||
}
|
||||
|
||||
func (k KLineWindow) GetAmplification() float64 {
|
||||
return k.GetMaxChange() / k.GetLow()
|
||||
}
|
||||
|
||||
func (k KLineWindow) AllDrop() bool {
|
||||
for _, n := range k {
|
||||
if n.Direction() >= 0 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user