mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +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"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CancelOrderWaitTime = 10 * time.Millisecond
|
const SentOrderWaitTime = 50 * time.Millisecond
|
||||||
const SentOrderWaitTime = 10 * time.Millisecond
|
const CancelOrderWaitTime = 20 * time.Millisecond
|
||||||
|
|
||||||
// LocalActiveOrderBook manages the local active order books.
|
// LocalActiveOrderBook manages the local active order books.
|
||||||
//go:generate callbackgen -type LocalActiveOrderBook
|
//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,
|
// 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
|
// 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.
|
// 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
|
// since ctx might be canceled, we should use background context here
|
||||||
if err := ex.CancelOrders(context.Background(), orders...); err != nil {
|
if err := ex.CancelOrders(context.Background(), orders...); err != nil {
|
||||||
|
|
|
@ -115,7 +115,7 @@ type Strategy struct {
|
||||||
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 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"`
|
ShadowProtectionRatio fixedpoint.Value `json:"shadowProtectionRatio"`
|
||||||
|
|
||||||
session *bbgo.ExchangeSession
|
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)
|
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)
|
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
|
// adjust quantity for closing position if we over sold or over bought
|
||||||
if s.MaxExposurePosition > 0 && base.Abs() > s.MaxExposurePosition {
|
if s.MaxExposurePosition > 0 && base.Abs() > s.MaxExposurePosition {
|
||||||
scale := &bbgo.ExponentialScale{
|
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() {
|
if midPrice.Float64() > s.neutralBoll.LastDownBand() && midPrice.Float64() < s.neutralBoll.LastUpBand() {
|
||||||
// we don't have position yet
|
// we don't have position yet
|
||||||
if base == 0 || base.Abs() < minQuantity {
|
// place orders on both side if it's in oscillating band
|
||||||
// place orders on both side if it's in oscillating band
|
// if base == 0 || base.Abs() < minQuantity { }
|
||||||
if canBuy {
|
|
||||||
submitOrders = append(submitOrders, buyOrder)
|
|
||||||
}
|
|
||||||
if !s.DisableShort && canSell {
|
|
||||||
submitOrders = append(submitOrders, sellOrder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if midPrice.Float64() > s.defaultBoll.LastDownBand() && midPrice.Float64() < s.neutralBoll.LastDownBand() { // downtrend, might bounce back
|
} else if midPrice.Float64() > s.defaultBoll.LastDownBand() && midPrice.Float64() < s.neutralBoll.LastDownBand() { // downtrend, might bounce back
|
||||||
|
|
||||||
skew := s.DowntrendSkew.Float64()
|
skew := s.DowntrendSkew.Float64()
|
||||||
ratio := 1.0 / skew
|
ratio := 1.0 / skew
|
||||||
sellOrder.Quantity = math.Max(s.market.MinQuantity, buyOrder.Quantity*ratio)
|
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
|
} else if midPrice.Float64() < s.defaultBoll.LastUpBand() && midPrice.Float64() > s.neutralBoll.LastUpBand() { // uptrend, might bounce back
|
||||||
|
|
||||||
skew := s.UptrendSkew.Float64()
|
skew := s.UptrendSkew.Float64()
|
||||||
buyOrder.Quantity = math.Max(s.market.MinQuantity, sellOrder.Quantity*skew)
|
buyOrder.Quantity = math.Max(s.market.MinQuantity, sellOrder.Quantity*skew)
|
||||||
|
|
||||||
} else if midPrice.Float64() < s.defaultBoll.LastDownBand() { // strong downtrend
|
} else if midPrice.Float64() < s.defaultBoll.LastDownBand() { // strong downtrend
|
||||||
|
|
||||||
skew := s.StrongDowntrendSkew.Float64()
|
skew := s.StrongDowntrendSkew.Float64()
|
||||||
ratio := 1.0 / skew
|
ratio := 1.0 / skew
|
||||||
sellOrder.Quantity = math.Max(s.market.MinQuantity, buyOrder.Quantity*ratio)
|
sellOrder.Quantity = math.Max(s.market.MinQuantity, buyOrder.Quantity*ratio)
|
||||||
|
|
||||||
} else if midPrice.Float64() > s.defaultBoll.LastUpBand() { // strong uptrend
|
} else if midPrice.Float64() > s.defaultBoll.LastUpBand() { // strong uptrend
|
||||||
|
|
||||||
skew := s.StrongUptrendSkew.Float64()
|
skew := s.StrongUptrendSkew.Float64()
|
||||||
buyOrder.Quantity = math.Max(s.market.MinQuantity, sellOrder.Quantity*skew)
|
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)) {
|
if !(s.DisableShort && (base.Float64()-sellOrder.Quantity < 0)) {
|
||||||
submitOrders = append(submitOrders, sellOrder)
|
submitOrders = append(submitOrders, sellOrder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if canBuy {
|
||||||
if midPrice < s.state.Position.AverageCost.MulFloat64(1.0-s.MinProfitSpread.Float64()) && canBuy {
|
submitOrders = append(submitOrders, buyOrder)
|
||||||
// submitOrders = append(submitOrders, buyOrder)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if canBuy {
|
// condition for lower the average cost
|
||||||
if s.ShadowProtection != nil && *s.ShadowProtection && kline != nil {
|
/*
|
||||||
switch kline.Direction() {
|
if midPrice < s.state.Position.AverageCost.MulFloat64(1.0-s.MinProfitSpread.Float64()) && canBuy {
|
||||||
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)
|
submitOrders = append(submitOrders, buyOrder)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
if len(submitOrders) == 0 {
|
if len(submitOrders) == 0 {
|
||||||
return
|
return
|
||||||
|
@ -426,14 +418,8 @@ 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 {
|
if s.ShadowProtectionRatio == 0 {
|
||||||
// 1%
|
s.ShadowProtectionRatio = fixedpoint.NewFromFloat(0.01)
|
||||||
s.ShadowProtectionRatio = fixedpoint.NewFromFloat(0.02)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial required information
|
// initial required information
|
||||||
|
|
|
@ -131,6 +131,11 @@ func (k KLine) GetMaxChange() float64 {
|
||||||
return k.GetHigh() - k.GetLow()
|
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
|
// GetThickness returns the thickness of the kline. 1 => thick, 0.1 => thin
|
||||||
func (k KLine) GetThickness() float64 {
|
func (k KLine) GetThickness() float64 {
|
||||||
return math.Abs(k.GetChange()) / math.Abs(k.GetMaxChange())
|
return math.Abs(k.GetChange()) / math.Abs(k.GetMaxChange())
|
||||||
|
@ -292,6 +297,10 @@ func (k KLineWindow) GetMaxChange() float64 {
|
||||||
return k.GetHigh() - k.GetLow()
|
return k.GetHigh() - k.GetLow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k KLineWindow) GetAmplification() float64 {
|
||||||
|
return k.GetMaxChange() / k.GetLow()
|
||||||
|
}
|
||||||
|
|
||||||
func (k KLineWindow) AllDrop() bool {
|
func (k KLineWindow) AllDrop() bool {
|
||||||
for _, n := range k {
|
for _, n := range k {
|
||||||
if n.Direction() >= 0 {
|
if n.Direction() >= 0 {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user