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 }