code.vegaprotocol.io/vega@v0.79.0/core/matching/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  	"fmt"
    20  	"sort"
    21  	"sync"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/events"
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	"code.vegaprotocol.io/vega/libs/crypto"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  	"code.vegaprotocol.io/vega/libs/ptr"
    29  	"code.vegaprotocol.io/vega/logging"
    30  	"code.vegaprotocol.io/vega/protos/vega"
    31  
    32  	"github.com/pkg/errors"
    33  )
    34  
    35  var (
    36  	// ErrNotEnoughOrders signals that not enough orders were
    37  	// in the book to achieve a given operation.
    38  	ErrNotEnoughOrders   = errors.New("insufficient orders")
    39  	ErrOrderDoesNotExist = errors.New("order does not exist")
    40  	ErrInvalidVolume     = errors.New("invalid volume")
    41  	ErrNoBestBid         = errors.New("no best bid")
    42  	ErrNoBestAsk         = errors.New("no best ask")
    43  	ErrNotCrossed        = errors.New("not crossed")
    44  )
    45  
    46  //go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/matching OffbookSource
    47  type OffbookSource interface {
    48  	BestPricesAndVolumes() (*num.Uint, uint64, *num.Uint, uint64)
    49  	SubmitOrder(agg *types.Order, inner, outer *num.Uint) []*types.Order
    50  	NotifyFinished()
    51  	OrderbookShape(st, nd *num.Uint, id *string) []*types.OrderbookShapeResult
    52  }
    53  
    54  // OrderBook represents the book holding all orders in the system.
    55  type OrderBook struct {
    56  	log *logging.Logger
    57  	Config
    58  
    59  	cfgMu                    *sync.Mutex
    60  	marketID                 string
    61  	buy                      *OrderBookSide
    62  	sell                     *OrderBookSide
    63  	lastTradedPrice          *num.Uint
    64  	latestTimestamp          int64
    65  	ordersByID               map[string]*types.Order
    66  	ordersPerParty           map[string]map[string]struct{}
    67  	auction                  bool
    68  	batchID                  uint64
    69  	indicativePriceAndVolume *IndicativePriceAndVolume
    70  	snapshot                 *types.PayloadMatchingBook
    71  	stopped                  bool // if true then we should stop creating snapshots
    72  
    73  	// we keep track here of which type of orders are in the orderbook so we can quickly
    74  	// find an order of a certain type. These get updated when orders are added or removed from the book.
    75  	peggedOrders *peggedOrders
    76  
    77  	peggedOrdersCount uint64
    78  	peggedCountNotify func(int64)
    79  }
    80  
    81  // CumulativeVolumeLevel represents the cumulative volume at a price level for both bid and ask.
    82  type CumulativeVolumeLevel struct {
    83  	price               *num.Uint
    84  	bidVolume           uint64
    85  	askVolume           uint64
    86  	cumulativeBidVolume uint64
    87  	cumulativeAskVolume uint64
    88  	maxTradableAmount   uint64
    89  
    90  	// keep track of how much of the cumulative volume is from AMMs
    91  	cumulativeBidOffbook uint64
    92  	cumulativeAskOffbook uint64
    93  }
    94  
    95  type ipvResult struct {
    96  	price         *num.Uint  // uncrossing price
    97  	side          types.Side // uncrossing side
    98  	volume        uint64     // uncrossing volume
    99  	offbookVolume uint64     // how much of the uncrossing volume comes from AMMs
   100  
   101  	// volume maximising range
   102  	vmrMin *num.Uint
   103  	vmrMax *num.Uint
   104  }
   105  
   106  func (b *OrderBook) Hash() []byte {
   107  	return crypto.Hash(append(b.buy.Hash(), b.sell.Hash()...))
   108  }
   109  
   110  // NewOrderBook create an order book with a given name.
   111  func NewOrderBook(log *logging.Logger, config Config, marketID string, auction bool, peggedCountNotify func(int64)) *OrderBook {
   112  	// setup logger
   113  	log = log.Named(namedLogger)
   114  	log.SetLevel(config.Level.Get())
   115  
   116  	return &OrderBook{
   117  		log:             log,
   118  		marketID:        marketID,
   119  		cfgMu:           &sync.Mutex{},
   120  		buy:             &OrderBookSide{log: log, side: types.SideBuy},
   121  		sell:            &OrderBookSide{log: log, side: types.SideSell},
   122  		Config:          config,
   123  		ordersByID:      map[string]*types.Order{},
   124  		auction:         auction,
   125  		batchID:         0,
   126  		ordersPerParty:  map[string]map[string]struct{}{},
   127  		lastTradedPrice: num.UintZero(),
   128  		snapshot: &types.PayloadMatchingBook{
   129  			MatchingBook: &types.MatchingBook{
   130  				MarketID: marketID,
   131  			},
   132  		},
   133  		peggedOrders:      newPeggedOrders(),
   134  		peggedOrdersCount: 0,
   135  		peggedCountNotify: peggedCountNotify,
   136  	}
   137  }
   138  
   139  // ReloadConf is used in order to reload the internal configuration of
   140  // the OrderBook.
   141  func (b *OrderBook) ReloadConf(cfg Config) {
   142  	b.log.Info("reloading configuration")
   143  	if b.log.GetLevel() != cfg.Level.Get() {
   144  		b.log.Info("updating log level",
   145  			logging.String("old", b.log.GetLevel().String()),
   146  			logging.String("new", cfg.Level.String()),
   147  		)
   148  		b.log.SetLevel(cfg.Level.Get())
   149  	}
   150  
   151  	b.cfgMu.Lock()
   152  	b.Config = cfg
   153  	b.cfgMu.Unlock()
   154  }
   155  
   156  func (b *OrderBook) SetOffbookSource(obs OffbookSource) {
   157  	b.buy.offbook = obs
   158  	b.sell.offbook = obs
   159  }
   160  
   161  // GetOrderBookLevelCount returns the number of levels in the book.
   162  func (b *OrderBook) GetOrderBookLevelCount() uint64 {
   163  	return uint64(len(b.buy.levels) + len(b.sell.levels))
   164  }
   165  
   166  func (b *OrderBook) GetPeggedOrdersCount() uint64 {
   167  	return b.peggedOrdersCount
   168  }
   169  
   170  // GetFillPrice returns the average price which would be achieved for a given
   171  // volume and give side of the book.
   172  func (b *OrderBook) GetFillPrice(volume uint64, side types.Side) (*num.Uint, error) {
   173  	if b.auction {
   174  		p := b.GetIndicativePrice()
   175  		return p, nil
   176  	}
   177  
   178  	if volume == 0 {
   179  		return num.UintZero(), ErrInvalidVolume
   180  	}
   181  
   182  	var (
   183  		price  = num.UintZero()
   184  		vol    = volume
   185  		levels []*PriceLevel
   186  	)
   187  	if side == types.SideSell {
   188  		levels = b.sell.getLevels()
   189  	} else {
   190  		levels = b.buy.getLevels()
   191  	}
   192  
   193  	for i := len(levels) - 1; i >= 0; i-- {
   194  		lvl := levels[i]
   195  		if lvl.volume >= vol {
   196  			// price += lvl.price * vol
   197  			price.Add(price, num.UintZero().Mul(lvl.price, num.NewUint(vol)))
   198  			// return price / volume, nil
   199  			return price.Div(price, num.NewUint(volume)), nil
   200  		}
   201  		price.Add(price, num.UintZero().Mul(lvl.price, num.NewUint(lvl.volume)))
   202  		vol -= lvl.volume
   203  	}
   204  	// at this point, we should check vol, make sure it's 0, if not return an error to indicate something is wrong
   205  	// still return the price for the volume we could close out, so the caller can make a decision on what to do
   206  	if vol == volume {
   207  		return b.lastTradedPrice.Clone(), ErrNotEnoughOrders
   208  	}
   209  	price.Div(price, num.NewUint(volume-vol))
   210  	if vol != 0 {
   211  		return price, ErrNotEnoughOrders
   212  	}
   213  	return price, nil
   214  }
   215  
   216  // EnterAuction Moves the order book into an auction state.
   217  func (b *OrderBook) EnterAuction() []*types.Order {
   218  	// Scan existing orders to see which ones can be kept or cancelled
   219  	ordersToCancel := append(
   220  		b.buy.getOrdersToCancel(true),
   221  		b.sell.getOrdersToCancel(true)...,
   222  	)
   223  
   224  	// Set the market state
   225  	b.auction = true
   226  	b.indicativePriceAndVolume = NewIndicativePriceAndVolume(b.log, b.buy, b.sell)
   227  
   228  	// Return all the orders that have been removed from the book and need to be cancelled
   229  	return ordersToCancel
   230  }
   231  
   232  // LeaveAuction Moves the order book back into continuous trading state.
   233  func (b *OrderBook) LeaveAuction(at time.Time) ([]*types.OrderConfirmation, []*types.Order, error) {
   234  	// Update batchID
   235  	b.batchID++
   236  
   237  	ts := at.UnixNano()
   238  
   239  	// Uncross the book
   240  	uncrossedOrders, err := b.uncrossBook()
   241  	if err != nil {
   242  		return nil, nil, err
   243  	}
   244  
   245  	for _, uo := range uncrossedOrders {
   246  		// refresh if its an iceberg, noop if not
   247  		b.icebergRefresh(uo.Order)
   248  
   249  		if uo.Order.Remaining == 0 {
   250  			uo.Order.Status = types.OrderStatusFilled
   251  			b.remove(uo.Order)
   252  		}
   253  
   254  		if uo.Order.GeneratedOffbook {
   255  			uo.Order.CreatedAt = ts
   256  		}
   257  
   258  		uo.Order.UpdatedAt = ts
   259  		for idx, po := range uo.PassiveOrdersAffected {
   260  			po.UpdatedAt = ts
   261  
   262  			// refresh if its an iceberg, noop if not
   263  			b.icebergRefresh(po)
   264  
   265  			// also remove the orders from lookup tables
   266  			if uo.PassiveOrdersAffected[idx].Remaining == 0 {
   267  				uo.PassiveOrdersAffected[idx].Status = types.OrderStatusFilled
   268  				b.remove(po)
   269  			}
   270  		}
   271  		for _, tr := range uo.Trades {
   272  			tr.Timestamp = ts
   273  			tr.Aggressor = types.SideUnspecified
   274  		}
   275  	}
   276  
   277  	// Remove any orders that will not be valid in continuous trading
   278  	// Return all the orders that have been cancelled from the book
   279  	ordersToCancel := append(
   280  		b.buy.getOrdersToCancel(false),
   281  		b.sell.getOrdersToCancel(false)...,
   282  	)
   283  
   284  	for _, oc := range ordersToCancel {
   285  		oc.UpdatedAt = ts
   286  	}
   287  
   288  	// Flip back to continuous
   289  	b.auction = false
   290  	b.indicativePriceAndVolume = nil
   291  
   292  	return uncrossedOrders, ordersToCancel, nil
   293  }
   294  
   295  func (b OrderBook) InAuction() bool {
   296  	return b.auction
   297  }
   298  
   299  // CanLeaveAuction calls canUncross with required trades and, if that returns false
   300  // without required trades (which still permits leaving liquidity auction
   301  // if canUncross with required trades returs true, both returns are true.
   302  func (b *OrderBook) CanLeaveAuction() (withTrades, withoutTrades bool) {
   303  	withTrades = b.canUncross(true)
   304  	withoutTrades = withTrades
   305  	if withTrades {
   306  		return
   307  	}
   308  	withoutTrades = b.canUncross(false)
   309  	return
   310  }
   311  
   312  // CanUncross - a clunky name for a somewhat clunky function: this checks if there will be LIMIT orders
   313  // on the book after we uncross the book (at the end of an auction). If this returns false, the opening auction should be extended.
   314  func (b *OrderBook) CanUncross() bool {
   315  	return b.canUncross(true)
   316  }
   317  
   318  func (b *OrderBook) BidAndAskPresentAfterAuction() bool {
   319  	return b.canUncross(false)
   320  }
   321  
   322  func (b *OrderBook) canUncross(requireTrades bool) bool {
   323  	bb, err := b.GetBestBidPrice() // sell
   324  	if err != nil {
   325  		return false
   326  	}
   327  	ba, err := b.GetBestAskPrice() // buy
   328  	if err != nil || bb.IsZero() || ba.IsZero() || (requireTrades && bb.LT(ba)) {
   329  		return false
   330  	}
   331  
   332  	// check all buy price levels below ba, find limit orders
   333  	buyMatch := false
   334  	// iterate from the end, where best is
   335  	for i := len(b.buy.levels) - 1; i >= 0; i-- {
   336  		l := b.buy.levels[i]
   337  		if l.price.LT(ba) {
   338  			for _, o := range l.orders {
   339  				// limit order && not just GFA found
   340  				if o.Type == types.OrderTypeLimit && o.TimeInForce != types.OrderTimeInForceGFA {
   341  					buyMatch = true
   342  					break
   343  				}
   344  			}
   345  		}
   346  	}
   347  	sellMatch := false
   348  	for i := len(b.sell.levels) - 1; i >= 0; i-- {
   349  		l := b.sell.levels[i]
   350  		if l.price.GT(bb) {
   351  			for _, o := range l.orders {
   352  				if o.Type == types.OrderTypeLimit && o.TimeInForce != types.OrderTimeInForceGFA {
   353  					sellMatch = true
   354  					break
   355  				}
   356  			}
   357  		}
   358  	}
   359  	// non-GFA orders outside the price range found on the book, we can uncross
   360  	if buyMatch && sellMatch {
   361  		return true
   362  	}
   363  	r := b.GetIndicativePriceAndVolume()
   364  
   365  	// no buy orders remaining on the book after uncrossing, it buyMatches exactly
   366  	vol := uint64(0)
   367  	if !buyMatch {
   368  		for i := len(b.buy.levels) - 1; i >= 0; i-- {
   369  			l := b.buy.levels[i]
   370  			// buy orders are ordered ascending
   371  			if l.price.LT(ba) {
   372  				break
   373  			}
   374  			for _, o := range l.orders {
   375  				vol += o.Remaining
   376  				// we've filled the uncrossing volume, and found an order that is not GFA
   377  				if vol > r.volume && o.TimeInForce != types.OrderTimeInForceGFA {
   378  					buyMatch = true
   379  					break
   380  				}
   381  			}
   382  		}
   383  		if !buyMatch {
   384  			return false
   385  		}
   386  	}
   387  	// we've had to check buy side - sell side is fine
   388  	if sellMatch {
   389  		return true
   390  	}
   391  
   392  	vol = 0
   393  	// for _, l := range b.sell.levels {
   394  	// sell side is ordered descending
   395  	for i := len(b.sell.levels) - 1; i >= 0; i-- {
   396  		l := b.sell.levels[i]
   397  		if l.price.GT(bb) {
   398  			break
   399  		}
   400  		for _, o := range l.orders {
   401  			vol += o.Remaining
   402  			if vol > r.volume && o.TimeInForce != types.OrderTimeInForceGFA {
   403  				sellMatch = true
   404  				break
   405  			}
   406  		}
   407  	}
   408  
   409  	return sellMatch
   410  }
   411  
   412  // GetRefinedIndicativePriceAndVolume calculates a refined ipv. This is required if an AMM was approximately expanded and steps
   413  // over the boundaries of the volume maximising range.
   414  func (b *OrderBook) GetRefinedIndicativePriceAndVolume(r ipvResult) ipvResult {
   415  	ipv := b.indicativePriceAndVolume
   416  
   417  	if len(ipv.generated) == 0 {
   418  		return r
   419  	}
   420  
   421  	for party, gen := range ipv.generated {
   422  		if !gen.approx {
   423  			// this AMM was already expanded accurately so we don't need to refine it
   424  			continue
   425  		}
   426  
   427  		// remove the volume from this AMM
   428  		ipv.removeOffbookShape(party)
   429  
   430  		// and add it in three parts so that we can get accurate orders within the volume-maximising range
   431  		// min-price -> min-vmr -> max-vmr -> max-price
   432  
   433  		ipv.addOffbookShape(ptr.From(party), ipv.lastMinPrice, r.vmrMin, false, false)
   434  
   435  		if r.vmrMin.NEQ(r.vmrMax) {
   436  			ipv.addOffbookShape(ptr.From(party), r.vmrMin, r.vmrMax, true, true)
   437  		}
   438  
   439  		ipv.addOffbookShape(ptr.From(party), r.vmrMax, ipv.lastMaxPrice, false, false)
   440  
   441  		ipv.needsUpdate = true
   442  	}
   443  
   444  	return b.GetIndicativePriceAndVolume()
   445  }
   446  
   447  // GetIndicativePriceAndVolume Calculates the indicative price and volume of the order book without modifying the order book state.
   448  func (b *OrderBook) GetIndicativePriceAndVolume() ipvResult {
   449  	// Generate a set of price level pairs with their maximum tradable volumes
   450  	cumulativeVolumes, maxTradableAmount, err := b.buildCumulativePriceLevels()
   451  	if err != nil {
   452  		if b.log.GetLevel() <= logging.DebugLevel {
   453  			b.log.Debug("could not get cumulative price levels", logging.Error(err))
   454  		}
   455  		return ipvResult{price: num.UintZero()}
   456  	}
   457  
   458  	// Pull out all prices that match that volume
   459  	prices := make([]*num.Uint, 0, len(cumulativeVolumes))
   460  	for _, value := range cumulativeVolumes {
   461  		if value.maxTradableAmount == maxTradableAmount {
   462  			prices = append(prices, value.price)
   463  		}
   464  	}
   465  
   466  	// get the maximum volume price from the average of the maximum and minimum tradable price levels
   467  	var (
   468  		uncrossPrice  = num.UintZero()
   469  		uncrossSide   types.Side
   470  		offbookVolume uint64
   471  		vmrMin        *num.Uint
   472  		vmrMax        *num.Uint
   473  	)
   474  	if len(prices) > 0 {
   475  		// uncrossPrice = (prices[len(prices)-1] + prices[0]) / 2
   476  		uncrossPrice.Div(
   477  			num.UintZero().Add(prices[len(prices)-1], prices[0]),
   478  			num.NewUint(2),
   479  		)
   480  
   481  		vmrMin = prices[len(prices)-1]
   482  		vmrMax = prices[0]
   483  	}
   484  
   485  	// See which side we should fully process when we uncross
   486  	ordersToFill := int64(maxTradableAmount)
   487  	for _, value := range cumulativeVolumes {
   488  		ordersToFill -= int64(value.bidVolume)
   489  		if ordersToFill == 0 {
   490  			// Buys fill exactly, uncross from the buy side
   491  			uncrossSide = types.SideBuy
   492  			offbookVolume = value.cumulativeBidOffbook
   493  			break
   494  		} else if ordersToFill < 0 {
   495  			// Buys are not exact, uncross from the sell side
   496  			uncrossSide = types.SideSell
   497  			offbookVolume = value.cumulativeAskOffbook
   498  			break
   499  		}
   500  	}
   501  
   502  	return ipvResult{
   503  		price:         uncrossPrice,
   504  		volume:        maxTradableAmount,
   505  		side:          uncrossSide,
   506  		offbookVolume: offbookVolume,
   507  		vmrMin:        vmrMin,
   508  		vmrMax:        vmrMax,
   509  	}
   510  }
   511  
   512  // GetIndicativePrice Calculates the indicative price of the order book without modifying the order book state.
   513  func (b *OrderBook) GetIndicativePrice() (retprice *num.Uint) {
   514  	// Generate a set of price level pairs with their maximum tradable volumes
   515  	cumulativeVolumes, maxTradableAmount, err := b.buildCumulativePriceLevels()
   516  	if err != nil {
   517  		if b.log.GetLevel() <= logging.DebugLevel {
   518  			b.log.Debug("could not get cumulative price levels", logging.Error(err))
   519  		}
   520  		return num.UintZero()
   521  	}
   522  
   523  	// Pull out all prices that match that volume
   524  	prices := make([]*num.Uint, 0, len(cumulativeVolumes))
   525  	for _, value := range cumulativeVolumes {
   526  		if value.maxTradableAmount == maxTradableAmount {
   527  			prices = append(prices, value.price)
   528  		}
   529  	}
   530  
   531  	// get the maximum volume price from the average of the minimum and maximum tradable price levels
   532  	if len(prices) > 0 {
   533  		// return (prices[len(prices)-1] + prices[0]) / 2
   534  		return num.UintZero().Div(
   535  			num.UintZero().Add(prices[len(prices)-1], prices[0]),
   536  			num.NewUint(2),
   537  		)
   538  	}
   539  	return num.UintZero()
   540  }
   541  
   542  func (b *OrderBook) GetIndicativeTrades() ([]*types.Trade, error) {
   543  	// Get the uncrossing price and which side has the most volume at that price
   544  	r := b.GetIndicativePriceAndVolume()
   545  
   546  	// If we have no uncrossing price, we have nothing to do
   547  	if r.price.IsZero() && r.volume == 0 {
   548  		return nil, nil
   549  	}
   550  
   551  	var (
   552  		uncrossOrders  []*types.Order
   553  		uncrossingSide *OrderBookSide
   554  	)
   555  
   556  	if r.side == types.SideBuy {
   557  		uncrossingSide = b.buy
   558  	} else {
   559  		uncrossingSide = b.sell
   560  	}
   561  
   562  	// extract uncrossing orders from all AMMs
   563  	uncrossOrders = b.indicativePriceAndVolume.ExtractOffbookOrders(r.price, r.side, r.offbookVolume)
   564  
   565  	// the remaining volume should now come from the orderbook
   566  	volume := r.volume - r.offbookVolume
   567  
   568  	// Remove all the orders from that side of the book up to the given volume
   569  	uncrossOrders = append(uncrossOrders, uncrossingSide.ExtractOrders(r.price, volume, false)...)
   570  	opSide := b.getOppositeSide(r.side)
   571  	output := make([]*types.Trade, 0, len(uncrossOrders))
   572  	trades, err := opSide.fakeUncrossAuction(uncrossOrders)
   573  	if err != nil {
   574  		return nil, err
   575  	}
   576  	// Update all the trades to have the correct uncrossing price
   577  	for _, t := range trades {
   578  		t.Price = r.price.Clone()
   579  	}
   580  	output = append(output, trades...)
   581  
   582  	return output, nil
   583  }
   584  
   585  // buildCumulativePriceLevels this returns a slice of all the price levels with the
   586  // cumulative volume for each level. Also returns the max tradable size.
   587  func (b *OrderBook) buildCumulativePriceLevels() ([]CumulativeVolumeLevel, uint64, error) {
   588  	bestBid, err := b.GetBestBidPrice()
   589  	if err != nil {
   590  		return nil, 0, ErrNoBestBid
   591  	}
   592  	bestAsk, err := b.GetBestAskPrice()
   593  	if err != nil {
   594  		return nil, 0, ErrNoBestAsk
   595  	}
   596  	// Short circuit if the book is not crossed
   597  	if bestBid.LT(bestAsk) || bestBid.IsZero() || bestAsk.IsZero() {
   598  		return nil, 0, ErrNotCrossed
   599  	}
   600  
   601  	volume, maxTradableAmount := b.indicativePriceAndVolume.
   602  		GetCumulativePriceLevels(bestBid, bestAsk)
   603  	return volume, maxTradableAmount, nil
   604  }
   605  
   606  // Uncrosses the book to generate the maximum volume set of trades
   607  // if removeOrders is set to true then matched orders get removed from the book.
   608  func (b *OrderBook) uncrossBook() ([]*types.OrderConfirmation, error) {
   609  	// Get the uncrossing price and which side has the most volume at that price
   610  	r := b.GetIndicativePriceAndVolume()
   611  
   612  	// If we have no uncrossing price, we have nothing to do
   613  	if r.price.IsZero() && r.volume == 0 {
   614  		return nil, nil
   615  	}
   616  
   617  	// if any offbook order step over the boundaries of the volume maximising range we need to refine the
   618  	// AMM expansion in this region to get a more accurate uncrossing volume
   619  	r = b.GetRefinedIndicativePriceAndVolume(r)
   620  
   621  	var uncrossingSide *OrderBookSide
   622  
   623  	if r.side == types.SideBuy {
   624  		uncrossingSide = b.buy
   625  	} else {
   626  		uncrossingSide = b.sell
   627  	}
   628  
   629  	// extract uncrossing orders from all AMMs
   630  	uncrossOrders := b.indicativePriceAndVolume.ExtractOffbookOrders(r.price, r.side, r.offbookVolume)
   631  
   632  	// the remaining volume should now come from the orderbook
   633  	volume := r.volume - r.offbookVolume
   634  
   635  	// Remove all the orders from that side of the book up to the given volume
   636  	uncrossOrders = append(uncrossOrders, uncrossingSide.ExtractOrders(r.price, volume, true)...)
   637  	return b.uncrossBookSide(uncrossOrders, b.getOppositeSide(r.side), r.price.Clone())
   638  }
   639  
   640  // Takes extracted order from a side of the book, and uncross them
   641  // with the opposite side.
   642  func (b *OrderBook) uncrossBookSide(
   643  	uncrossOrders []*types.Order,
   644  	opSide *OrderBookSide,
   645  	price *num.Uint,
   646  ) ([]*types.OrderConfirmation, error) {
   647  	var (
   648  		uncrossedOrder *types.OrderConfirmation
   649  		allOrders      = make([]*types.OrderConfirmation, 0, len(uncrossOrders))
   650  	)
   651  	if len(uncrossOrders) == 0 {
   652  		return nil, nil
   653  	}
   654  
   655  	defer b.buy.uncrossFinished()
   656  
   657  	// get price factor, if price is 10,000, but market price is 100, this is 10,000/100 -> 100
   658  	// so we can get the market price simply by doing price / (order.Price/ order.OriginalPrice)
   659  	// as the asset decimals may be < market decimals, the calculation must be done in decimals.
   660  	mPrice, _ := num.UintFromDecimal(price.ToDecimal().Div(uncrossOrders[0].Price.ToDecimal().Div(uncrossOrders[0].OriginalPrice.ToDecimal())))
   661  	// Uncross each one
   662  	for _, order := range uncrossOrders {
   663  		// since all of uncrossOrders will be traded away and at the same uncrossing price
   664  		// iceberg orders are sent in as their full value instead of refreshing at each step
   665  		if order.IcebergOrder != nil {
   666  			order.Remaining += order.IcebergOrder.ReservedRemaining
   667  			order.IcebergOrder.ReservedRemaining = 0
   668  		}
   669  
   670  		// try to get the market price value from the order
   671  		trades, affectedOrders, _, err := opSide.uncross(order, false)
   672  		if err != nil {
   673  			return nil, err
   674  		}
   675  		// If the affected order is fully filled set the status
   676  		for _, affectedOrder := range affectedOrders {
   677  			if affectedOrder.Remaining == 0 {
   678  				affectedOrder.Status = types.OrderStatusFilled
   679  			}
   680  		}
   681  		// Update all the trades to have the correct uncrossing price
   682  		for index := 0; index < len(trades); index++ {
   683  			trades[index].Price = price.Clone()
   684  			trades[index].MarketPrice = mPrice.Clone()
   685  		}
   686  		uncrossedOrder = &types.OrderConfirmation{Order: order, PassiveOrdersAffected: affectedOrders, Trades: trades}
   687  		allOrders = append(allOrders, uncrossedOrder)
   688  	}
   689  
   690  	return allOrders, nil
   691  }
   692  
   693  func (b *OrderBook) GetOrdersPerParty(party string) []*types.Order {
   694  	orderIDs := b.ordersPerParty[party]
   695  	if len(orderIDs) <= 0 {
   696  		return []*types.Order{}
   697  	}
   698  
   699  	orders := make([]*types.Order, 0, len(orderIDs))
   700  	for oid := range orderIDs {
   701  		orders = append(orders, b.ordersByID[oid])
   702  	}
   703  
   704  	// sort before returning since callers will assume they are in a deterministic order
   705  	sort.Slice(orders, func(i, j int) bool {
   706  		return orders[i].ID < orders[j].ID
   707  	})
   708  	return orders
   709  }
   710  
   711  // BestBidPriceAndVolume : Return the best bid and volume for the buy side of the book.
   712  func (b *OrderBook) BestBidPriceAndVolume() (*num.Uint, uint64, error) {
   713  	price, volume, err := b.buy.BestPriceAndVolume()
   714  
   715  	if b.buy.offbook != nil {
   716  		oPrice, oVolume, _, _ := b.buy.offbook.BestPricesAndVolumes()
   717  
   718  		// no off source volume, return the orderbook
   719  		if oVolume == 0 {
   720  			return price, volume, err
   721  		}
   722  
   723  		// no orderbook volume or AMM price is better
   724  		if err != nil || oPrice.GT(price) {
   725  			//nolint: nilerr
   726  			return oPrice, oVolume, nil
   727  		}
   728  
   729  		// AMM price equals orderbook price, combined volumes
   730  		if err == nil && oPrice.EQ(price) {
   731  			oVolume += volume
   732  			return oPrice, oVolume, nil
   733  		}
   734  	}
   735  	return price, volume, err
   736  }
   737  
   738  // BestOfferPriceAndVolume : Return the best bid and volume for the sell side of the book.
   739  func (b *OrderBook) BestOfferPriceAndVolume() (*num.Uint, uint64, error) {
   740  	price, volume, err := b.sell.BestPriceAndVolume()
   741  
   742  	if b.sell.offbook != nil {
   743  		_, _, oPrice, oVolume := b.buy.offbook.BestPricesAndVolumes()
   744  
   745  		// no off source volume, return the orderbook
   746  		if oVolume == 0 {
   747  			return price, volume, err
   748  		}
   749  
   750  		// no orderbook volume or AMM price is better
   751  		if err != nil || oPrice.LT(price) {
   752  			//nolint: nilerr
   753  			return oPrice, oVolume, nil
   754  		}
   755  
   756  		// AMM price equals orderbook price, combined volumes
   757  		if err == nil && oPrice.EQ(price) {
   758  			oVolume += volume
   759  			return oPrice, oVolume, nil
   760  		}
   761  	}
   762  	return price, volume, err
   763  }
   764  
   765  func (b *OrderBook) CancelAllOrders(party string) ([]*types.OrderCancellationConfirmation, error) {
   766  	var (
   767  		orders = b.GetOrdersPerParty(party)
   768  		confs  = []*types.OrderCancellationConfirmation{}
   769  		conf   *types.OrderCancellationConfirmation
   770  		err    error
   771  	)
   772  
   773  	for _, o := range orders {
   774  		conf, err = b.CancelOrder(o)
   775  		if err != nil {
   776  			return nil, err
   777  		}
   778  		confs = append(confs, conf)
   779  	}
   780  
   781  	return confs, err
   782  }
   783  
   784  func (b *OrderBook) CheckBook() bool {
   785  	checkOBB, checkOBS := false, false
   786  	if len(b.buy.levels) > 0 {
   787  		checkOBB = true
   788  		for _, o := range b.buy.levels[len(b.buy.levels)-1].orders {
   789  			if o.PeggedOrder == nil || o.PeggedOrder.Reference != types.PeggedReferenceBestBid {
   790  				checkOBB = false
   791  				break
   792  			}
   793  		}
   794  	}
   795  	if len(b.sell.levels) > 0 {
   796  		checkOBS = true
   797  		for _, o := range b.sell.levels[len(b.sell.levels)-1].orders {
   798  			if o.PeggedOrder == nil || o.PeggedOrder.Reference != types.PeggedReferenceBestAsk {
   799  				checkOBS = false
   800  				break
   801  			}
   802  		}
   803  	}
   804  	// if either buy or sell side is lacking non-pegged orders, check AMM orders.
   805  	if checkOBB || checkOBS {
   806  		// get best bid/ask price and volumes.
   807  		bb, bbv, bs, bsv := b.buy.offbook.BestPricesAndVolumes()
   808  		// if the buy side is lacking non-pegged orders, check if there are off-book orders.
   809  		if checkOBB && (bb == nil || bb.IsZero() || bbv == 0) {
   810  			return false
   811  		}
   812  		// same, but for sell side.
   813  		if checkOBS && (bs == nil || bs.IsZero() || bsv == 0) {
   814  			return false
   815  		}
   816  	}
   817  	return true
   818  }
   819  
   820  // CancelOrder cancel an order that is active on an order book. Market and Order ID are validated, however the order must match
   821  // the order on the book with respect to side etc. The caller will typically validate this by using a store, we should
   822  // not trust that the external world can provide these values reliably.
   823  func (b *OrderBook) CancelOrder(order *types.Order) (*types.OrderCancellationConfirmation, error) {
   824  	// Validate Market
   825  	if order.MarketID != b.marketID {
   826  		b.log.Panic("Market ID mismatch",
   827  			logging.Order(*order),
   828  			logging.String("order-book", b.marketID))
   829  	}
   830  
   831  	// Validate Order ID must be present
   832  	if err := validateOrderID(order.ID); err != nil {
   833  		if b.log.GetLevel() == logging.DebugLevel {
   834  			b.log.Debug("Order ID missing or invalid",
   835  				logging.Order(*order),
   836  				logging.String("order-book", b.marketID))
   837  		}
   838  		return nil, err
   839  	}
   840  
   841  	order, err := b.DeleteOrder(order)
   842  	if err != nil {
   843  		return nil, err
   844  	}
   845  
   846  	// Important to mark the order as cancelled (and no longer active)
   847  	order.Status = types.OrderStatusCancelled
   848  
   849  	result := &types.OrderCancellationConfirmation{
   850  		Order: order,
   851  	}
   852  	return result, nil
   853  }
   854  
   855  // RemoveOrder takes the order off the order book.
   856  func (b *OrderBook) RemoveOrder(id string) (*types.Order, error) {
   857  	order, err := b.GetOrderByID(id)
   858  	if err != nil {
   859  		return nil, err
   860  	}
   861  	order, err = b.DeleteOrder(order)
   862  	if err != nil {
   863  		return nil, err
   864  	}
   865  
   866  	// Important to mark the order as parked (and no longer active)
   867  	order.Status = types.OrderStatusParked
   868  
   869  	return order, nil
   870  }
   871  
   872  // AmendOrder amends an order which is an active order on the book.
   873  func (b *OrderBook) AmendOrder(originalOrder, amendedOrder *types.Order) error {
   874  	if originalOrder == nil {
   875  		if amendedOrder != nil {
   876  			b.log.Panic("invalid input, orginalOrder is nil", logging.Order(*amendedOrder))
   877  		}
   878  	}
   879  
   880  	// If the creation date for the 2 orders is different, something went wrong
   881  	if originalOrder != nil && originalOrder.CreatedAt != amendedOrder.CreatedAt {
   882  		return types.ErrOrderOutOfSequence
   883  	}
   884  
   885  	if err := b.validateOrder(amendedOrder); err != nil {
   886  		if b.log.GetLevel() == logging.DebugLevel {
   887  			b.log.Debug("Order validation failure",
   888  				logging.Order(*amendedOrder),
   889  				logging.Error(err),
   890  				logging.String("order-book", b.marketID))
   891  		}
   892  		return err
   893  	}
   894  
   895  	var (
   896  		volumeChange int64
   897  		side         = b.sell
   898  		err          error
   899  	)
   900  	if amendedOrder.Side == types.SideBuy {
   901  		side = b.buy
   902  	}
   903  
   904  	if volumeChange, err = side.amendOrder(amendedOrder); err != nil {
   905  		if b.log.GetLevel() == logging.DebugLevel {
   906  			b.log.Debug("Failed to amend",
   907  				logging.String("side", amendedOrder.Side.String()),
   908  				logging.Order(*amendedOrder),
   909  				logging.String("market", b.marketID),
   910  				logging.Error(err),
   911  			)
   912  		}
   913  		return err
   914  	}
   915  
   916  	// update the order by ids mapping
   917  	b.ordersByID[amendedOrder.ID] = amendedOrder
   918  	if !b.auction {
   919  		return nil
   920  	}
   921  
   922  	if volumeChange < 0 {
   923  		b.indicativePriceAndVolume.RemoveVolumeAtPrice(
   924  			amendedOrder.Price, uint64(-volumeChange), amendedOrder.Side, false)
   925  	}
   926  
   927  	if volumeChange > 0 {
   928  		b.indicativePriceAndVolume.AddVolumeAtPrice(
   929  			amendedOrder.Price, uint64(volumeChange), amendedOrder.Side, false)
   930  	}
   931  
   932  	return nil
   933  }
   934  
   935  func (b *OrderBook) UpdateAMM(party string) {
   936  	if !b.auction {
   937  		return
   938  	}
   939  
   940  	ipv := b.indicativePriceAndVolume
   941  	ipv.removeOffbookShape(party)
   942  
   943  	min, max := ipv.lastMinPrice, ipv.lastMaxPrice
   944  	if min.IsZero() || max.IsZero() || min.GT(max) {
   945  		// region is not crossed so we won't expand just yet
   946  		return
   947  	}
   948  
   949  	ipv.addOffbookShape(ptr.From(party), ipv.lastMinPrice, ipv.lastMaxPrice, false, false)
   950  	ipv.needsUpdate = true
   951  }
   952  
   953  // GetTrades returns the trades a given order generates if we were to submit it now
   954  // this is used to calculate fees, perform price monitoring, etc...
   955  func (b *OrderBook) GetTrades(order *types.Order) ([]*types.Trade, error) {
   956  	// this should always return straight away in an auction
   957  	if b.auction {
   958  		return nil, nil
   959  	}
   960  
   961  	if err := b.validateOrder(order); err != nil {
   962  		return nil, err
   963  	}
   964  	if order.CreatedAt > b.latestTimestamp {
   965  		b.latestTimestamp = order.CreatedAt
   966  	}
   967  
   968  	trades, err := b.getOppositeSide(order.Side).fakeUncross(order, true)
   969  	// it's fine for the error to be a wash trade here,
   970  	// it's just be stopped when really uncrossing.
   971  	if err != nil && err != ErrWashTrade {
   972  		// some random error happened, return both trades and error
   973  		// this is a case that isn't covered by the current SubmitOrder call
   974  		return trades, err
   975  	}
   976  
   977  	// no error uncrossing, in all other cases, return trades (could be empty) without an error
   978  	return trades, nil
   979  }
   980  
   981  func (b *OrderBook) ReplaceOrder(rm, rpl *types.Order) (*types.OrderConfirmation, error) {
   982  	if _, err := b.CancelOrder(rm); err != nil {
   983  		return nil, err
   984  	}
   985  	return b.SubmitOrder(rpl)
   986  }
   987  
   988  func (b *OrderBook) ReSubmitSpecialOrders(order *types.Order) {
   989  	// not allowed to submit a normal order here
   990  	if order.PeggedOrder == nil {
   991  		b.log.Panic("only pegged orders allowed", logging.Order(order))
   992  	}
   993  
   994  	order.BatchID = b.batchID
   995  
   996  	// check if order would trade, that should never happen as well.
   997  	switch order.Side {
   998  	case types.SideBuy:
   999  		price, err := b.GetBestAskPrice()
  1000  		if err == nil && price.LTE(order.Price) {
  1001  			b.log.Panic("re submit special order would cross", logging.Order(order), logging.BigUint("best-ask", price))
  1002  		}
  1003  	case types.SideSell:
  1004  		price, err := b.GetBestBidPrice()
  1005  		if err == nil && price.GTE(order.Price) {
  1006  			b.log.Panic("re submit special order would cross", logging.Order(order), logging.BigUint("best-bid", price))
  1007  		}
  1008  	default:
  1009  		b.log.Panic("invalid order side", logging.Order(order))
  1010  	}
  1011  
  1012  	// now we can nicely add the order to the book, no uncrossing needed
  1013  	b.getSide(order.Side).addOrder(order)
  1014  	b.add(order)
  1015  }
  1016  
  1017  // SubmitOrder Add an order and attempt to uncross the book, returns a TradeSet protobuf message object.
  1018  func (b *OrderBook) SubmitOrder(order *types.Order) (*types.OrderConfirmation, error) {
  1019  	if err := b.validateOrder(order); err != nil {
  1020  		return nil, err
  1021  	}
  1022  
  1023  	if _, ok := b.ordersByID[order.ID]; ok {
  1024  		b.log.Panic("an order in the book already exists with the same ID", logging.Order(*order))
  1025  	}
  1026  
  1027  	if order.CreatedAt > b.latestTimestamp {
  1028  		b.latestTimestamp = order.CreatedAt
  1029  	}
  1030  
  1031  	var (
  1032  		trades          []*types.Trade
  1033  		impactedOrders  []*types.Order
  1034  		lastTradedPrice *num.Uint
  1035  		err             error
  1036  	)
  1037  	order.BatchID = b.batchID
  1038  
  1039  	if !b.auction {
  1040  		// uncross with opposite
  1041  
  1042  		defer b.buy.uncrossFinished()
  1043  		trades, impactedOrders, lastTradedPrice, err = b.getOppositeSide(order.Side).uncross(order, true)
  1044  		if !lastTradedPrice.IsZero() {
  1045  			b.lastTradedPrice = lastTradedPrice
  1046  		}
  1047  	}
  1048  
  1049  	// if order is persistent type add to order book to the correct side
  1050  	// and we did not hit a error / wash trade error
  1051  	if order.IsPersistent() && err == nil {
  1052  		if order.IcebergOrder != nil && order.Status == types.OrderStatusActive {
  1053  			// now trades have been generated for the aggressive iceberg based on the
  1054  			// full size, set the peak limits ready for it to be added to the book.
  1055  			order.SetIcebergPeaks()
  1056  		}
  1057  
  1058  		b.getSide(order.Side).addOrder(order)
  1059  		// also add it to the indicative price and volume if in auction
  1060  		if b.auction {
  1061  			b.indicativePriceAndVolume.AddVolumeAtPrice(
  1062  				order.Price, order.TrueRemaining(), order.Side, false)
  1063  		}
  1064  	}
  1065  
  1066  	// Was the aggressive order fully filled?
  1067  	if order.Remaining == 0 {
  1068  		order.Status = types.OrderStatusFilled
  1069  	}
  1070  
  1071  	// What is an Immediate or Cancel Order?
  1072  	// An immediate or cancel order (IOC) is an order to buy or sell that executes all
  1073  	// or part immediately and cancels any unfilled portion of the order.
  1074  	if order.TimeInForce == types.OrderTimeInForceIOC && order.Remaining > 0 {
  1075  		// Stopped as not filled at all
  1076  		if order.Remaining == order.Size {
  1077  			order.Status = types.OrderStatusStopped
  1078  		} else {
  1079  			// IOC so we set status as Cancelled.
  1080  			order.Status = types.OrderStatusPartiallyFilled
  1081  		}
  1082  	}
  1083  
  1084  	// What is Fill Or Kill?
  1085  	// Fill or kill (FOK) is a type of time-in-force designation used in trading that instructs
  1086  	// the protocol to execute an order immediately and completely or not at all.
  1087  	// The order must be filled in its entirety or cancelled (killed).
  1088  	if order.TimeInForce == types.OrderTimeInForceFOK && order.Remaining == order.Size {
  1089  		// FOK and didnt trade at all we set status as Stopped
  1090  		order.Status = types.OrderStatusStopped
  1091  	}
  1092  
  1093  	for idx := range impactedOrders {
  1094  		// refresh if its an iceberg, noop if not
  1095  		b.icebergRefresh(impactedOrders[idx])
  1096  
  1097  		if impactedOrders[idx].Remaining == 0 {
  1098  			impactedOrders[idx].Status = types.OrderStatusFilled
  1099  
  1100  			// delete from lookup tables
  1101  			b.remove(impactedOrders[idx])
  1102  		}
  1103  	}
  1104  
  1105  	// if we did hit a wash trade, set the status to STOPPED
  1106  	if err == ErrWashTrade {
  1107  		if order.Size > order.Remaining {
  1108  			order.Status = types.OrderStatusPartiallyFilled
  1109  		} else {
  1110  			order.Status = types.OrderStatusStopped
  1111  		}
  1112  		order.Reason = types.OrderErrorSelfTrading
  1113  	}
  1114  
  1115  	if order.Status == types.OrderStatusActive {
  1116  		b.add(order)
  1117  	}
  1118  
  1119  	orderConfirmation := makeResponse(order, trades, impactedOrders)
  1120  	return orderConfirmation, nil
  1121  }
  1122  
  1123  // DeleteOrder remove a given order on a given side from the book.
  1124  func (b *OrderBook) DeleteOrder(
  1125  	order *types.Order,
  1126  ) (*types.Order, error) {
  1127  	dorder, err := b.getSide(order.Side).RemoveOrder(order)
  1128  	if err != nil {
  1129  		if b.log.GetLevel() == logging.DebugLevel {
  1130  			b.log.Debug("Failed to remove order",
  1131  				logging.Order(*order),
  1132  				logging.Error(err),
  1133  				logging.String("order-book", b.marketID))
  1134  		}
  1135  		return nil, types.ErrOrderRemovalFailure
  1136  	}
  1137  
  1138  	b.remove(order)
  1139  
  1140  	// also remove it to the indicative price and volume if in auction
  1141  	// here we use specifically the order which was in the book, just in case
  1142  	// the order passed in would be wrong
  1143  	// TODO: refactor this better, we should never need to pass in more that IDs there
  1144  	// because by using the order passed in remain and price, we could use
  1145  	// values which have been amended previously... (e.g: amending an order which
  1146  	// cancel the order if it expires it
  1147  	if b.auction {
  1148  		b.indicativePriceAndVolume.RemoveVolumeAtPrice(
  1149  			dorder.Price, dorder.TrueRemaining(), dorder.Side, false)
  1150  	}
  1151  	return dorder, err
  1152  }
  1153  
  1154  // GetOrderByID returns order by its ID (IDs are not expected to collide within same market).
  1155  func (b *OrderBook) GetOrderByID(orderID string) (*types.Order, error) {
  1156  	if err := validateOrderID(orderID); err != nil {
  1157  		if b.log.GetLevel() == logging.DebugLevel {
  1158  			b.log.Debug("Order ID missing or invalid",
  1159  				logging.String("order-id", orderID))
  1160  		}
  1161  		return nil, err
  1162  	}
  1163  	// First look for the order in the order book
  1164  	order, exists := b.ordersByID[orderID]
  1165  	if !exists {
  1166  		return nil, ErrOrderDoesNotExist
  1167  	}
  1168  	return order, nil
  1169  }
  1170  
  1171  // RemoveDistressedOrders remove from the book all order holding distressed positions.
  1172  func (b *OrderBook) RemoveDistressedOrders(
  1173  	parties []events.MarketPosition,
  1174  ) ([]*types.Order, error) {
  1175  	rmorders := []*types.Order{}
  1176  
  1177  	for _, party := range parties {
  1178  		orders := []*types.Order{}
  1179  		for _, l := range b.buy.levels {
  1180  			rm := l.getOrdersByParty(party.Party())
  1181  			orders = append(orders, rm...)
  1182  		}
  1183  		for _, l := range b.sell.levels {
  1184  			rm := l.getOrdersByParty(party.Party())
  1185  			orders = append(orders, rm...)
  1186  		}
  1187  		for _, o := range orders {
  1188  			confirm, err := b.CancelOrder(o)
  1189  			if err != nil {
  1190  				b.log.Panic(
  1191  					"Failed to cancel a given order for party",
  1192  					logging.Order(*o),
  1193  					logging.String("party", party.Party()),
  1194  					logging.Error(err))
  1195  			}
  1196  			// here we set the status of the order as stopped as the system triggered it as well.
  1197  			confirm.Order.Status = types.OrderStatusStopped
  1198  			rmorders = append(rmorders, confirm.Order)
  1199  		}
  1200  	}
  1201  	return rmorders, nil
  1202  }
  1203  
  1204  func (b OrderBook) getSide(orderSide types.Side) *OrderBookSide {
  1205  	if orderSide == types.SideBuy {
  1206  		return b.buy
  1207  	}
  1208  	return b.sell
  1209  }
  1210  
  1211  func (b *OrderBook) getOppositeSide(orderSide types.Side) *OrderBookSide {
  1212  	if orderSide == types.SideBuy {
  1213  		return b.sell
  1214  	}
  1215  	return b.buy
  1216  }
  1217  
  1218  func makeResponse(order *types.Order, trades []*types.Trade, impactedOrders []*types.Order) *types.OrderConfirmation {
  1219  	return &types.OrderConfirmation{
  1220  		Order:                 order,
  1221  		PassiveOrdersAffected: impactedOrders,
  1222  		Trades:                trades,
  1223  	}
  1224  }
  1225  
  1226  func (b *OrderBook) GetBestBidPrice() (*num.Uint, error) {
  1227  	price, _, err := b.buy.BestPriceAndVolume()
  1228  
  1229  	if b.buy.offbook != nil {
  1230  		offbook, volume, _, _ := b.buy.offbook.BestPricesAndVolumes()
  1231  		if volume == 0 {
  1232  			return price, err
  1233  		}
  1234  
  1235  		if err != nil || offbook.GT(price) {
  1236  			//nolint: nilerr
  1237  			return offbook, nil
  1238  		}
  1239  	}
  1240  	return price, err
  1241  }
  1242  
  1243  func (b *OrderBook) GetBestStaticBidPrice() (*num.Uint, error) {
  1244  	price, err := b.buy.BestStaticPrice()
  1245  	if b.buy.offbook != nil {
  1246  		offbook, volume, _, _ := b.buy.offbook.BestPricesAndVolumes()
  1247  		if volume == 0 {
  1248  			return price, err
  1249  		}
  1250  
  1251  		if err != nil || offbook.GT(price) {
  1252  			//nolint: nilerr
  1253  			return offbook, nil
  1254  		}
  1255  	}
  1256  	return price, err
  1257  }
  1258  
  1259  func (b *OrderBook) GetBestStaticBidPriceAndVolume() (*num.Uint, uint64, error) {
  1260  	price, volume, err := b.buy.BestStaticPriceAndVolume()
  1261  
  1262  	if b.buy.offbook != nil {
  1263  		oPrice, oVolume, _, _ := b.buy.offbook.BestPricesAndVolumes()
  1264  
  1265  		// no off source volume, return the orderbook
  1266  		if oVolume == 0 {
  1267  			return price, volume, err
  1268  		}
  1269  
  1270  		// no orderbook volume or AMM price is better
  1271  		if err != nil || oPrice.GT(price) {
  1272  			//nolint: nilerr
  1273  			return oPrice, oVolume, nil
  1274  		}
  1275  
  1276  		// AMM price equals orderbook price, combined volumes
  1277  		if err == nil && oPrice.EQ(price) {
  1278  			oVolume += volume
  1279  			return oPrice, oVolume, nil
  1280  		}
  1281  	}
  1282  	return price, volume, err
  1283  }
  1284  
  1285  func (b *OrderBook) GetBestAskPrice() (*num.Uint, error) {
  1286  	price, _, err := b.sell.BestPriceAndVolume()
  1287  
  1288  	if b.sell.offbook != nil {
  1289  		_, _, offbook, volume := b.sell.offbook.BestPricesAndVolumes()
  1290  		if volume == 0 {
  1291  			return price, err
  1292  		}
  1293  
  1294  		if err != nil || offbook.LT(price) {
  1295  			//nolint: nilerr
  1296  			return offbook, nil
  1297  		}
  1298  	}
  1299  	return price, err
  1300  }
  1301  
  1302  func (b *OrderBook) GetBestStaticAskPrice() (*num.Uint, error) {
  1303  	price, err := b.sell.BestStaticPrice()
  1304  	if b.sell.offbook != nil {
  1305  		_, _, offbook, volume := b.sell.offbook.BestPricesAndVolumes()
  1306  		if volume == 0 {
  1307  			return price, err
  1308  		}
  1309  
  1310  		if err != nil || offbook.LT(price) {
  1311  			//nolint: nilerr
  1312  			return offbook, nil
  1313  		}
  1314  	}
  1315  	return price, err
  1316  }
  1317  
  1318  func (b *OrderBook) GetBestStaticAskPriceAndVolume() (*num.Uint, uint64, error) {
  1319  	price, volume, err := b.sell.BestStaticPriceAndVolume()
  1320  
  1321  	if b.sell.offbook != nil {
  1322  		_, _, oPrice, oVolume := b.sell.offbook.BestPricesAndVolumes()
  1323  
  1324  		// no off source volume, return the orderbook
  1325  		if oVolume == 0 {
  1326  			return price, volume, err
  1327  		}
  1328  
  1329  		// no orderbook volume or AMM price is better
  1330  		if err != nil || oPrice.LT(price) {
  1331  			//nolint: nilerr
  1332  			return oPrice, oVolume, nil
  1333  		}
  1334  
  1335  		// AMM price equals orderbook price, combined volumes
  1336  		if err == nil && oPrice.EQ(price) {
  1337  			oVolume += volume
  1338  			return oPrice, oVolume, nil
  1339  		}
  1340  	}
  1341  	return price, volume, err
  1342  }
  1343  
  1344  func (b *OrderBook) GetLastTradedPrice() *num.Uint {
  1345  	return b.lastTradedPrice
  1346  }
  1347  
  1348  // PrintState prints the actual state of the book.
  1349  // this should be use only in debug / non production environment as it
  1350  // rely a lot on logging.
  1351  func (b *OrderBook) PrintState(types string) {
  1352  	b.log.Debug("PrintState",
  1353  		logging.String("types", types))
  1354  	b.log.Debug("------------------------------------------------------------")
  1355  	b.log.Debug("                        BUY SIDE                            ")
  1356  	for _, priceLevel := range b.buy.getLevels() {
  1357  		if len(priceLevel.orders) > 0 {
  1358  			priceLevel.print(b.log)
  1359  		}
  1360  	}
  1361  	b.log.Debug("------------------------------------------------------------")
  1362  	b.log.Debug("                        SELL SIDE                           ")
  1363  	for _, priceLevel := range b.sell.getLevels() {
  1364  		if len(priceLevel.orders) > 0 {
  1365  			priceLevel.print(b.log)
  1366  		}
  1367  	}
  1368  	b.log.Debug("------------------------------------------------------------")
  1369  }
  1370  
  1371  // GetTotalNumberOfOrders is a debug/testing function to return the total number of orders in the book.
  1372  func (b *OrderBook) GetTotalNumberOfOrders() int64 {
  1373  	return b.buy.getOrderCount() + b.sell.getOrderCount()
  1374  }
  1375  
  1376  // GetTotalVolume is a debug/testing function to return the total volume in the order book.
  1377  func (b *OrderBook) GetTotalVolume() int64 {
  1378  	return b.buy.getTotalVolume() + b.sell.getTotalVolume()
  1379  }
  1380  
  1381  func (b *OrderBook) Settled() []*types.Order {
  1382  	orders := make([]*types.Order, 0, len(b.ordersByID))
  1383  	for _, v := range b.ordersByID {
  1384  		v.Status = types.OrderStatusStopped
  1385  		orders = append(orders, v)
  1386  	}
  1387  
  1388  	sort.Slice(orders, func(i, j int) bool {
  1389  		return orders[i].ID < orders[j].ID
  1390  	})
  1391  
  1392  	// reset all order stores
  1393  	b.cleanup()
  1394  	b.buy.cleanup()
  1395  	b.sell.cleanup()
  1396  
  1397  	return orders
  1398  }
  1399  
  1400  // GetActivePeggedOrderIDs returns the order identifiers of all pegged orders in the order book that are not parked.
  1401  func (b *OrderBook) GetActivePeggedOrderIDs() []string {
  1402  	pegged := make([]string, 0, b.peggedOrders.Len())
  1403  	for _, ID := range b.peggedOrders.Iter() {
  1404  		if o, ok := b.ordersByID[ID]; ok {
  1405  			if o.Status == vega.Order_STATUS_PARKED {
  1406  				b.log.Panic("unexpected parked pegged order in order book",
  1407  					logging.Order(o))
  1408  			}
  1409  			pegged = append(pegged, o.ID)
  1410  		}
  1411  	}
  1412  	return pegged
  1413  }
  1414  
  1415  func (b *OrderBook) GetVolumeAtPrice(price *num.Uint, side types.Side) uint64 {
  1416  	lvls := b.getSide(side).getLevelsForPrice(price)
  1417  	vol := uint64(0)
  1418  	for _, lvl := range lvls {
  1419  		vol += lvl.volume
  1420  	}
  1421  	return vol
  1422  }
  1423  
  1424  // icebergRefresh will restore the peaks of an iceberg order if they have drifted below the minimum value
  1425  // if not the order remains unchanged.
  1426  func (b *OrderBook) icebergRefresh(o *types.Order) {
  1427  	if !o.IcebergNeedsRefresh() {
  1428  		return
  1429  	}
  1430  
  1431  	if _, err := b.DeleteOrder(o); err != nil {
  1432  		b.log.Panic("could not delete iceberg order during refresh", logging.Error(err), logging.Order(o))
  1433  	}
  1434  
  1435  	// refresh peaks
  1436  	o.SetIcebergPeaks()
  1437  
  1438  	// make sure its active again
  1439  	o.Status = types.OrderStatusActive
  1440  
  1441  	// put it to the back of the line
  1442  	b.getSide(o.Side).addOrder(o)
  1443  	b.add(o)
  1444  }
  1445  
  1446  // remove removes the given order from all the lookup map.
  1447  func (b *OrderBook) remove(o *types.Order) {
  1448  	if ok := b.peggedOrders.Exists(o.ID); ok {
  1449  		b.peggedOrdersCount--
  1450  		b.peggedCountNotify(-1)
  1451  		b.peggedOrders.Delete(o.ID)
  1452  	}
  1453  	delete(b.ordersByID, o.ID)
  1454  	delete(b.ordersPerParty[o.Party], o.ID)
  1455  }
  1456  
  1457  // add adds the given order too all the lookup maps.
  1458  func (b *OrderBook) add(o *types.Order) {
  1459  	if o.GeneratedOffbook {
  1460  		b.log.Panic("Can not add offbook order to the orderbook", logging.Order(o))
  1461  	}
  1462  
  1463  	b.ordersByID[o.ID] = o
  1464  	if orders, ok := b.ordersPerParty[o.Party]; !ok {
  1465  		b.ordersPerParty[o.Party] = map[string]struct{}{
  1466  			o.ID: {},
  1467  		}
  1468  	} else {
  1469  		orders[o.ID] = struct{}{}
  1470  	}
  1471  
  1472  	if o.PeggedOrder != nil {
  1473  		b.peggedOrders.Add(o.ID)
  1474  		b.peggedOrdersCount++
  1475  		b.peggedCountNotify(1)
  1476  	}
  1477  }
  1478  
  1479  // cleanup removes all orders and resets the the order lookup maps.
  1480  func (b *OrderBook) cleanup() {
  1481  	b.ordersByID = map[string]*types.Order{}
  1482  	b.ordersPerParty = map[string]map[string]struct{}{}
  1483  	b.indicativePriceAndVolume = nil
  1484  	b.peggedOrders.Clear()
  1485  	b.peggedCountNotify(-int64(b.peggedOrdersCount))
  1486  	b.peggedOrdersCount = 0
  1487  }
  1488  
  1489  // VWAP returns an error if the total volume for the side of the book is less than the given volume or if there are no levels.
  1490  // Otherwise it returns the volume weighted average price for achieving the given volume.
  1491  func (b *OrderBook) VWAP(volume uint64, side types.Side) (*num.Uint, error) {
  1492  	sidePriceLevels := b.buy
  1493  	if side == types.SideSell {
  1494  		sidePriceLevels = b.sell
  1495  	}
  1496  	dVol := num.DecimalFromInt64(int64(volume))
  1497  	remaining := volume
  1498  	price := num.UintZero()
  1499  	i := len(sidePriceLevels.levels) - 1
  1500  	if i < 0 {
  1501  		return nil, fmt.Errorf("no orders in book for side")
  1502  	}
  1503  
  1504  	if volume == 0 && i >= 0 {
  1505  		return sidePriceLevels.levels[i].price.Clone(), nil
  1506  	}
  1507  
  1508  	for {
  1509  		if i < 0 || remaining == 0 {
  1510  			break
  1511  		}
  1512  		size := remaining
  1513  		if sidePriceLevels.levels[i].volume < remaining {
  1514  			size = sidePriceLevels.levels[i].volume
  1515  		}
  1516  		price.AddSum(num.UintZero().Mul(sidePriceLevels.levels[i].price, num.NewUint(size)))
  1517  		remaining -= size
  1518  		i -= 1
  1519  	}
  1520  
  1521  	if remaining == 0 {
  1522  		res, _ := num.UintFromDecimal(price.ToDecimal().Div(dVol))
  1523  		return res, nil
  1524  	}
  1525  	return nil, fmt.Errorf("insufficient volume in order book")
  1526  }