code.vegaprotocol.io/vega@v0.79.0/core/matching/cached_orderbook.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package matching
    17  
    18  import (
    19  	"context"
    20  	"time"
    21  
    22  	"code.vegaprotocol.io/vega/core/events"
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	"code.vegaprotocol.io/vega/libs/num"
    25  	"code.vegaprotocol.io/vega/logging"
    26  )
    27  
    28  type CachedOrderBook struct {
    29  	*OrderBook
    30  	cache BookCache
    31  }
    32  
    33  func NewCachedOrderBook(
    34  	log *logging.Logger, config Config, market string, auction bool, peggedCounterNotify func(int64),
    35  ) *CachedOrderBook {
    36  	return &CachedOrderBook{
    37  		OrderBook: NewOrderBook(log, config, market, auction, peggedCounterNotify),
    38  		cache:     NewBookCache(),
    39  	}
    40  }
    41  
    42  func (b *CachedOrderBook) SetOffbookSource(obs OffbookSource) {
    43  	b.OrderBook.SetOffbookSource(obs)
    44  }
    45  
    46  func (b *CachedOrderBook) LoadState(ctx context.Context, payload *types.Payload) ([]types.StateProvider, error) {
    47  	providers, err := b.OrderBook.LoadState(ctx, payload)
    48  	if err != nil {
    49  		return providers, err
    50  	}
    51  
    52  	if b.auction {
    53  		b.cache.Invalidate()
    54  		b.log.Info("restoring orderbook cache for", logging.String("marketID", b.marketID))
    55  		b.GetIndicativePriceAndVolume()
    56  	}
    57  
    58  	return providers, err
    59  }
    60  
    61  func (b *CachedOrderBook) EnterAuction() []*types.Order {
    62  	b.cache.Invalidate()
    63  	return b.OrderBook.EnterAuction()
    64  }
    65  
    66  func (b *CachedOrderBook) LeaveAuction(
    67  	at time.Time,
    68  ) ([]*types.OrderConfirmation, []*types.Order, error) {
    69  	b.cache.Invalidate()
    70  	return b.OrderBook.LeaveAuction(at)
    71  }
    72  
    73  func (b *CachedOrderBook) CancelAllOrders(
    74  	party string,
    75  ) ([]*types.OrderCancellationConfirmation, error) {
    76  	b.cache.Invalidate()
    77  	return b.OrderBook.CancelAllOrders(party)
    78  }
    79  
    80  func (b *CachedOrderBook) maybeInvalidateDuringAuction(orderID string) {
    81  	bestBid, errBestBid := b.GetBestBidPrice()
    82  	bestAsk, errBestAsk := b.GetBestAskPrice()
    83  	// if any of side have not best price, let's invalidate
    84  	if errBestBid != nil || errBestAsk != nil {
    85  		b.cache.Invalidate()
    86  		return
    87  	}
    88  
    89  	order, ok := b.ordersByID[orderID]
    90  	if !ok {
    91  		b.log.Panic("could not find order in order book", logging.OrderID(orderID))
    92  	}
    93  
    94  	// only invalidate cache if it gets in the
    95  	// uncrossing range
    96  	switch order.Side {
    97  	case types.SideBuy:
    98  		if order.Price.GTE(bestAsk) {
    99  			b.cache.Invalidate()
   100  		}
   101  	case types.SideSell:
   102  		if order.Price.LTE(bestBid) {
   103  			b.cache.Invalidate()
   104  		}
   105  	}
   106  }
   107  
   108  func (b *CachedOrderBook) maybeInvalidateDuringAuctionNewOrder(order *types.Order) {
   109  	bestBid, errBestBid := b.GetBestBidPrice()
   110  	bestAsk, errBestAsk := b.GetBestAskPrice()
   111  	// if any of side have not best price, let's invalidate
   112  	if errBestBid != nil || errBestAsk != nil {
   113  		b.cache.Invalidate()
   114  		return
   115  	}
   116  
   117  	// only invalidate cache if it gets in the
   118  	// uncrossing range
   119  	switch order.Side {
   120  	case types.SideBuy:
   121  		if order.Price.GTE(bestAsk) {
   122  			b.cache.Invalidate()
   123  		}
   124  	case types.SideSell:
   125  		if order.Price.LTE(bestBid) {
   126  			b.cache.Invalidate()
   127  		}
   128  	}
   129  }
   130  
   131  func (b *CachedOrderBook) CancelOrder(order *types.Order) (*types.OrderCancellationConfirmation, error) {
   132  	if !b.InAuction() {
   133  		b.cache.Invalidate()
   134  	} else {
   135  		b.maybeInvalidateDuringAuction(order.ID)
   136  	}
   137  	return b.OrderBook.CancelOrder(order)
   138  }
   139  
   140  func (b *CachedOrderBook) RemoveOrder(order string) (*types.Order, error) {
   141  	if !b.InAuction() {
   142  		b.cache.Invalidate()
   143  	} else {
   144  		b.maybeInvalidateDuringAuction(order)
   145  	}
   146  	return b.OrderBook.RemoveOrder(order)
   147  }
   148  
   149  func (b *CachedOrderBook) AmendOrder(
   150  	originalOrder, amendedOrder *types.Order,
   151  ) error {
   152  	if !b.InAuction() {
   153  		b.cache.Invalidate()
   154  	} else {
   155  		b.maybeInvalidateDuringAuction(amendedOrder.ID)
   156  	}
   157  	return b.OrderBook.AmendOrder(originalOrder, amendedOrder)
   158  }
   159  
   160  func (b *CachedOrderBook) ReplaceOrder(rm, rpl *types.Order) (*types.OrderConfirmation, error) {
   161  	if !b.InAuction() {
   162  		b.cache.Invalidate()
   163  	} else {
   164  		b.maybeInvalidateDuringAuction(rpl.ID)
   165  	}
   166  	return b.OrderBook.ReplaceOrder(rm, rpl)
   167  }
   168  
   169  func (b *CachedOrderBook) SubmitOrder(
   170  	order *types.Order,
   171  ) (*types.OrderConfirmation, error) {
   172  	if !b.InAuction() {
   173  		b.cache.Invalidate()
   174  	} else {
   175  		b.maybeInvalidateDuringAuctionNewOrder(order)
   176  	}
   177  	return b.OrderBook.SubmitOrder(order)
   178  }
   179  
   180  func (b *CachedOrderBook) DeleteOrder(
   181  	order *types.Order,
   182  ) (*types.Order, error) {
   183  	if !b.InAuction() {
   184  		b.cache.Invalidate()
   185  	} else {
   186  		b.maybeInvalidateDuringAuction(order.ID)
   187  	}
   188  	return b.OrderBook.DeleteOrder(order)
   189  }
   190  
   191  func (b *CachedOrderBook) RemoveDistressedOrders(
   192  	parties []events.MarketPosition,
   193  ) ([]*types.Order, error) {
   194  	b.cache.Invalidate()
   195  	return b.OrderBook.RemoveDistressedOrders(parties)
   196  }
   197  
   198  func (b *CachedOrderBook) GetIndicativePriceAndVolume() (*num.Uint, uint64, types.Side) {
   199  	price, cachedPriceOk := b.cache.GetIndicativePrice()
   200  	volume, cachedVolOk := b.cache.GetIndicativeVolume()
   201  	side, cachedSideOk := b.cache.GetIndicativeUncrossingSide()
   202  	if !cachedPriceOk || !cachedVolOk || !cachedSideOk {
   203  		r := b.OrderBook.GetIndicativePriceAndVolume()
   204  		price, volume, side = r.price, r.volume, r.side
   205  
   206  		b.cache.SetIndicativePrice(price.Clone())
   207  		b.cache.SetIndicativeVolume(volume)
   208  		b.cache.SetIndicativeUncrossingSide(side)
   209  	}
   210  	return price, volume, side
   211  }
   212  
   213  func (b *CachedOrderBook) GetIndicativePrice() *num.Uint {
   214  	price, ok := b.cache.GetIndicativePrice()
   215  
   216  	if !ok {
   217  		price = b.OrderBook.GetIndicativePrice()
   218  		b.cache.SetIndicativePrice(price.Clone())
   219  	}
   220  	return price
   221  }
   222  
   223  func (b *CachedOrderBook) UpdateAMM(party string) {
   224  	if !b.auction {
   225  		return
   226  	}
   227  
   228  	b.cache.Invalidate()
   229  	b.OrderBook.UpdateAMM(party)
   230  }