From 888a550c80a5d95177106f9e8bbec57bc79ba11b Mon Sep 17 00:00:00 2001 From: c9s Date: Thu, 30 Nov 2023 17:10:09 +0800 Subject: [PATCH] xdepthmaker: support partial maker order replenish --- pkg/strategy/xdepthmaker/strategy.go | 53 +++++++++++++++++++---- pkg/strategy/xdepthmaker/strategy_test.go | 2 +- pkg/types/ordermap.go | 13 ++++++ pkg/types/price_volume_heartbeat.go | 4 ++ pkg/types/price_volume_slice.go | 20 +++++++++ 5 files changed, 82 insertions(+), 10 deletions(-) diff --git a/pkg/strategy/xdepthmaker/strategy.go b/pkg/strategy/xdepthmaker/strategy.go index 4b5ebe197..9169faadf 100644 --- a/pkg/strategy/xdepthmaker/strategy.go +++ b/pkg/strategy/xdepthmaker/strategy.go @@ -2,6 +2,7 @@ package xdepthmaker import ( "context" + stderrors "errors" "fmt" "sync" "time" @@ -381,8 +382,10 @@ func (s *Strategy) CrossRun( switch sig.Type { case types.BookSignalSnapshot: - case types.BookSignalUpdate: + s.updateQuote(ctx, 0) + case types.BookSignalUpdate: + s.updateQuote(ctx, 5) } case <-posTicker.C: @@ -581,7 +584,7 @@ func (s *Strategy) runTradeRecover(ctx context.Context) { } } -func (s *Strategy) generateMakerOrders(pricingBook *types.StreamOrderBook) ([]types.SubmitOrder, error) { +func (s *Strategy) generateMakerOrders(pricingBook *types.StreamOrderBook, maxLayer int) ([]types.SubmitOrder, error) { bestBid, bestAsk, hasPrice := pricingBook.BestBidAndAsk() if !hasPrice { return nil, nil @@ -600,8 +603,12 @@ func (s *Strategy) generateMakerOrders(pricingBook *types.StreamOrderBook) ([]ty dupPricingBook := pricingBook.CopyDepth(0) + if maxLayer == 0 || maxLayer > s.NumLayers { + maxLayer = s.NumLayers + } + for _, side := range []types.SideType{types.SideTypeBuy, types.SideTypeSell} { - for i := 1; i <= s.NumLayers; i++ { + for i := 1; i <= maxLayer; i++ { requiredDepthFloat, err := s.DepthScale.Scale(i) if err != nil { return nil, errors.Wrapf(err, "depthScale scale error") @@ -679,11 +686,31 @@ func (s *Strategy) generateMakerOrders(pricingBook *types.StreamOrderBook) ([]ty return submitOrders, nil } -func (s *Strategy) updateQuote(ctx context.Context) { - if err := s.MakerOrderExecutor.GracefulCancel(ctx); err != nil { - log.Warnf("there are some %s orders not canceled, skipping placing maker orders", s.Symbol) - s.MakerOrderExecutor.ActiveMakerOrders().Print() - return +func (s *Strategy) partiallyCancelOrders(ctx context.Context, maxLayer int) error { + buyOrders, sellOrders := s.MakerOrderExecutor.ActiveMakerOrders().Orders().SeparateBySide() + buyOrders = types.SortOrdersByPrice(buyOrders, true) + sellOrders = types.SortOrdersByPrice(sellOrders, false) + + buyOrdersToCancel := buyOrders[0:min(maxLayer, len(buyOrders))] + sellOrdersToCancel := sellOrders[0:min(maxLayer, len(sellOrders))] + + err1 := s.MakerOrderExecutor.GracefulCancel(ctx, buyOrdersToCancel...) + err2 := s.MakerOrderExecutor.GracefulCancel(ctx, sellOrdersToCancel...) + return stderrors.Join(err1, err2) +} + +func (s *Strategy) updateQuote(ctx context.Context, maxLayer int) { + if maxLayer == 0 { + if err := s.MakerOrderExecutor.GracefulCancel(ctx); err != nil { + log.WithError(err).Warnf("there are some %s orders not canceled, skipping placing maker orders", s.Symbol) + s.MakerOrderExecutor.ActiveMakerOrders().Print() + return + } + } else { + if err := s.partiallyCancelOrders(ctx, maxLayer); err != nil { + log.WithError(err).Warnf("%s partial order cancel failed", s.Symbol) + return + } } numOfMakerOrders := s.MakerOrderExecutor.ActiveMakerOrders().NumOfOrders() @@ -719,7 +746,7 @@ func (s *Strategy) updateQuote(ctx context.Context) { return } - submitOrders, err := s.generateMakerOrders(s.pricingBook) + submitOrders, err := s.generateMakerOrders(s.pricingBook, maxLayer) if err != nil { log.WithError(err).Errorf("generate order error") return @@ -771,3 +798,11 @@ func averageDepthPrice(pvs types.PriceVolumeSlice) (price fixedpoint.Value, err price = totalQuoteAmount.Div(totalQuantity) return price, nil } + +func min(a, b int) int { + if a < b { + return a + } + + return b +} diff --git a/pkg/strategy/xdepthmaker/strategy_test.go b/pkg/strategy/xdepthmaker/strategy_test.go index 10ccced40..5f40af7f6 100644 --- a/pkg/strategy/xdepthmaker/strategy_test.go +++ b/pkg/strategy/xdepthmaker/strategy_test.go @@ -59,7 +59,7 @@ func TestStrategy_generateMakerOrders(t *testing.T) { Time: time.Now(), }) - orders, err := s.generateMakerOrders(pricingBook) + orders, err := s.generateMakerOrders(pricingBook, 0) assert.NoError(t, err) AssertOrdersPriceSideQuantity(t, []PriceSideQuantityAssert{ {Side: types.SideTypeBuy, Price: Number("25000"), Quantity: Number("0.04")}, // =~ $1000.00 diff --git a/pkg/types/ordermap.go b/pkg/types/ordermap.go index e7cfca1a4..a086d12ed 100644 --- a/pkg/types/ordermap.go +++ b/pkg/types/ordermap.go @@ -243,3 +243,16 @@ func (m *SyncOrderMap) Orders() (slice OrderSlice) { } type OrderSlice []Order + +func (s OrderSlice) SeparateBySide() (buyOrders, sellOrders []Order) { + for _, o := range s { + switch o.Side { + case SideTypeBuy: + buyOrders = append(buyOrders, o) + case SideTypeSell: + sellOrders = append(sellOrders, o) + } + } + + return buyOrders, sellOrders +} diff --git a/pkg/types/price_volume_heartbeat.go b/pkg/types/price_volume_heartbeat.go index 9f249a67a..17c3ed55d 100644 --- a/pkg/types/price_volume_heartbeat.go +++ b/pkg/types/price_volume_heartbeat.go @@ -18,6 +18,10 @@ func NewPriceHeartBeat(timeout time.Duration) *PriceHeartBeat { } } +func (b *PriceHeartBeat) Last() PriceVolume { + return b.last +} + // Update updates the price volume object and the last update time // It returns (bool, error), when the price is successfully updated, it returns true. // If the price is not updated (same price) and the last time exceeded the timeout, diff --git a/pkg/types/price_volume_slice.go b/pkg/types/price_volume_slice.go index 1aa1756fc..53fb7d913 100644 --- a/pkg/types/price_volume_slice.go +++ b/pkg/types/price_volume_slice.go @@ -82,6 +82,26 @@ func (slice PriceVolumeSlice) IndexByQuoteVolumeDepth(requiredQuoteVolume fixedp return -1 } +func (slice PriceVolumeSlice) SumDepth() fixedpoint.Value { + var total = fixedpoint.Zero + for _, pv := range slice { + total = total.Add(pv.Volume) + } + + return total +} + +func (slice PriceVolumeSlice) SumDepthInQuote() fixedpoint.Value { + var total = fixedpoint.Zero + + for _, pv := range slice { + quoteVolume := fixedpoint.Mul(pv.Price, pv.Volume) + total = total.Add(quoteVolume) + } + + return total +} + func (slice PriceVolumeSlice) IndexByVolumeDepth(requiredVolume fixedpoint.Value) int { var tv = fixedpoint.Zero for x, el := range slice {