xmaker: improve bollinger band trend detection

This commit is contained in:
c9s 2024-08-27 17:30:43 +08:00
parent 4ae8ad77b3
commit f81ce5ce95
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54

View File

@ -192,6 +192,93 @@ func (s *Strategy) Initialize() error {
return nil return nil
} }
type Quote struct {
BestBidPrice, BestAskPrice fixedpoint.Value
BidMargin, AskMargin fixedpoint.Value
// BidLayerPips is the price pips between each layer
BidLayerPips, AskLayerPips fixedpoint.Value
}
// getBollingerTrend returns -1 when the price is in the downtrend, 1 when the price is in the uptrend, 0 when the price is in the band
func (s *Strategy) getBollingerTrend(quote *Quote) int {
// when bid price is lower than the down band, then it's in the downtrend
// when ask price is higher than the up band, then it's in the uptrend
lastDownBand := fixedpoint.NewFromFloat(s.boll.DownBand.Last(0))
lastUpBand := fixedpoint.NewFromFloat(s.boll.UpBand.Last(0))
log.Infof("bollinger band: up/down = %f/%f, bid/ask = %f/%f",
lastUpBand.Float64(),
lastDownBand.Float64(),
quote.BestBidPrice.Float64(),
quote.BestAskPrice.Float64())
if quote.BestAskPrice.Compare(lastDownBand) < 0 {
return -1
} else if quote.BestBidPrice.Compare(lastUpBand) > 0 {
return 1
} else {
return 0
}
}
// applyBollingerMargin applies the bollinger band margin to the quote
func (s *Strategy) applyBollingerMargin(
quote *Quote,
) error {
lastDownBand := fixedpoint.NewFromFloat(s.boll.DownBand.Last(0))
lastUpBand := fixedpoint.NewFromFloat(s.boll.UpBand.Last(0))
if lastUpBand.IsZero() || lastDownBand.IsZero() {
log.Warnf("bollinger band value is zero, skipping")
return nil
}
factor := fixedpoint.Min(s.BollBandMarginFactor, fixedpoint.One)
switch s.getBollingerTrend(quote) {
case -1:
// for the downtrend, increase the bid margin
// ratio here should be greater than 1.00
ratio := fixedpoint.Min(lastDownBand.Div(quote.BestAskPrice), fixedpoint.One)
// so that 1.x can multiply the original bid margin
bollMargin := s.BollBandMargin.Mul(ratio).Mul(factor)
log.Infof("%s bollband downtrend: increasing bid margin %f (bidMargin) + %f (bollMargin) = %f (finalBidMargin)",
s.Symbol,
quote.BidMargin.Float64(),
bollMargin.Float64(),
quote.BidMargin.Add(bollMargin).Float64())
quote.BidMargin = quote.BidMargin.Add(bollMargin)
quote.BidLayerPips = quote.BidLayerPips.Mul(ratio)
case 1:
// for the uptrend, increase the ask margin
// ratio here should be greater than 1.00
ratio := fixedpoint.Min(quote.BestAskPrice.Div(lastUpBand), fixedpoint.One)
// so that the original bid margin can be multiplied by 1.x
bollMargin := s.BollBandMargin.Mul(ratio).Mul(factor)
log.Infof("%s bollband uptrend adjusting bid margin %f (askMargin) + %f (bollMargin) = %f (finalAskMargin)",
s.Symbol,
quote.AskMargin.Float64(),
bollMargin.Float64(),
quote.AskMargin.Add(bollMargin).Float64())
quote.AskMargin = quote.AskMargin.Add(bollMargin)
quote.AskLayerPips = quote.AskLayerPips.Mul(ratio)
default:
// default, in the band
}
return nil
}
func (s *Strategy) updateQuote(ctx context.Context) { func (s *Strategy) updateQuote(ctx context.Context) {
if err := s.activeMakerOrders.GracefulCancel(ctx, s.makerSession.Exchange); err != nil { if err := s.activeMakerOrders.GracefulCancel(ctx, s.makerSession.Exchange); err != nil {
log.Warnf("there are some %s orders not canceled, skipping placing maker orders", s.Symbol) log.Warnf("there are some %s orders not canceled, skipping placing maker orders", s.Symbol)
@ -340,55 +427,19 @@ func (s *Strategy) updateQuote(ctx context.Context) {
var accumulativeBidQuantity, accumulativeAskQuantity fixedpoint.Value var accumulativeBidQuantity, accumulativeAskQuantity fixedpoint.Value
var bidQuantity = s.Quantity var bidQuantity = s.Quantity
var askQuantity = s.Quantity var askQuantity = s.Quantity
var bidMargin = s.BidMargin
var askMargin = s.AskMargin var quote = &Quote{
var pips = s.Pips BestBidPrice: bestBidPrice,
BestAskPrice: bestAskPrice,
BidMargin: s.BidMargin,
AskMargin: s.AskMargin,
BidLayerPips: s.Pips,
AskLayerPips: s.Pips,
}
if s.EnableBollBandMargin { if s.EnableBollBandMargin {
lastDownBand := fixedpoint.NewFromFloat(s.boll.DownBand.Last(0)) if err := s.applyBollingerMargin(quote); err != nil {
lastUpBand := fixedpoint.NewFromFloat(s.boll.UpBand.Last(0)) log.WithError(err).Errorf("unable to apply bollinger margin")
if lastUpBand.IsZero() || lastDownBand.IsZero() {
log.Warnf("bollinger band value is zero, skipping")
return
}
log.Infof("bollinger band: up/down = %f/%f", lastUpBand.Float64(), lastDownBand.Float64())
// when bid price is lower than the down band, then it's in the downtrend
// when ask price is higher than the up band, then it's in the uptrend
if bestBidPrice.Compare(lastDownBand) < 0 {
// ratio here should be greater than 1.00
ratio := lastDownBand.Div(bestBidPrice)
// so that the original bid margin can be multiplied by 1.x
bollMargin := s.BollBandMargin.Mul(ratio).Mul(s.BollBandMarginFactor)
log.Infof("%s bollband downtrend: adjusting ask margin %v + %v = %v",
s.Symbol,
askMargin,
bollMargin,
askMargin.Add(bollMargin))
askMargin = askMargin.Add(bollMargin)
pips = pips.Mul(ratio)
}
if bestAskPrice.Compare(lastUpBand) > 0 {
// ratio here should be greater than 1.00
ratio := bestAskPrice.Div(lastUpBand)
// so that the original bid margin can be multiplied by 1.x
bollMargin := s.BollBandMargin.Mul(ratio).Mul(s.BollBandMarginFactor)
log.Infof("%s bollband uptrend adjusting bid margin %v + %v = %v",
s.Symbol,
bidMargin,
bollMargin,
bidMargin.Add(bollMargin))
bidMargin = bidMargin.Add(bollMargin)
pips = pips.Mul(ratio)
} }
} }
@ -401,11 +452,11 @@ func (s *Strategy) updateQuote(ctx context.Context) {
bidExposureInUsd := fixedpoint.Zero bidExposureInUsd := fixedpoint.Zero
askExposureInUsd := fixedpoint.Zero askExposureInUsd := fixedpoint.Zero
bidPrice := bestBidPrice bidPrice := quote.BestBidPrice
askPrice := bestAskPrice askPrice := quote.BestAskPrice
bidMarginMetrics.With(labels).Set(bidMargin.Float64()) bidMarginMetrics.With(labels).Set(quote.BidMargin.Float64())
askMarginMetrics.With(labels).Set(askMargin.Float64()) askMarginMetrics.With(labels).Set(quote.AskMargin.Float64())
for i := 0; i < s.NumLayers; i++ { for i := 0; i < s.NumLayers; i++ {
// for maker bid orders // for maker bid orders
@ -425,19 +476,20 @@ func (s *Strategy) updateQuote(ctx context.Context) {
accumulativeBidQuantity = accumulativeBidQuantity.Add(bidQuantity) accumulativeBidQuantity = accumulativeBidQuantity.Add(bidQuantity)
if s.UseDepthPrice { if s.UseDepthPrice {
sideBook := sourceBook.SideBook(types.SideTypeBuy)
if s.DepthQuantity.Sign() > 0 { if s.DepthQuantity.Sign() > 0 {
bidPrice = aggregatePrice(sourceBook.SideBook(types.SideTypeBuy), s.DepthQuantity) bidPrice = aggregatePrice(sideBook, s.DepthQuantity)
} else { } else {
bidPrice = aggregatePrice(sourceBook.SideBook(types.SideTypeBuy), accumulativeBidQuantity) bidPrice = aggregatePrice(sideBook, accumulativeBidQuantity)
} }
} }
bidPrice = bidPrice.Mul(fixedpoint.One.Sub(bidMargin))
if i == 0 { if i == 0 {
bidPrice = bidPrice.Mul(fixedpoint.One.Sub(quote.BidMargin))
makerBestBidPriceMetrics.With(labels).Set(bidPrice.Float64()) makerBestBidPriceMetrics.With(labels).Set(bidPrice.Float64())
} else if i > 0 && pips.Sign() > 0 { } else if i > 0 && quote.BidLayerPips.Sign() > 0 {
bidPrice = bidPrice.Sub(pips.Mul(fixedpoint.NewFromInt(int64(i)). pips := quote.BidLayerPips.Mul(s.makerMarket.TickSize)
Mul(s.makerMarket.TickSize))) bidPrice = bidPrice.Sub(pips)
} }
if makerQuota.QuoteAsset.Lock(bidQuantity.Mul(bidPrice)) && hedgeQuota.BaseAsset.Lock(bidQuantity) { if makerQuota.QuoteAsset.Lock(bidQuantity.Mul(bidPrice)) && hedgeQuota.BaseAsset.Lock(bidQuantity) {
@ -489,11 +541,12 @@ func (s *Strategy) updateQuote(ctx context.Context) {
} }
} }
askPrice = askPrice.Mul(fixedpoint.One.Add(askMargin))
if i == 0 { if i == 0 {
askPrice = askPrice.Mul(fixedpoint.One.Add(quote.AskMargin))
makerBestAskPriceMetrics.With(labels).Set(askPrice.Float64()) makerBestAskPriceMetrics.With(labels).Set(askPrice.Float64())
} else if i > 0 && pips.Sign() > 0 { } else if i > 0 && quote.AskLayerPips.Sign() > 0 {
askPrice = askPrice.Add(pips.Mul(fixedpoint.NewFromInt(int64(i)).Mul(s.makerMarket.TickSize))) pips := quote.AskLayerPips.Mul(s.makerMarket.TickSize)
askPrice = askPrice.Add(pips)
} }
if makerQuota.BaseAsset.Lock(askQuantity) && hedgeQuota.QuoteAsset.Lock(askQuantity.Mul(askPrice)) { if makerQuota.BaseAsset.Lock(askQuantity) && hedgeQuota.QuoteAsset.Lock(askQuantity.Mul(askPrice)) {