xmaker: implement tryArbitrage

This commit is contained in:
c9s 2024-09-09 17:49:53 +08:00 committed by lychiyu
parent 5df06c2c2c
commit 0eb6808ad4

View File

@ -546,22 +546,6 @@ func (s *Strategy) updateQuote(ctx context.Context) error {
)
}
if s.EnableArbitrage {
if makerBid, makerAsk, ok := s.makerBook.BestBidAndAsk(); ok {
if makerAsk.Price.Compare(bestBid.Price) <= 0 {
askPvs := s.makerBook.SideBook(types.SideTypeSell)
for _, pv := range askPvs {
if pv.Price.Compare(bestBid.Price) <= 0 {
}
}
// send ioc order for arbitrage
} else if makerBid.Price.Compare(bestAsk.Price) >= 0 {
// send ioc order for arbitrage
}
}
}
// use mid-price for the last price
s.lastPrice = bestBid.Price.Add(bestAsk.Price).Div(two)
@ -785,12 +769,19 @@ func (s *Strategy) updateQuote(ctx context.Context) error {
bidExposureInUsd := fixedpoint.Zero
askExposureInUsd := fixedpoint.Zero
bidPrice := quote.BestBidPrice
askPrice := quote.BestAskPrice
bidMarginMetrics.With(s.metricsLabels).Set(quote.BidMargin.Float64())
askMarginMetrics.With(s.metricsLabels).Set(quote.AskMargin.Float64())
if s.EnableArbitrage {
done, err := s.tryArbitrage(ctx, quote)
if err != nil {
s.logger.WithError(err).Errorf("unable to arbitrage")
} else if done {
return nil
}
}
if !disableMakerBid {
for i := 0; i < s.NumLayers; i++ {
bidQuantity, err := s.getInitialLayerQuantity(i)
@ -810,7 +801,7 @@ func (s *Strategy) updateQuote(ctx context.Context) error {
}
}
bidPrice = s.getLayerPrice(i, types.SideTypeBuy, s.sourceBook, quote, requiredDepth)
bidPrice := s.getLayerPrice(i, types.SideTypeBuy, s.sourceBook, quote, requiredDepth)
if i == 0 {
s.logger.Infof("maker best bid price %f", bidPrice.Float64())
@ -859,7 +850,7 @@ func (s *Strategy) updateQuote(ctx context.Context) error {
}
}
askPrice = s.getLayerPrice(i, types.SideTypeSell, s.sourceBook, quote, requiredDepth)
askPrice := s.getLayerPrice(i, types.SideTypeSell, s.sourceBook, quote, requiredDepth)
if i == 0 {
s.logger.Infof("maker best ask price %f", askPrice.Float64())
@ -904,14 +895,9 @@ func (s *Strategy) updateQuote(ctx context.Context) error {
return err
}
orderCreateCallback := func(createdOrder types.Order) {
s.orderStore.Add(createdOrder)
s.activeMakerOrders.Add(createdOrder)
}
defer s.tradeCollector.Process()
createdOrders, errIdx, err := bbgo.BatchPlaceOrder(ctx, s.makerSession.Exchange, orderCreateCallback, formattedOrders...)
createdOrders, errIdx, err := bbgo.BatchPlaceOrder(ctx, s.makerSession.Exchange, s.makerOrderCreateCallback, formattedOrders...)
if err != nil {
log.WithError(err).Errorf("unable to place maker orders: %+v", formattedOrders)
return err
@ -925,6 +911,88 @@ func (s *Strategy) updateQuote(ctx context.Context) error {
return nil
}
func (s *Strategy) makerOrderCreateCallback(createdOrder types.Order) {
s.orderStore.Add(createdOrder)
s.activeMakerOrders.Add(createdOrder)
}
func aggregatePriceVolumeSliceWithPriceFilter(pvs types.PriceVolumeSlice, filterPrice fixedpoint.Value) types.PriceVolume {
var totalVolume = fixedpoint.Zero
var lastPrice = fixedpoint.Zero
for _, pv := range pvs {
if pv.Price.Compare(filterPrice) > 0 {
break
}
lastPrice = pv.Price
totalVolume = totalVolume.Add(pv.Volume)
}
return types.PriceVolume{
Price: lastPrice,
Volume: totalVolume,
}
}
// tryArbitrage tries to arbitrage between the source and maker exchange
func (s *Strategy) tryArbitrage(ctx context.Context, quote *Quote) (bool, error) {
marginBidPrice := quote.BestBidPrice.Mul(fixedpoint.One.Sub(quote.BidMargin))
marginAskPrice := quote.BestAskPrice.Mul(fixedpoint.One.Add(quote.BidMargin))
var iocOrders []types.SubmitOrder
if makerBid, makerAsk, ok := s.makerBook.BestBidAndAsk(); ok {
if makerAsk.Price.Compare(marginBidPrice) <= 0 {
askPvs := s.makerBook.SideBook(types.SideTypeSell)
sumPv := aggregatePriceVolumeSliceWithPriceFilter(askPvs, marginBidPrice)
iocOrders = append(iocOrders, types.SubmitOrder{
Symbol: s.Symbol,
Type: types.OrderTypeLimit,
Side: types.SideTypeBuy,
Price: sumPv.Price,
Quantity: sumPv.Volume,
TimeInForce: types.TimeInForceIOC,
})
} else if makerBid.Price.Compare(marginAskPrice) >= 0 {
askPvs := s.makerBook.SideBook(types.SideTypeSell)
sumPv := aggregatePriceVolumeSliceWithPriceFilter(askPvs, marginBidPrice)
// send ioc order for arbitrage
iocOrders = append(iocOrders, types.SubmitOrder{
Symbol: s.Symbol,
Type: types.OrderTypeLimit,
Side: types.SideTypeSell,
Price: sumPv.Price,
Quantity: sumPv.Volume,
TimeInForce: types.TimeInForceIOC,
})
}
if len(iocOrders) == 0 {
return false, nil
}
// send ioc order for arbitrage
formattedOrders, err := s.makerSession.FormatOrders(iocOrders)
if err != nil {
return false, err
}
defer s.tradeCollector.Process()
createdOrders, _, err := bbgo.BatchPlaceOrder(ctx, s.makerSession.Exchange, s.makerOrderCreateCallback, formattedOrders...)
if err != nil {
return false, err
}
s.logger.Infof("sent arbitrage orders: %+v", createdOrders)
return true, nil
}
return false, nil
}
func (s *Strategy) adjustHedgeQuantityWithAvailableBalance(
account *types.Account, side types.SideType, quantity, lastPrice fixedpoint.Value,
) fixedpoint.Value {