mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Merge pull request #1719 from c9s/c9s/xmaker/stb-improvements
IMPROVE: [xmaker] fix bollinger band price calculation
This commit is contained in:
commit
652c9b62e8
|
@ -26,7 +26,6 @@ var defaultMargin = fixedpoint.NewFromFloat(0.003)
|
||||||
var two = fixedpoint.NewFromInt(2)
|
var two = fixedpoint.NewFromInt(2)
|
||||||
|
|
||||||
var lastPriceModifier = fixedpoint.NewFromFloat(1.001)
|
var lastPriceModifier = fixedpoint.NewFromFloat(1.001)
|
||||||
var minGap = fixedpoint.NewFromFloat(1.02)
|
|
||||||
|
|
||||||
const priceUpdateTimeout = 30 * time.Second
|
const priceUpdateTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
@ -134,6 +133,8 @@ type Strategy struct {
|
||||||
|
|
||||||
reportProfitStatsRateLimiter *rate.Limiter
|
reportProfitStatsRateLimiter *rate.Limiter
|
||||||
circuitBreakerAlertLimiter *rate.Limiter
|
circuitBreakerAlertLimiter *rate.Limiter
|
||||||
|
|
||||||
|
logger logrus.FieldLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) ID() string {
|
func (s *Strategy) ID() string {
|
||||||
|
@ -189,12 +190,105 @@ func aggregatePrice(pvs types.PriceVolumeSlice, requiredQuantity fixedpoint.Valu
|
||||||
func (s *Strategy) Initialize() error {
|
func (s *Strategy) Initialize() error {
|
||||||
s.bidPriceHeartBeat = types.NewPriceHeartBeat(priceUpdateTimeout)
|
s.bidPriceHeartBeat = types.NewPriceHeartBeat(priceUpdateTimeout)
|
||||||
s.askPriceHeartBeat = types.NewPriceHeartBeat(priceUpdateTimeout)
|
s.askPriceHeartBeat = types.NewPriceHeartBeat(priceUpdateTimeout)
|
||||||
|
|
||||||
|
s.logger = logrus.WithFields(logrus.Fields{
|
||||||
|
"symbol": s.Symbol,
|
||||||
|
"strategy": ID,
|
||||||
|
"strategy_id": s.InstanceID(),
|
||||||
|
})
|
||||||
|
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))
|
||||||
|
|
||||||
|
s.logger.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() {
|
||||||
|
s.logger.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)
|
||||||
|
|
||||||
|
s.logger.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)
|
||||||
|
|
||||||
|
s.logger.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
|
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)
|
s.logger.Warnf("there are some %s orders not canceled, skipping placing maker orders", s.Symbol)
|
||||||
s.activeMakerOrders.Print()
|
s.activeMakerOrders.Print()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -206,7 +300,7 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
if s.CircuitBreaker != nil {
|
if s.CircuitBreaker != nil {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if reason, halted := s.CircuitBreaker.IsHalted(now); halted {
|
if reason, halted := s.CircuitBreaker.IsHalted(now); halted {
|
||||||
log.Warnf("[arbWorker] strategy is halted, reason: %s", reason)
|
s.logger.Warnf("[arbWorker] strategy is halted, reason: %s", reason)
|
||||||
|
|
||||||
if s.circuitBreakerAlertLimiter.AllowN(now, 1) {
|
if s.circuitBreakerAlertLimiter.AllowN(now, 1) {
|
||||||
bbgo.Notify("Strategy is halted, reason: %s", reason)
|
bbgo.Notify("Strategy is halted, reason: %s", reason)
|
||||||
|
@ -229,14 +323,14 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
bookLastUpdateTime := s.book.LastUpdateTime()
|
bookLastUpdateTime := s.book.LastUpdateTime()
|
||||||
|
|
||||||
if _, err := s.bidPriceHeartBeat.Update(bestBid); err != nil {
|
if _, err := s.bidPriceHeartBeat.Update(bestBid); err != nil {
|
||||||
log.WithError(err).Errorf("quote update error, %s price not updating, order book last update: %s ago",
|
s.logger.WithError(err).Errorf("quote update error, %s price not updating, order book last update: %s ago",
|
||||||
s.Symbol,
|
s.Symbol,
|
||||||
time.Since(bookLastUpdateTime))
|
time.Since(bookLastUpdateTime))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := s.askPriceHeartBeat.Update(bestAsk); err != nil {
|
if _, err := s.askPriceHeartBeat.Update(bestAsk); err != nil {
|
||||||
log.WithError(err).Errorf("quote update error, %s price not updating, order book last update: %s ago",
|
s.logger.WithError(err).Errorf("quote update error, %s price not updating, order book last update: %s ago",
|
||||||
s.Symbol,
|
s.Symbol,
|
||||||
time.Since(bookLastUpdateTime))
|
time.Since(bookLastUpdateTime))
|
||||||
return
|
return
|
||||||
|
@ -244,7 +338,7 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
|
|
||||||
sourceBook := s.book.CopyDepth(10)
|
sourceBook := s.book.CopyDepth(10)
|
||||||
if valid, err := sourceBook.IsValid(); !valid {
|
if valid, err := sourceBook.IsValid(); !valid {
|
||||||
log.WithError(err).Errorf("%s invalid copied order book, skip quoting: %v", s.Symbol, err)
|
s.logger.WithError(err).Errorf("%s invalid copied order book, skip quoting: %v", s.Symbol, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,13 +376,13 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
if b.Available.Compare(minAvailable) > 0 {
|
if b.Available.Compare(minAvailable) > 0 {
|
||||||
hedgeQuota.BaseAsset.Add(b.Available.Sub(minAvailable))
|
hedgeQuota.BaseAsset.Add(b.Available.Sub(minAvailable))
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("%s maker bid disabled: insufficient base balance %s", s.Symbol, b.String())
|
s.logger.Warnf("%s maker bid disabled: insufficient base balance %s", s.Symbol, b.String())
|
||||||
disableMakerBid = true
|
disableMakerBid = true
|
||||||
}
|
}
|
||||||
} else if b.Available.Compare(s.sourceMarket.MinQuantity) > 0 {
|
} else if b.Available.Compare(s.sourceMarket.MinQuantity) > 0 {
|
||||||
hedgeQuota.BaseAsset.Add(b.Available)
|
hedgeQuota.BaseAsset.Add(b.Available)
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("%s maker bid disabled: insufficient base balance %s", s.Symbol, b.String())
|
s.logger.Warnf("%s maker bid disabled: insufficient base balance %s", s.Symbol, b.String())
|
||||||
disableMakerBid = true
|
disableMakerBid = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,13 +395,13 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
if b.Available.Compare(minAvailable) > 0 {
|
if b.Available.Compare(minAvailable) > 0 {
|
||||||
hedgeQuota.QuoteAsset.Add(b.Available.Sub(minAvailable))
|
hedgeQuota.QuoteAsset.Add(b.Available.Sub(minAvailable))
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("%s maker ask disabled: insufficient quote balance %s", s.Symbol, b.String())
|
s.logger.Warnf("%s maker ask disabled: insufficient quote balance %s", s.Symbol, b.String())
|
||||||
disableMakerAsk = true
|
disableMakerAsk = true
|
||||||
}
|
}
|
||||||
} else if b.Available.Compare(s.sourceMarket.MinNotional) > 0 {
|
} else if b.Available.Compare(s.sourceMarket.MinNotional) > 0 {
|
||||||
hedgeQuota.QuoteAsset.Add(b.Available)
|
hedgeQuota.QuoteAsset.Add(b.Available)
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("%s maker ask disabled: insufficient quote balance %s", s.Symbol, b.String())
|
s.logger.Warnf("%s maker ask disabled: insufficient quote balance %s", s.Symbol, b.String())
|
||||||
disableMakerAsk = true
|
disableMakerAsk = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,61 +428,33 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
|
|
||||||
bestBidPrice := bestBid.Price
|
bestBidPrice := bestBid.Price
|
||||||
bestAskPrice := bestAsk.Price
|
bestAskPrice := bestAsk.Price
|
||||||
log.Infof("%s book ticker: best ask / best bid = %v / %v", s.Symbol, bestAskPrice, bestBidPrice)
|
s.logger.Infof("%s book ticker: best ask / best bid = %v / %v", s.Symbol, bestAskPrice, bestBidPrice)
|
||||||
|
|
||||||
|
if bestBidPrice.Compare(bestAskPrice) > 0 {
|
||||||
|
log.Errorf("best bid price %f is higher than best ask price %f, skip quoting",
|
||||||
|
bestBidPrice.Float64(),
|
||||||
|
bestAskPrice.Float64(),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var submitOrders []types.SubmitOrder
|
var submitOrders []types.SubmitOrder
|
||||||
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 +467,19 @@ 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())
|
if bidPrice.Compare(askPrice) > 0 {
|
||||||
askMarginMetrics.With(labels).Set(askMargin.Float64())
|
log.Errorf("maker bid price %f is higher than maker ask price %f, skip quoting",
|
||||||
|
bidPrice.Float64(),
|
||||||
|
askPrice.Float64(),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bidMarginMetrics.With(labels).Set(quote.BidMargin.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
|
||||||
|
@ -424,20 +498,32 @@ 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)
|
if i == 0 {
|
||||||
|
bidPrice = aggregatePrice(sideBook, s.DepthQuantity)
|
||||||
|
bidPrice = bidPrice.Mul(fixedpoint.One.Sub(quote.BidMargin))
|
||||||
|
} else if i > 0 && quote.BidLayerPips.Sign() > 0 {
|
||||||
|
pips := quote.BidLayerPips.Mul(s.makerMarket.TickSize)
|
||||||
|
bidPrice = bidPrice.Sub(pips)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
bidPrice = aggregatePrice(sourceBook.SideBook(types.SideTypeBuy), accumulativeBidQuantity)
|
bidPrice = aggregatePrice(sideBook, accumulativeBidQuantity)
|
||||||
|
bidPrice = bidPrice.Mul(fixedpoint.One.Sub(quote.BidMargin))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if i == 0 {
|
||||||
|
bidPrice = bidPrice.Mul(fixedpoint.One.Sub(quote.BidMargin))
|
||||||
|
} else if i > 0 && quote.BidLayerPips.Sign() > 0 {
|
||||||
|
pips := quote.BidLayerPips.Mul(s.makerMarket.TickSize)
|
||||||
|
bidPrice = bidPrice.Sub(pips)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bidPrice = bidPrice.Mul(fixedpoint.One.Sub(bidMargin))
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
makerBestBidPriceMetrics.With(labels).Set(bidPrice.Float64())
|
makerBestBidPriceMetrics.With(labels).Set(bidPrice.Float64())
|
||||||
} else if i > 0 && pips.Sign() > 0 {
|
|
||||||
bidPrice = bidPrice.Sub(pips.Mul(fixedpoint.NewFromInt(int64(i)).
|
|
||||||
Mul(s.makerMarket.TickSize)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if makerQuota.QuoteAsset.Lock(bidQuantity.Mul(bidPrice)) && hedgeQuota.BaseAsset.Lock(bidQuantity) {
|
if makerQuota.QuoteAsset.Lock(bidQuantity.Mul(bidPrice)) && hedgeQuota.BaseAsset.Lock(bidQuantity) {
|
||||||
|
@ -483,17 +569,28 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
|
|
||||||
if s.UseDepthPrice {
|
if s.UseDepthPrice {
|
||||||
if s.DepthQuantity.Sign() > 0 {
|
if s.DepthQuantity.Sign() > 0 {
|
||||||
|
if i == 0 {
|
||||||
askPrice = aggregatePrice(sourceBook.SideBook(types.SideTypeSell), s.DepthQuantity)
|
askPrice = aggregatePrice(sourceBook.SideBook(types.SideTypeSell), s.DepthQuantity)
|
||||||
|
askPrice = askPrice.Mul(fixedpoint.One.Add(quote.AskMargin))
|
||||||
|
} else if i > 0 && quote.AskLayerPips.Sign() > 0 {
|
||||||
|
pips := quote.AskLayerPips.Mul(s.makerMarket.TickSize)
|
||||||
|
askPrice = askPrice.Add(pips)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
askPrice = aggregatePrice(sourceBook.SideBook(types.SideTypeSell), accumulativeAskQuantity)
|
askPrice = aggregatePrice(sourceBook.SideBook(types.SideTypeSell), accumulativeAskQuantity)
|
||||||
|
askPrice = askPrice.Mul(fixedpoint.One.Add(quote.AskMargin))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if i == 0 {
|
||||||
|
askPrice = askPrice.Mul(fixedpoint.One.Add(quote.AskMargin))
|
||||||
|
} else if i > 0 && quote.AskLayerPips.Sign() > 0 {
|
||||||
|
pips := quote.AskLayerPips.Mul(s.makerMarket.TickSize)
|
||||||
|
askPrice = askPrice.Add(pips)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
askPrice = askPrice.Mul(fixedpoint.One.Add(askMargin))
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
makerBestAskPriceMetrics.With(labels).Set(askPrice.Float64())
|
makerBestAskPriceMetrics.With(labels).Set(askPrice.Float64())
|
||||||
} else if i > 0 && pips.Sign() > 0 {
|
|
||||||
askPrice = askPrice.Add(pips.Mul(fixedpoint.NewFromInt(int64(i)).Mul(s.makerMarket.TickSize)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if makerQuota.BaseAsset.Lock(askQuantity) && hedgeQuota.QuoteAsset.Lock(askQuantity.Mul(askPrice)) {
|
if makerQuota.BaseAsset.Lock(askQuantity) && hedgeQuota.QuoteAsset.Lock(askQuantity.Mul(askPrice)) {
|
||||||
|
@ -612,13 +709,8 @@ func (s *Strategy) Hedge(ctx context.Context, pos fixedpoint.Value) {
|
||||||
// truncate quantity for the supported precision
|
// truncate quantity for the supported precision
|
||||||
quantity = s.sourceMarket.TruncateQuantity(quantity)
|
quantity = s.sourceMarket.TruncateQuantity(quantity)
|
||||||
|
|
||||||
if notional.Compare(s.sourceMarket.MinNotional.Mul(minGap)) <= 0 {
|
if s.sourceMarket.IsDustQuantity(quantity, lastPrice) {
|
||||||
log.Warnf("the adjusted amount %v is less than minimal notional %v, skipping hedge", notional, s.sourceMarket.MinNotional)
|
log.Warnf("skip dust quantity: %s", quantity.String())
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if quantity.Compare(s.sourceMarket.MinQuantity.Mul(minGap)) <= 0 {
|
|
||||||
log.Warnf("the adjusted quantity %v is less than minimal quantity %v, skipping hedge", quantity, s.sourceMarket.MinQuantity)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,7 +847,7 @@ func (s *Strategy) Defaults() error {
|
||||||
|
|
||||||
// circuitBreakerAlertLimiter is for CircuitBreaker alerts
|
// circuitBreakerAlertLimiter is for CircuitBreaker alerts
|
||||||
s.circuitBreakerAlertLimiter = rate.NewLimiter(rate.Every(3*time.Minute), 2)
|
s.circuitBreakerAlertLimiter = rate.NewLimiter(rate.Every(3*time.Minute), 2)
|
||||||
s.reportProfitStatsRateLimiter = rate.NewLimiter(rate.Every(5*time.Minute), 1)
|
s.reportProfitStatsRateLimiter = rate.NewLimiter(rate.Every(3*time.Minute), 1)
|
||||||
s.hedgeErrorLimiter = rate.NewLimiter(rate.Every(1*time.Minute), 1)
|
s.hedgeErrorLimiter = rate.NewLimiter(rate.Every(1*time.Minute), 1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -808,6 +900,9 @@ func (s *Strategy) hedgeWorker(ctx context.Context) {
|
||||||
ticker := time.NewTicker(util.MillisecondsJitter(s.HedgeInterval.Duration(), 200))
|
ticker := time.NewTicker(util.MillisecondsJitter(s.HedgeInterval.Duration(), 200))
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
profitChanged := false
|
||||||
|
reportTicker := time.NewTicker(5 * time.Minute)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -841,9 +936,16 @@ func (s *Strategy) hedgeWorker(ctx context.Context) {
|
||||||
s.Hedge(ctx, uncoverPosition.Neg())
|
s.Hedge(ctx, uncoverPosition.Neg())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profitChanged = true
|
||||||
|
|
||||||
|
case <-reportTicker.C:
|
||||||
|
if profitChanged {
|
||||||
if s.reportProfitStatsRateLimiter.Allow() {
|
if s.reportProfitStatsRateLimiter.Allow() {
|
||||||
bbgo.Notify(s.ProfitStats)
|
bbgo.Notify(s.ProfitStats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profitChanged = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user