code.vegaprotocol.io/vega@v0.79.0/core/matching/orderbook_test.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_test 17 18 import ( 19 "encoding/hex" 20 "fmt" 21 "math/rand" 22 "testing" 23 "time" 24 25 "code.vegaprotocol.io/vega/core/matching" 26 "code.vegaprotocol.io/vega/core/types" 27 "code.vegaprotocol.io/vega/libs/crypto" 28 vgcrypto "code.vegaprotocol.io/vega/libs/crypto" 29 "code.vegaprotocol.io/vega/libs/num" 30 vgrand "code.vegaprotocol.io/vega/libs/rand" 31 "code.vegaprotocol.io/vega/logging" 32 "code.vegaprotocol.io/vega/protos/vega" 33 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 ) 37 38 // launch aggressiveOrder orders from both sides to fully clear the order book. 39 type aggressiveOrderScenario struct { 40 aggressiveOrder *types.Order 41 expectedPassiveOrdersAffected []types.Order 42 expectedTrades []types.Trade 43 } 44 45 func peggedOrderCounterForTest(int64) {} 46 47 type tstOB struct { 48 ob *matching.CachedOrderBook 49 log *logging.Logger 50 } 51 52 func (t *tstOB) Finish() { 53 t.log.Sync() 54 } 55 56 func getCurrentUtcTimestampNano() int64 { 57 return time.Now().UnixNano() 58 } 59 60 func getTestOrderBook(t *testing.T, market string) *tstOB { 61 t.Helper() 62 tob := tstOB{ 63 log: logging.NewTestLogger(), 64 } 65 tob.ob = matching.NewCachedOrderBook(tob.log, matching.NewDefaultConfig(), market, false, peggedOrderCounterForTest) 66 tob.ob.LogRemovedOrdersDebug = true 67 return &tob 68 } 69 70 func TestOrderBook_GetClosePNL(t *testing.T) { 71 t.Run("Get Buy-side close PNL values", getClosePNLBuy) 72 t.Run("Get Sell-side close PNL values", getClosePNLSell) 73 t.Run("Get Incomplete close-out-pnl (check error) - Buy", getClosePNLIncompleteBuy) 74 t.Run("Get Incomplete close-out-pnl (check error) - Sell", getClosePNLIncompleteSell) 75 t.Run("Get Best bid price and volume", testBestBidPriceAndVolume) 76 t.Run("Get Best offer price and volume", testBestOfferPriceAndVolume) 77 } 78 79 func TestOrderBook_CancelBulk(t *testing.T) { 80 t.Run("Cancel all order for a party", cancelAllOrderForAParty) 81 t.Run("Get all order for a party", getAllOrderForAParty) 82 t.Run("Party with no order cancel nothing", partyWithNoOrderCancelNothing) 83 } 84 85 func TestGetVolumeAtPrice(t *testing.T) { 86 market := "testMarket" 87 book := getTestOrderBook(t, market) 88 defer book.Finish() 89 90 buyPrices := []uint64{ 91 90, 92 85, 93 80, 94 75, 95 70, 96 65, 97 50, 98 } 99 sellPrices := []uint64{ 100 100, 101 105, 102 110, 103 120, 104 125, 105 130, 106 150, 107 } 108 // populate a book with buy orders ranging between 50 and 90 109 // sell orders starting at 100, up to 150. All orders have a size of 2 110 orders := getTestOrders(t, market, 2, buyPrices, sellPrices) 111 for _, o := range orders { 112 _, err := book.ob.SubmitOrder(o) 113 assert.NoError(t, err) 114 } 115 t.Run("Getting volume at price with a single price level returns the volume for that price level", func(t *testing.T) { 116 // check the buy side 117 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[0]), types.SideBuy) 118 require.Equal(t, uint64(2), v) 119 // check the sell side 120 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[0]), types.SideSell) 121 require.Equal(t, uint64(2), v) 122 }) 123 t.Run("Getting volume at price containing all price levels returns the total volume on that side of the book", func(t *testing.T) { 124 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[len(buyPrices)-1]), types.SideBuy) 125 exp := uint64(len(buyPrices) * 2) 126 require.Equal(t, exp, v) 127 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[len(sellPrices)-1]), types.SideSell) 128 exp = uint64(len(sellPrices) * 2) 129 require.Equal(t, exp, v) 130 }) 131 t.Run("Getting volume at a price that is out of range returns zero volume", func(t *testing.T) { 132 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[0]+1), types.SideBuy) 133 require.Equal(t, uint64(0), v) 134 // check the sell side 135 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[0]-1), types.SideSell) 136 require.Equal(t, uint64(0), v) 137 }) 138 t.Run("Getting volume at price allowing for more than all price levels returns the total volume on that side of the book", func(t *testing.T) { 139 // lowest buy order -1 140 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[len(buyPrices)-1]-1), types.SideBuy) 141 exp := uint64(len(buyPrices) * 2) 142 require.Equal(t, exp, v) 143 // highest sell order on the book +1 144 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[len(sellPrices)-1]+1), types.SideSell) 145 exp = uint64(len(sellPrices) * 2) 146 require.Equal(t, exp, v) 147 }) 148 t.Run("Getting volume at a price that is somewhere in the middle returns the correct volume", func(t *testing.T) { 149 idx := len(buyPrices) / 2 150 // remember: includes 0 idx 151 exp := uint64(idx*2 + 2) 152 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[idx]), types.SideBuy) 153 require.Equal(t, exp, v) 154 idx = len(sellPrices) / 2 155 exp = uint64(idx*2 + 2) 156 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[idx]), types.SideSell) 157 require.Equal(t, exp, v) 158 }) 159 } 160 161 func TestGetVolumeAtPriceIceberg(t *testing.T) { 162 market := "testMarket" 163 book := getTestOrderBook(t, market) 164 defer book.Finish() 165 166 buyPrices := []uint64{ 167 90, 168 85, 169 80, 170 75, 171 70, 172 65, 173 50, 174 } 175 sellPrices := []uint64{ 176 100, 177 105, 178 110, 179 120, 180 125, 181 130, 182 150, 183 } 184 // populate a book with buy orders ranging between 50 and 90 185 // sell orders starting at 100, up to 150. All orders are iceberg orders with a visible size of 2, hidden size of 2. 186 orders := getTestOrders(t, market, 4, buyPrices, sellPrices) 187 for _, o := range orders { 188 // make this an iceberg order 189 o.IcebergOrder = &types.IcebergOrder{ 190 PeakSize: 2, 191 MinimumVisibleSize: 2, 192 } 193 _, err := book.ob.SubmitOrder(o) 194 assert.NoError(t, err) 195 } 196 t.Run("Getting volume at price with a single price level returns the volume for that price level", func(t *testing.T) { 197 // check the buy side 198 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[0]), types.SideBuy) 199 require.Equal(t, uint64(4), v) 200 // check the sell side 201 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[0]), types.SideSell) 202 require.Equal(t, uint64(4), v) 203 }) 204 t.Run("Getting volume at price containing all price levels returns the total volume on that side of the book", func(t *testing.T) { 205 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[len(buyPrices)-1]), types.SideBuy) 206 exp := uint64(len(buyPrices) * 4) 207 require.Equal(t, exp, v) 208 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[len(sellPrices)-1]), types.SideSell) 209 exp = uint64(len(sellPrices) * 4) 210 require.Equal(t, exp, v) 211 }) 212 t.Run("Getting volume at a price that is out of range returns zero volume", func(t *testing.T) { 213 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[0]+1), types.SideBuy) 214 require.Equal(t, uint64(0), v) 215 // check the sell side 216 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[0]-1), types.SideSell) 217 require.Equal(t, uint64(0), v) 218 }) 219 t.Run("Getting volume at price allowing for more than all price levels returns the total volume on that side of the book", func(t *testing.T) { 220 // lowest buy order -1 221 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[len(buyPrices)-1]-1), types.SideBuy) 222 exp := uint64(len(buyPrices) * 4) 223 require.Equal(t, exp, v) 224 // highest sell order on the book +1 225 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[len(sellPrices)-1]+1), types.SideSell) 226 exp = uint64(len(sellPrices) * 4) 227 require.Equal(t, exp, v) 228 }) 229 t.Run("Getting volume at a price that is somewhere in the middle returns the correct volume", func(t *testing.T) { 230 idx := len(buyPrices) / 2 231 // remember: includes 0 idx 232 exp := uint64(idx*4 + 4) 233 v := book.ob.GetVolumeAtPrice(num.NewUint(buyPrices[idx]), types.SideBuy) 234 require.Equal(t, exp, v) 235 idx = len(sellPrices) / 2 236 exp = uint64(idx*4 + 4) 237 v = book.ob.GetVolumeAtPrice(num.NewUint(sellPrices[idx]), types.SideSell) 238 require.Equal(t, exp, v) 239 }) 240 } 241 242 func TestHash(t *testing.T) { 243 market := "testMarket" 244 book := getTestOrderBook(t, market) 245 defer book.Finish() 246 247 orders := []*types.Order{ 248 { 249 ID: "1111111111111111111111", 250 Status: types.OrderStatusActive, 251 Type: types.OrderTypeLimit, 252 MarketID: market, 253 Party: "A", 254 Side: types.SideBuy, 255 Price: num.NewUint(10), 256 OriginalPrice: num.NewUint(10), 257 Size: 1, 258 Remaining: 1, 259 TimeInForce: types.OrderTimeInForceGTC, 260 }, 261 { 262 ID: "2222222222222222222222", 263 Status: types.OrderStatusActive, 264 Type: types.OrderTypeLimit, 265 MarketID: market, 266 Party: "A", 267 Side: types.SideBuy, 268 Price: num.NewUint(30), 269 OriginalPrice: num.NewUint(30), 270 Size: 5, 271 Remaining: 5, 272 TimeInForce: types.OrderTimeInForceGTC, 273 }, 274 { 275 ID: "3333333333333333333333", 276 Status: types.OrderStatusActive, 277 Type: types.OrderTypeLimit, 278 MarketID: market, 279 Party: "B", 280 Side: types.SideSell, 281 Price: num.NewUint(200), 282 OriginalPrice: num.NewUint(200), 283 Size: 1, 284 Remaining: 1, 285 TimeInForce: types.OrderTimeInForceGTC, 286 }, 287 { 288 ID: "4444444444444444444444", 289 Status: types.OrderStatusActive, 290 Type: types.OrderTypeLimit, 291 MarketID: market, 292 Party: "A", 293 Side: types.SideSell, 294 Price: num.NewUint(400), 295 OriginalPrice: num.NewUint(400), 296 Size: 10, 297 Remaining: 10, 298 TimeInForce: types.OrderTimeInForceGTC, 299 }, 300 } 301 302 for _, o := range orders { 303 _, err := book.ob.SubmitOrder(o) 304 assert.NoError(t, err) 305 } 306 307 hash := book.ob.Hash() 308 require.Equal(t, 309 "fc0073b33273253dd021d4fdd330e00c32c8b484c8bb484abac92acfb9d575bf", 310 hex.EncodeToString(hash), 311 "It should match against the known hash", 312 ) 313 // compute the hash 100 times for determinism verification 314 for i := 0; i < 100; i++ { 315 got := book.ob.Hash() 316 require.Equal(t, hash, got) 317 } 318 } 319 320 func cancelAllOrderForAParty(t *testing.T) { 321 market := "testMarket" 322 book := getTestOrderBook(t, market) 323 defer book.Finish() 324 orderID1 := vgcrypto.RandomHash() 325 orderID2 := vgcrypto.RandomHash() 326 orderID3 := vgcrypto.RandomHash() 327 orderID4 := vgcrypto.RandomHash() 328 329 orders := []*types.Order{ 330 { 331 ID: orderID1, 332 Status: types.OrderStatusActive, 333 Type: types.OrderTypeLimit, 334 MarketID: market, 335 Party: "A", 336 Side: types.SideBuy, 337 Price: num.NewUint(100), 338 OriginalPrice: num.NewUint(100), 339 Size: 1, 340 Remaining: 1, 341 TimeInForce: types.OrderTimeInForceGTC, 342 }, 343 { 344 ID: orderID2, 345 Status: types.OrderStatusActive, 346 Type: types.OrderTypeLimit, 347 MarketID: market, 348 Party: "A", 349 Side: types.SideBuy, 350 Price: num.NewUint(300), 351 OriginalPrice: num.NewUint(300), 352 Size: 5, 353 Remaining: 5, 354 TimeInForce: types.OrderTimeInForceGTC, 355 }, 356 { 357 ID: orderID3, 358 Status: types.OrderStatusActive, 359 Type: types.OrderTypeLimit, 360 MarketID: market, 361 Party: "B", 362 Side: types.SideBuy, 363 Price: num.NewUint(200), 364 OriginalPrice: num.NewUint(200), 365 Size: 1, 366 Remaining: 1, 367 TimeInForce: types.OrderTimeInForceGTC, 368 }, 369 { 370 ID: orderID4, 371 Status: types.OrderStatusActive, 372 Type: types.OrderTypeLimit, 373 MarketID: market, 374 Party: "A", 375 Side: types.SideBuy, 376 Price: num.NewUint(300), 377 OriginalPrice: num.NewUint(300), 378 Size: 10, 379 Remaining: 10, 380 TimeInForce: types.OrderTimeInForceGTC, 381 }, 382 } 383 for _, o := range orders { 384 confirm, err := book.ob.SubmitOrder(o) 385 assert.NoError(t, err) 386 assert.Equal(t, 0, len(confirm.Trades)) 387 } 388 confs, err := book.ob.CancelAllOrders("A") 389 assert.NoError(t, err) 390 assert.Len(t, confs, 3) 391 expectedIDs := map[string]struct{}{ 392 orderID1: {}, 393 orderID2: {}, 394 orderID4: {}, 395 } 396 for _, conf := range confs { 397 if _, ok := expectedIDs[conf.Order.ID]; ok { 398 delete(expectedIDs, conf.Order.ID) 399 } else { 400 t.Fatalf("unexpected order has been cancelled %v", conf.Order) 401 } 402 } 403 } 404 405 func getAllOrderForAParty(t *testing.T) { 406 market := "testMarket" 407 book := getTestOrderBook(t, market) 408 defer book.Finish() 409 410 orderID1 := vgcrypto.RandomHash() 411 orderID2 := vgcrypto.RandomHash() 412 orderID3 := vgcrypto.RandomHash() 413 orderID4 := vgcrypto.RandomHash() 414 415 orders := []*types.Order{ 416 { 417 ID: orderID1, 418 Status: types.OrderStatusActive, 419 Type: types.OrderTypeLimit, 420 MarketID: market, 421 Party: "A", 422 Side: types.SideBuy, 423 Price: num.NewUint(100), 424 OriginalPrice: num.NewUint(100), 425 Size: 1, 426 Remaining: 1, 427 TimeInForce: types.OrderTimeInForceGTC, 428 }, 429 { 430 ID: orderID2, 431 Status: types.OrderStatusActive, 432 Type: types.OrderTypeLimit, 433 MarketID: market, 434 Party: "A", 435 Side: types.SideBuy, 436 Price: num.NewUint(300), 437 OriginalPrice: num.NewUint(300), 438 Size: 5, 439 Remaining: 5, 440 TimeInForce: types.OrderTimeInForceGTC, 441 }, 442 { 443 ID: orderID3, 444 Status: types.OrderStatusActive, 445 Type: types.OrderTypeLimit, 446 MarketID: market, 447 Party: "B", 448 Side: types.SideBuy, 449 Price: num.NewUint(200), 450 OriginalPrice: num.NewUint(200), 451 Size: 1, 452 Remaining: 1, 453 TimeInForce: types.OrderTimeInForceGTC, 454 }, 455 { 456 ID: orderID4, 457 Status: types.OrderStatusActive, 458 Type: types.OrderTypeLimit, 459 MarketID: market, 460 Party: "A", 461 Side: types.SideBuy, 462 Price: num.NewUint(300), 463 OriginalPrice: num.NewUint(300), 464 Size: 10, 465 Remaining: 10, 466 TimeInForce: types.OrderTimeInForceGTC, 467 }, 468 } 469 for _, o := range orders { 470 confirm, err := book.ob.SubmitOrder(o) 471 assert.NoError(t, err) 472 assert.Equal(t, 0, len(confirm.Trades)) 473 } 474 ordersLs := book.ob.GetOrdersPerParty("A") 475 assert.Len(t, ordersLs, 3) 476 expectedIDs := map[string]struct{}{ 477 orderID1: {}, 478 orderID2: {}, 479 orderID4: {}, 480 } 481 for _, o := range ordersLs { 482 if _, ok := expectedIDs[o.ID]; ok { 483 delete(expectedIDs, o.ID) 484 } else { 485 t.Fatalf("unexpected order has been cancelled %v", o) 486 } 487 } 488 } 489 490 func partyWithNoOrderCancelNothing(t *testing.T) { 491 market := "testMarket" 492 book := getTestOrderBook(t, market) 493 defer book.Finish() 494 orders := []*types.Order{ 495 { 496 ID: vgcrypto.RandomHash(), 497 Status: types.OrderStatusActive, 498 Type: types.OrderTypeLimit, 499 MarketID: market, 500 Party: "A", 501 Side: types.SideBuy, 502 Price: num.NewUint(100), 503 OriginalPrice: num.NewUint(100), 504 Size: 1, 505 Remaining: 1, 506 TimeInForce: types.OrderTimeInForceGTC, 507 }, 508 { 509 ID: vgcrypto.RandomHash(), 510 Status: types.OrderStatusActive, 511 Type: types.OrderTypeLimit, 512 MarketID: market, 513 Party: "A", 514 Side: types.SideBuy, 515 Price: num.NewUint(300), 516 OriginalPrice: num.NewUint(300), 517 Size: 5, 518 Remaining: 5, 519 TimeInForce: types.OrderTimeInForceGTC, 520 }, 521 { 522 ID: vgcrypto.RandomHash(), 523 Status: types.OrderStatusActive, 524 Type: types.OrderTypeLimit, 525 MarketID: market, 526 Party: "B", 527 Side: types.SideBuy, 528 Price: num.NewUint(200), 529 OriginalPrice: num.NewUint(200), 530 Size: 1, 531 Remaining: 1, 532 TimeInForce: types.OrderTimeInForceGTC, 533 }, 534 { 535 ID: vgcrypto.RandomHash(), 536 Status: types.OrderStatusActive, 537 Type: types.OrderTypeLimit, 538 MarketID: market, 539 Party: "A", 540 Side: types.SideBuy, 541 Price: num.NewUint(300), 542 OriginalPrice: num.NewUint(300), 543 Size: 10, 544 Remaining: 10, 545 TimeInForce: types.OrderTimeInForceGTC, 546 }, 547 } 548 for _, o := range orders { 549 confirm, err := book.ob.SubmitOrder(o) 550 assert.NoError(t, err) 551 assert.Equal(t, 0, len(confirm.Trades)) 552 } 553 ordersLs := book.ob.GetOrdersPerParty("X") 554 assert.Len(t, ordersLs, 0) 555 } 556 557 func testBestBidPriceAndVolume(t *testing.T) { 558 market := "testMarket" 559 book := getTestOrderBook(t, market) 560 defer book.Finish() 561 // 3 orders of size 1, 3 different prices 562 orders := []*types.Order{ 563 { 564 ID: vgcrypto.RandomHash(), 565 Status: types.OrderStatusActive, 566 Type: types.OrderTypeLimit, 567 MarketID: market, 568 Party: "A", 569 Side: types.SideBuy, 570 Price: num.NewUint(100), 571 OriginalPrice: num.NewUint(100), 572 Size: 1, 573 Remaining: 1, 574 TimeInForce: types.OrderTimeInForceGTC, 575 }, 576 { 577 ID: vgcrypto.RandomHash(), 578 Status: types.OrderStatusActive, 579 Type: types.OrderTypeLimit, 580 MarketID: market, 581 Party: "B", 582 Side: types.SideBuy, 583 Price: num.NewUint(300), 584 OriginalPrice: num.NewUint(300), 585 Size: 5, 586 Remaining: 5, 587 TimeInForce: types.OrderTimeInForceGTC, 588 }, 589 { 590 ID: vgcrypto.RandomHash(), 591 Status: types.OrderStatusActive, 592 Type: types.OrderTypeLimit, 593 MarketID: market, 594 Party: "B", 595 Side: types.SideBuy, 596 Price: num.NewUint(200), 597 OriginalPrice: num.NewUint(200), 598 Size: 1, 599 Remaining: 1, 600 TimeInForce: types.OrderTimeInForceGTC, 601 }, 602 { 603 ID: vgcrypto.RandomHash(), 604 Status: types.OrderStatusActive, 605 Type: types.OrderTypeLimit, 606 MarketID: market, 607 Party: "d", 608 Side: types.SideBuy, 609 Price: num.NewUint(300), 610 OriginalPrice: num.NewUint(300), 611 Size: 10, 612 Remaining: 10, 613 TimeInForce: types.OrderTimeInForceGTC, 614 }, 615 } 616 for _, o := range orders { 617 trades, getErr := book.ob.GetTrades(o) 618 assert.NoError(t, getErr) 619 confirm, err := book.ob.SubmitOrder(o) 620 assert.NoError(t, err) 621 assert.Equal(t, 0, len(confirm.Trades)) 622 assert.Equal(t, len(confirm.Trades), len(trades)) 623 } 624 625 price, volume, err := book.ob.BestBidPriceAndVolume() 626 assert.NoError(t, err) 627 assert.Equal(t, uint64(300), price.Uint64()) 628 assert.Equal(t, uint64(15), volume) 629 } 630 631 func testBestOfferPriceAndVolume(t *testing.T) { 632 market := "testMarket" 633 book := getTestOrderBook(t, market) 634 defer book.Finish() 635 // 3 orders of size 1, 3 different prices 636 orders := []*types.Order{ 637 { 638 ID: vgcrypto.RandomHash(), 639 Status: types.OrderStatusActive, 640 Type: types.OrderTypeLimit, 641 MarketID: market, 642 Party: "A", 643 Side: types.SideSell, 644 Price: num.NewUint(100), 645 OriginalPrice: num.NewUint(100), 646 Size: 1, 647 Remaining: 1, 648 TimeInForce: types.OrderTimeInForceGTC, 649 }, 650 { 651 ID: vgcrypto.RandomHash(), 652 Status: types.OrderStatusActive, 653 Type: types.OrderTypeLimit, 654 MarketID: market, 655 Party: "B", 656 Side: types.SideSell, 657 Price: num.NewUint(10), 658 OriginalPrice: num.NewUint(10), 659 Size: 5, 660 Remaining: 5, 661 TimeInForce: types.OrderTimeInForceGTC, 662 }, 663 { 664 ID: vgcrypto.RandomHash(), 665 Status: types.OrderStatusActive, 666 Type: types.OrderTypeLimit, 667 MarketID: market, 668 Party: "B", 669 Side: types.SideSell, 670 Price: num.NewUint(200), 671 OriginalPrice: num.NewUint(200), 672 Size: 1, 673 Remaining: 1, 674 TimeInForce: types.OrderTimeInForceGTC, 675 }, 676 { 677 ID: vgcrypto.RandomHash(), 678 Status: types.OrderStatusActive, 679 Type: types.OrderTypeLimit, 680 MarketID: market, 681 Party: "d", 682 Side: types.SideSell, 683 Price: num.NewUint(10), 684 OriginalPrice: num.NewUint(10), 685 Size: 10, 686 Remaining: 10, 687 TimeInForce: types.OrderTimeInForceGTC, 688 }, 689 } 690 for _, o := range orders { 691 trades, getErr := book.ob.GetTrades(o) 692 assert.NoError(t, getErr) 693 confirm, err := book.ob.SubmitOrder(o) 694 assert.NoError(t, err) 695 assert.Equal(t, 0, len(confirm.Trades)) 696 assert.Equal(t, len(trades), len(confirm.Trades)) 697 } 698 699 price, volume, err := book.ob.BestOfferPriceAndVolume() 700 assert.NoError(t, err) 701 assert.Equal(t, uint64(10), price.Uint64()) 702 assert.Equal(t, uint64(15), volume) 703 } 704 705 func getClosePNLIncompleteBuy(t *testing.T) { 706 market := "testMarket" 707 book := getTestOrderBook(t, market) 708 defer book.Finish() 709 // 3 orders of size 1, 3 different prices 710 orders := []*types.Order{ 711 { 712 ID: vgcrypto.RandomHash(), 713 Status: types.OrderStatusActive, 714 Type: types.OrderTypeLimit, 715 MarketID: market, 716 Party: "A", 717 Side: types.SideBuy, 718 Price: num.NewUint(100), 719 OriginalPrice: num.NewUint(100), 720 Size: 1, 721 Remaining: 1, 722 TimeInForce: types.OrderTimeInForceGTC, 723 CreatedAt: 0, 724 }, 725 { 726 ID: vgcrypto.RandomHash(), 727 Status: types.OrderStatusActive, 728 Type: types.OrderTypeLimit, 729 MarketID: market, 730 Party: "B", 731 Side: types.SideBuy, 732 Price: num.NewUint(110), 733 OriginalPrice: num.NewUint(110), 734 Size: 1, 735 Remaining: 1, 736 TimeInForce: types.OrderTimeInForceGTC, 737 CreatedAt: 0, 738 }, 739 } 740 for _, o := range orders { 741 trades, getErr := book.ob.GetTrades(o) 742 assert.NoError(t, getErr) 743 confirm, err := book.ob.SubmitOrder(o) 744 assert.NoError(t, err) 745 assert.Equal(t, 0, len(confirm.Trades)) 746 assert.Equal(t, len(trades), len(confirm.Trades)) 747 } 748 // volume + expected price 749 callExp := map[uint64]uint64{ 750 2: 210 / 2, 751 1: 110, 752 } 753 // this calculates the actual volume 754 for vol, exp := range callExp { 755 price, err := book.ob.GetFillPrice(vol, types.SideBuy) 756 assert.Equal(t, exp, price.Uint64()) 757 assert.NoError(t, err) 758 } 759 price, err := book.ob.GetFillPrice(3, types.SideBuy) 760 assert.Equal(t, callExp[2], price.Uint64()) 761 assert.Equal(t, matching.ErrNotEnoughOrders, err) 762 } 763 764 func getClosePNLIncompleteSell(t *testing.T) { 765 market := "testMarket" 766 book := getTestOrderBook(t, market) 767 defer book.Finish() 768 // 3 orders of size 1, 3 different prices 769 orders := []*types.Order{ 770 { 771 ID: vgcrypto.RandomHash(), 772 Status: types.OrderStatusActive, 773 Type: types.OrderTypeLimit, 774 MarketID: market, 775 Party: "A", 776 Side: types.SideSell, 777 Price: num.NewUint(100), 778 OriginalPrice: num.NewUint(100), 779 Size: 1, 780 Remaining: 1, 781 TimeInForce: types.OrderTimeInForceGTC, 782 CreatedAt: 0, 783 }, 784 { 785 ID: vgcrypto.RandomHash(), 786 Status: types.OrderStatusActive, 787 Type: types.OrderTypeLimit, 788 MarketID: market, 789 Party: "B", 790 Side: types.SideSell, 791 Price: num.NewUint(110), 792 OriginalPrice: num.NewUint(110), 793 Size: 1, 794 Remaining: 1, 795 TimeInForce: types.OrderTimeInForceGTC, 796 CreatedAt: 0, 797 }, 798 } 799 for _, o := range orders { 800 trades, getErr := book.ob.GetTrades(o) 801 assert.NoError(t, getErr) 802 confirm, err := book.ob.SubmitOrder(o) 803 assert.NoError(t, err) 804 assert.Equal(t, 0, len(confirm.Trades)) 805 assert.Equal(t, len(trades), len(confirm.Trades)) 806 } 807 // volume + expected price 808 callExp := map[uint64]uint64{ 809 2: 210 / 2, 810 1: 100, 811 } 812 // this calculates the actual volume 813 for vol, exp := range callExp { 814 price, err := book.ob.GetFillPrice(vol, types.SideSell) 815 assert.Equal(t, exp, price.Uint64()) 816 assert.NoError(t, err) 817 } 818 price, err := book.ob.GetFillPrice(3, types.SideSell) 819 assert.Equal(t, callExp[2], price.Uint64()) 820 assert.Equal(t, matching.ErrNotEnoughOrders, err) 821 } 822 823 func getClosePNLBuy(t *testing.T) { 824 market := "testMarket" 825 book := getTestOrderBook(t, market) 826 defer book.Finish() 827 // 3 orders of size 1, 3 different prices 828 orders := []*types.Order{ 829 { 830 ID: vgcrypto.RandomHash(), 831 Status: types.OrderStatusActive, 832 Type: types.OrderTypeLimit, 833 MarketID: market, 834 Party: "A", 835 Side: types.SideBuy, 836 Price: num.NewUint(100), 837 OriginalPrice: num.NewUint(100), 838 Size: 1, 839 Remaining: 1, 840 TimeInForce: types.OrderTimeInForceGTC, 841 CreatedAt: 0, 842 }, 843 { 844 ID: vgcrypto.RandomHash(), 845 Status: types.OrderStatusActive, 846 Type: types.OrderTypeLimit, 847 MarketID: market, 848 Party: "B", 849 Side: types.SideBuy, 850 Price: num.NewUint(110), 851 OriginalPrice: num.NewUint(110), 852 Size: 1, 853 Remaining: 1, 854 TimeInForce: types.OrderTimeInForceGTC, 855 CreatedAt: 0, 856 }, 857 { 858 ID: vgcrypto.RandomHash(), 859 Status: types.OrderStatusActive, 860 Type: types.OrderTypeLimit, 861 MarketID: market, 862 Party: "C", 863 Side: types.SideBuy, 864 Price: num.NewUint(120), 865 OriginalPrice: num.NewUint(120), 866 Size: 1, 867 Remaining: 1, 868 TimeInForce: types.OrderTimeInForceGTC, 869 CreatedAt: 0, 870 }, 871 } 872 for _, o := range orders { 873 trades, getErr := book.ob.GetTrades(o) 874 assert.NoError(t, getErr) 875 confirm, err := book.ob.SubmitOrder(o) 876 assert.NoError(t, err) 877 assert.Equal(t, 0, len(confirm.Trades)) 878 assert.Equal(t, len(trades), len(confirm.Trades)) 879 } 880 // volume + expected price 881 callExp := map[uint64]uint64{ 882 3: 330 / 3, 883 2: 230 / 2, 884 1: 120, 885 } 886 // this calculates the actual volume 887 for vol, exp := range callExp { 888 price, err := book.ob.GetFillPrice(vol, types.SideBuy) 889 assert.Equal(t, exp, price.Uint64()) 890 assert.NoError(t, err) 891 } 892 } 893 894 func getClosePNLSell(t *testing.T) { 895 market := "testMarket" 896 book := getTestOrderBook(t, market) 897 defer book.Finish() 898 // 3 orders of size 1, 3 different prices 899 orders := []*types.Order{ 900 { 901 ID: vgcrypto.RandomHash(), 902 Status: types.OrderStatusActive, 903 Type: types.OrderTypeLimit, 904 MarketID: market, 905 Party: "A", 906 Side: types.SideSell, 907 Price: num.NewUint(100), 908 OriginalPrice: num.NewUint(100), 909 Size: 1, 910 Remaining: 1, 911 TimeInForce: types.OrderTimeInForceGTC, 912 CreatedAt: 0, 913 }, 914 { 915 ID: vgcrypto.RandomHash(), 916 Status: types.OrderStatusActive, 917 Type: types.OrderTypeLimit, 918 MarketID: market, 919 Party: "B", 920 Side: types.SideSell, 921 Price: num.NewUint(110), 922 OriginalPrice: num.NewUint(110), 923 Size: 1, 924 Remaining: 1, 925 TimeInForce: types.OrderTimeInForceGTC, 926 CreatedAt: 0, 927 }, 928 { 929 ID: vgcrypto.RandomHash(), 930 Status: types.OrderStatusActive, 931 Type: types.OrderTypeLimit, 932 MarketID: market, 933 Party: "C", 934 Side: types.SideSell, 935 Price: num.NewUint(120), 936 OriginalPrice: num.NewUint(120), 937 Size: 1, 938 Remaining: 1, 939 TimeInForce: types.OrderTimeInForceGTC, 940 CreatedAt: 0, 941 }, 942 } 943 for _, o := range orders { 944 trades, getErr := book.ob.GetTrades(o) 945 assert.NoError(t, getErr) 946 confirm, err := book.ob.SubmitOrder(o) 947 assert.NoError(t, err) 948 assert.Equal(t, 0, len(confirm.Trades)) 949 assert.Equal(t, len(trades), len(confirm.Trades)) 950 } 951 // volume + expected price 952 callExp := map[uint64]uint64{ 953 3: 330 / 3, 954 2: 210 / 2, 955 1: 100, 956 } 957 // this calculates the actual volume 958 for vol, exp := range callExp { 959 price, err := book.ob.GetFillPrice(vol, types.SideSell) 960 assert.NoError(t, err) 961 assert.Equal(t, exp, price.Uint64()) 962 } 963 } 964 965 func TestOrderBook_CancelReturnsTheOrderFromTheBook(t *testing.T) { 966 market := "cancel-returns-order" 967 party := "p1" 968 969 book := getTestOrderBook(t, market) 970 defer book.Finish() 971 currentTimestamp := getCurrentUtcTimestampNano() 972 973 orderID := vgcrypto.RandomHash() 974 order1 := types.Order{ 975 Status: types.OrderStatusActive, 976 Type: types.OrderTypeLimit, 977 MarketID: market, 978 Party: party, 979 Side: types.SideSell, 980 Price: num.NewUint(1), 981 OriginalPrice: num.NewUint(1), 982 Size: 100, 983 Remaining: 100, 984 TimeInForce: types.OrderTimeInForceGTC, 985 CreatedAt: currentTimestamp, 986 ID: orderID, 987 } 988 order2 := types.Order{ 989 Status: types.OrderStatusActive, 990 Type: types.OrderTypeLimit, 991 MarketID: market, 992 Party: party, 993 Side: types.SideSell, 994 Price: num.NewUint(1), 995 OriginalPrice: num.NewUint(1), 996 Size: 100, 997 Remaining: 1, // use a wrong remaining here to get the order from the book 998 TimeInForce: types.OrderTimeInForceGTC, 999 CreatedAt: currentTimestamp, 1000 ID: orderID, 1001 } 1002 1003 trades, getErr := book.ob.GetTrades(&order1) 1004 assert.NoError(t, getErr) 1005 confirm, err := book.ob.SubmitOrder(&order1) 1006 assert.Equal(t, err, nil) 1007 assert.Equal(t, len(trades), len(confirm.Trades)) 1008 1009 o, err := book.ob.CancelOrder(&order2) 1010 assert.Equal(t, err, nil) 1011 assert.Equal(t, o.Order.Remaining, order1.Remaining) 1012 } 1013 1014 func TestOrderBook_RemoveExpiredOrders(t *testing.T) { 1015 market := "expiringOrderBookTest" 1016 party := "clay-davis" 1017 1018 book := getTestOrderBook(t, market) 1019 defer book.Finish() 1020 currentTimestamp := getCurrentUtcTimestampNano() 1021 someTimeLater := currentTimestamp + (1000 * 1000) 1022 1023 order1 := &types.Order{ 1024 Status: types.OrderStatusActive, 1025 Type: types.OrderTypeLimit, 1026 MarketID: market, 1027 Party: party, 1028 Side: types.SideSell, 1029 Price: num.NewUint(1), 1030 OriginalPrice: num.NewUint(1), 1031 Size: 1, 1032 Remaining: 1, 1033 TimeInForce: types.OrderTimeInForceGTT, 1034 CreatedAt: currentTimestamp, 1035 ExpiresAt: someTimeLater, 1036 ID: "1", 1037 } 1038 trades, getErr := book.ob.GetTrades(order1) 1039 assert.NoError(t, getErr) 1040 confirm, err := book.ob.SubmitOrder(order1) 1041 assert.Equal(t, err, nil) 1042 assert.Equal(t, len(trades), len(confirm.Trades)) 1043 1044 order2 := &types.Order{ 1045 Status: types.OrderStatusActive, 1046 Type: types.OrderTypeLimit, 1047 MarketID: market, 1048 Party: party, 1049 Side: types.SideSell, 1050 Price: num.NewUint(3298), 1051 OriginalPrice: num.NewUint(3298), 1052 Size: 99, 1053 Remaining: 99, 1054 TimeInForce: types.OrderTimeInForceGTT, 1055 CreatedAt: currentTimestamp, 1056 ExpiresAt: someTimeLater + 1, 1057 ID: "2", 1058 } 1059 trades, getErr = book.ob.GetTrades(order2) 1060 assert.NoError(t, getErr) 1061 confirm, err = book.ob.SubmitOrder(order2) 1062 assert.Equal(t, err, nil) 1063 assert.Equal(t, len(trades), len(confirm.Trades)) 1064 1065 order3 := &types.Order{ 1066 Status: types.OrderStatusActive, 1067 Type: types.OrderTypeLimit, 1068 MarketID: market, 1069 Party: party, 1070 Side: types.SideSell, 1071 Price: num.NewUint(771), 1072 OriginalPrice: num.NewUint(771), 1073 Size: 19, 1074 Remaining: 19, 1075 TimeInForce: types.OrderTimeInForceGTT, 1076 CreatedAt: currentTimestamp, 1077 ExpiresAt: someTimeLater, 1078 ID: "3", 1079 } 1080 trades, getErr = book.ob.GetTrades(order3) 1081 assert.NoError(t, getErr) 1082 confirm, err = book.ob.SubmitOrder(order3) 1083 assert.Equal(t, err, nil) 1084 assert.Equal(t, len(trades), len(confirm.Trades)) 1085 1086 order4 := &types.Order{ 1087 Status: types.OrderStatusActive, 1088 Type: types.OrderTypeLimit, 1089 MarketID: market, 1090 Party: party, 1091 Side: types.SideSell, 1092 Price: num.NewUint(1000), 1093 OriginalPrice: num.NewUint(1000), 1094 Size: 7, 1095 Remaining: 7, 1096 TimeInForce: types.OrderTimeInForceGTC, 1097 CreatedAt: currentTimestamp, 1098 ID: "4", 1099 } 1100 trades, getErr = book.ob.GetTrades(order4) 1101 assert.NoError(t, getErr) 1102 confirm, err = book.ob.SubmitOrder(order4) 1103 assert.Equal(t, err, nil) 1104 assert.Equal(t, len(trades), len(confirm.Trades)) 1105 1106 order5 := &types.Order{ 1107 Status: types.OrderStatusActive, 1108 Type: types.OrderTypeLimit, 1109 MarketID: market, 1110 Party: party, 1111 Side: types.SideSell, 1112 Price: num.NewUint(199), 1113 OriginalPrice: num.NewUint(199), 1114 Size: 99999, 1115 Remaining: 99999, 1116 TimeInForce: types.OrderTimeInForceGTT, 1117 CreatedAt: currentTimestamp, 1118 ExpiresAt: someTimeLater, 1119 ID: "5", 1120 } 1121 1122 trades, getErr = book.ob.GetTrades(order5) 1123 assert.NoError(t, getErr) 1124 confirm, err = book.ob.SubmitOrder(order5) 1125 assert.Equal(t, err, nil) 1126 assert.Equal(t, len(trades), len(confirm.Trades)) 1127 1128 order6 := &types.Order{ 1129 Status: types.OrderStatusActive, 1130 Type: types.OrderTypeLimit, 1131 MarketID: market, 1132 Party: party, 1133 Side: types.SideSell, 1134 Price: num.NewUint(100), 1135 OriginalPrice: num.NewUint(100), 1136 Size: 100, 1137 Remaining: 100, 1138 TimeInForce: types.OrderTimeInForceGTC, 1139 CreatedAt: currentTimestamp, 1140 ID: "6", 1141 } 1142 trades, getErr = book.ob.GetTrades(order6) 1143 assert.NoError(t, getErr) 1144 confirm, err = book.ob.SubmitOrder(order6) 1145 assert.Equal(t, err, nil) 1146 assert.Equal(t, len(trades), len(confirm.Trades)) 1147 1148 order7 := &types.Order{ 1149 Status: types.OrderStatusActive, 1150 Type: types.OrderTypeLimit, 1151 MarketID: market, 1152 Party: party, 1153 Side: types.SideSell, 1154 Price: num.NewUint(41), 1155 OriginalPrice: num.NewUint(41), 1156 Size: 9999, 1157 Remaining: 9999, 1158 TimeInForce: types.OrderTimeInForceGTT, 1159 CreatedAt: currentTimestamp, 1160 ExpiresAt: someTimeLater + 9999, 1161 ID: "7", 1162 } 1163 trades, getErr = book.ob.GetTrades(order7) 1164 assert.NoError(t, getErr) 1165 confirm, err = book.ob.SubmitOrder(order7) 1166 assert.Equal(t, err, nil) 1167 assert.Equal(t, len(trades), len(confirm.Trades)) 1168 1169 order8 := &types.Order{ 1170 Status: types.OrderStatusActive, 1171 Type: types.OrderTypeLimit, 1172 MarketID: market, 1173 Party: party, 1174 Side: types.SideSell, 1175 Price: num.NewUint(1), 1176 OriginalPrice: num.NewUint(1), 1177 Size: 1, 1178 Remaining: 1, 1179 TimeInForce: types.OrderTimeInForceGTT, 1180 CreatedAt: currentTimestamp, 1181 ExpiresAt: someTimeLater - 9999, 1182 ID: "8", 1183 } 1184 trades, getErr = book.ob.GetTrades(order8) 1185 assert.NoError(t, getErr) 1186 confirm, err = book.ob.SubmitOrder(order8) 1187 assert.Equal(t, err, nil) 1188 assert.Equal(t, len(trades), len(confirm.Trades)) 1189 1190 order9 := &types.Order{ 1191 Status: types.OrderStatusActive, 1192 Type: types.OrderTypeLimit, 1193 MarketID: market, 1194 Party: party, 1195 Side: types.SideSell, 1196 Price: num.NewUint(65), 1197 OriginalPrice: num.NewUint(65), 1198 Size: 12, 1199 Remaining: 12, 1200 TimeInForce: types.OrderTimeInForceGTC, 1201 CreatedAt: currentTimestamp, 1202 ID: "9", 1203 } 1204 trades, getErr = book.ob.GetTrades(order9) 1205 assert.NoError(t, getErr) 1206 confirm, err = book.ob.SubmitOrder(order9) 1207 assert.Equal(t, err, nil) 1208 assert.Equal(t, len(trades), len(confirm.Trades)) 1209 1210 order10 := &types.Order{ 1211 Status: types.OrderStatusActive, 1212 Type: types.OrderTypeLimit, 1213 MarketID: market, 1214 Party: party, 1215 Side: types.SideSell, 1216 Price: num.NewUint(1), 1217 OriginalPrice: num.NewUint(1), 1218 Size: 1, 1219 Remaining: 1, 1220 TimeInForce: types.OrderTimeInForceGTT, 1221 CreatedAt: currentTimestamp, 1222 ExpiresAt: someTimeLater - 1, 1223 ID: "10", 1224 } 1225 trades, getErr = book.ob.GetTrades(order10) 1226 assert.NoError(t, getErr) 1227 confirm, err = book.ob.SubmitOrder(order10) 1228 assert.Equal(t, err, nil) 1229 assert.Equal(t, len(trades), len(confirm.Trades)) 1230 } 1231 1232 // test for order validation. 1233 func TestOrderBook_SubmitOrder2WithValidation(t *testing.T) { 1234 market := vgrand.RandomStr(5) 1235 book := getTestOrderBook(t, market) 1236 defer book.Finish() 1237 timeStampOrder := types.Order{ 1238 Status: types.OrderStatusActive, 1239 Type: types.OrderTypeLimit, 1240 ID: "timestamporderID", 1241 MarketID: market, 1242 Party: "A", 1243 CreatedAt: 10, 1244 Side: types.SideBuy, 1245 Size: 1, 1246 Remaining: 1, 1247 } 1248 trades, getErr := book.ob.GetTrades(&timeStampOrder) 1249 assert.NoError(t, getErr) 1250 confirm, err := book.ob.SubmitOrder(&timeStampOrder) 1251 assert.NoError(t, err) 1252 assert.Equal(t, len(trades), len(confirm.Trades)) 1253 // cancel order again, just so we set the timestamp as expected 1254 book.ob.CancelOrder(&timeStampOrder) 1255 1256 invalidRemainingSizeOrdertypes := &types.Order{ 1257 Status: types.OrderStatusActive, 1258 Type: types.OrderTypeLimit, 1259 MarketID: market, 1260 Party: "A", 1261 Side: types.SideSell, 1262 Price: num.NewUint(100), 1263 OriginalPrice: num.NewUint(100), 1264 Size: 100, 1265 Remaining: 300, 1266 TimeInForce: types.OrderTimeInForceGTC, 1267 CreatedAt: 10, 1268 ID: "id-number-one", 1269 } 1270 trades, getErr = book.ob.GetTrades(invalidRemainingSizeOrdertypes) 1271 _, err = book.ob.SubmitOrder(invalidRemainingSizeOrdertypes) 1272 assert.Equal(t, err, getErr) 1273 assert.Equal(t, types.OrderErrorInvalidRemainingSize, err) 1274 assert.Equal(t, 0, len(trades)) 1275 } 1276 1277 func TestOrderBook_DeleteOrder(t *testing.T) { 1278 market := vgrand.RandomStr(5) 1279 book := getTestOrderBook(t, market) 1280 defer book.Finish() 1281 1282 newOrder := &types.Order{ 1283 Status: types.OrderStatusActive, 1284 Type: types.OrderTypeLimit, 1285 MarketID: market, 1286 Party: "A", 1287 Side: types.SideSell, 1288 Price: num.NewUint(101), 1289 OriginalPrice: num.NewUint(101), 1290 Size: 100, 1291 Remaining: 100, 1292 TimeInForce: types.OrderTimeInForceGTC, 1293 CreatedAt: 0, 1294 } 1295 1296 trades, err := book.ob.GetTrades(newOrder) 1297 assert.NoError(t, err) 1298 assert.Equal(t, 0, len(trades)) 1299 book.ob.SubmitOrder(newOrder) 1300 1301 _, err = book.ob.DeleteOrder(newOrder) 1302 require.NoError(t, err) 1303 1304 book.ob.PrintState("AFTER REMOVE ORDER") 1305 } 1306 1307 func TestOrderBook_RemoveOrder(t *testing.T) { 1308 market := vgrand.RandomStr(5) 1309 book := getTestOrderBook(t, market) 1310 defer book.Finish() 1311 1312 newOrder := &types.Order{ 1313 Status: types.OrderStatusActive, 1314 Type: types.OrderTypeLimit, 1315 MarketID: market, 1316 Party: "A", 1317 Side: types.SideSell, 1318 Price: num.NewUint(101), 1319 OriginalPrice: num.NewUint(101), 1320 Size: 100, 1321 Remaining: 100, 1322 TimeInForce: types.OrderTimeInForceGTC, 1323 CreatedAt: 0, 1324 } 1325 1326 trades, err := book.ob.GetTrades(newOrder) 1327 assert.NoError(t, err) 1328 assert.Equal(t, 0, len(trades)) 1329 book.ob.SubmitOrder(newOrder) 1330 1331 // First time we remove the order it should succeed 1332 _, err = book.ob.RemoveOrder(newOrder.ID) 1333 assert.Error(t, err, vega.ErrInvalidOrderID) 1334 1335 // Second time we try to remove the order it should fail 1336 _, err = book.ob.RemoveOrder(newOrder.ID) 1337 assert.Error(t, err) 1338 } 1339 1340 func TestOrderBook_SubmitOrderInvalidMarket(t *testing.T) { 1341 market := vgrand.RandomStr(5) 1342 book := getTestOrderBook(t, market) 1343 defer book.Finish() 1344 1345 newOrder := &types.Order{ 1346 Status: types.OrderStatusActive, 1347 Type: types.OrderTypeLimit, 1348 MarketID: "invalid", 1349 Party: "A", 1350 Side: types.SideSell, 1351 Price: num.NewUint(101), 1352 OriginalPrice: num.NewUint(101), 1353 Size: 100, 1354 Remaining: 100, 1355 TimeInForce: types.OrderTimeInForceGTC, 1356 CreatedAt: 0, 1357 ID: vgcrypto.RandomHash(), 1358 } 1359 1360 trades, getErr := book.ob.GetTrades(newOrder) 1361 assert.Error(t, getErr) 1362 assert.Equal(t, 0, len(trades)) 1363 _, err := book.ob.SubmitOrder(newOrder) 1364 require.Error(t, err) 1365 assert.ErrorIs(t, err, types.OrderErrorInvalidMarketID) 1366 assert.ErrorIs(t, err, getErr) 1367 } 1368 1369 func TestOrderBook_CancelSellOrder(t *testing.T) { 1370 market := vgrand.RandomStr(5) 1371 book := getTestOrderBook(t, market) 1372 defer book.Finish() 1373 1374 // Arrange 1375 id := vgcrypto.RandomHash() 1376 newOrder := &types.Order{ 1377 Status: types.OrderStatusActive, 1378 Type: types.OrderTypeLimit, 1379 MarketID: market, 1380 Party: "A", 1381 Side: types.SideSell, 1382 Price: num.NewUint(101), 1383 OriginalPrice: num.NewUint(101), 1384 Size: 100, 1385 Remaining: 100, 1386 TimeInForce: types.OrderTimeInForceGTC, 1387 CreatedAt: 0, 1388 ID: id, 1389 } 1390 1391 trades, getErr := book.ob.GetTrades(newOrder) 1392 assert.NoError(t, getErr) 1393 confirmation, err := book.ob.SubmitOrder(newOrder) 1394 assert.NoError(t, err) 1395 orderAdded := confirmation.Order 1396 assert.Equal(t, len(trades), len(confirmation.Trades)) 1397 1398 // Act 1399 res, err := book.ob.CancelOrder(orderAdded) 1400 1401 // Assert 1402 assert.NoError(t, err) 1403 assert.Equal(t, id, res.Order.ID) 1404 assert.Equal(t, types.OrderStatusCancelled, res.Order.Status) 1405 1406 book.ob.PrintState("AFTER CANCEL ORDER") 1407 } 1408 1409 func TestOrderBook_CancelBuyOrder(t *testing.T) { 1410 market := vgrand.RandomStr(5) 1411 book := getTestOrderBook(t, market) 1412 defer book.Finish() 1413 1414 logger := logging.NewTestLogger() 1415 defer logger.Sync() 1416 logger.Debug("BEGIN CANCELLING VALID ORDER") 1417 1418 // Arrange 1419 id := vgcrypto.RandomHash() 1420 newOrder := &types.Order{ 1421 Status: types.OrderStatusActive, 1422 Type: types.OrderTypeLimit, 1423 MarketID: market, 1424 Party: "A", 1425 Side: types.SideBuy, 1426 Price: num.NewUint(101), 1427 OriginalPrice: num.NewUint(101), 1428 Size: 100, 1429 Remaining: 100, 1430 TimeInForce: types.OrderTimeInForceGTC, 1431 CreatedAt: 0, 1432 ID: id, 1433 } 1434 1435 trades, getErr := book.ob.GetTrades(newOrder) 1436 assert.NoError(t, getErr) 1437 confirmation, err := book.ob.SubmitOrder(newOrder) 1438 assert.NoError(t, err) 1439 assert.Equal(t, len(trades), len(confirmation.Trades)) 1440 orderAdded := confirmation.Order 1441 1442 // Act 1443 res, err := book.ob.CancelOrder(orderAdded) 1444 1445 // Assert 1446 assert.NoError(t, err) 1447 assert.Equal(t, id, res.Order.ID) 1448 assert.Equal(t, types.OrderStatusCancelled, res.Order.Status) 1449 1450 book.ob.PrintState("AFTER CANCEL ORDER") 1451 } 1452 1453 func TestOrderBook_CancelOrderByID(t *testing.T) { 1454 market := vgrand.RandomStr(5) 1455 book := getTestOrderBook(t, market) 1456 defer book.Finish() 1457 1458 logger := logging.NewTestLogger() 1459 defer logger.Sync() 1460 logger.Debug("BEGIN CANCELLING VALID ORDER BY ID") 1461 1462 id := vgcrypto.RandomHash() 1463 newOrder := &types.Order{ 1464 Status: types.OrderStatusActive, 1465 Type: types.OrderTypeLimit, 1466 MarketID: market, 1467 Party: "A", 1468 Side: types.SideBuy, 1469 Price: num.NewUint(101), 1470 OriginalPrice: num.NewUint(101), 1471 Size: 100, 1472 Remaining: 100, 1473 TimeInForce: types.OrderTimeInForceGTC, 1474 CreatedAt: 0, 1475 ID: id, 1476 } 1477 1478 trades, getErr := book.ob.GetTrades(newOrder) 1479 assert.NoError(t, getErr) 1480 confirmation, err := book.ob.SubmitOrder(newOrder) 1481 assert.NotNil(t, confirmation, "submit order should succeed") 1482 assert.NoError(t, err, "submit order should succeed") 1483 orderAdded := confirmation.Order 1484 assert.NotNil(t, orderAdded, "submitted order is expected to be valid") 1485 assert.Equal(t, len(trades), len(confirmation.Trades)) 1486 1487 orderFound, err := book.ob.GetOrderByID(orderAdded.ID) 1488 assert.NotNil(t, orderFound, "order lookup should work for the order just submitted") 1489 assert.NoError(t, err, "order lookup should not fail") 1490 1491 res, err := book.ob.CancelOrder(orderFound) 1492 assert.NotNil(t, res, "cancelling should work for a valid order that was just found") 1493 assert.NoError(t, err, "order cancel should not fail") 1494 1495 orderFound, err = book.ob.GetOrderByID(orderAdded.ID) 1496 assert.Error(t, err, "order lookup for an already cancelled order should fail") 1497 assert.Nil(t, orderFound, "order lookup for an already cancelled order should not be possible") 1498 1499 book.ob.PrintState("AFTER CANCEL ORDER BY ID") 1500 } 1501 1502 func TestOrderBook_CancelOrderMarketMismatch(t *testing.T) { 1503 logger := logging.NewTestLogger() 1504 defer logger.Sync() 1505 logger.Debug("BEGIN CANCELLING MARKET MISMATCH ORDER") 1506 1507 market := vgrand.RandomStr(5) 1508 book := getTestOrderBook(t, market) 1509 defer book.Finish() 1510 newOrder := &types.Order{ 1511 Status: types.OrderStatusActive, 1512 Type: types.OrderTypeLimit, 1513 MarketID: market, 1514 ID: vgcrypto.RandomHash(), 1515 Party: "A", 1516 Size: 100, 1517 Remaining: 100, 1518 } 1519 1520 trades, getErr := book.ob.GetTrades(newOrder) 1521 assert.NoError(t, getErr) 1522 confirmation, err := book.ob.SubmitOrder(newOrder) 1523 assert.NoError(t, err) 1524 orderAdded := confirmation.Order 1525 assert.Equal(t, len(trades), len(confirmation.Trades)) 1526 1527 orderAdded.MarketID = "invalid" // Bad market, malformed? 1528 1529 assert.Panics(t, func() { _, err = book.ob.CancelOrder(orderAdded) }) 1530 } 1531 1532 func TestOrderBook_CancelOrderInvalidID(t *testing.T) { 1533 logger := logging.NewTestLogger() 1534 defer logger.Sync() 1535 logger.Debug("BEGIN CANCELLING INVALID ORDER") 1536 1537 market := vgrand.RandomStr(5) 1538 book := getTestOrderBook(t, market) 1539 defer book.Finish() 1540 newOrder := &types.Order{ 1541 Status: types.OrderStatusActive, 1542 Type: types.OrderTypeLimit, 1543 MarketID: market, 1544 ID: "id", 1545 Party: "A", 1546 Size: 100, 1547 Remaining: 100, 1548 } 1549 1550 trades, getErr := book.ob.GetTrades(newOrder) 1551 assert.NoError(t, getErr) 1552 confirmation, err := book.ob.SubmitOrder(newOrder) 1553 assert.NoError(t, err) 1554 orderAdded := confirmation.Order 1555 assert.Equal(t, len(trades), len(confirmation.Trades)) 1556 1557 _, err = book.ob.CancelOrder(orderAdded) 1558 if err != nil { 1559 logger.Debug("error cancelling order", logging.Error(err)) 1560 } 1561 1562 assert.Equal(t, types.OrderErrorInvalidOrderID, err) 1563 } 1564 1565 func expectTrade(t *testing.T, expectedTrade, trade *types.Trade) { 1566 t.Helper() 1567 // run asserts for protocol trade data 1568 assert.Equal(t, expectedTrade.Type, trade.Type, "invalid trade type") 1569 assert.Equal(t, int(expectedTrade.Price.Uint64()), int(trade.Price.Uint64()), "invalid trade price") 1570 assert.Equal(t, int(expectedTrade.Size), int(trade.Size), "invalid trade size") 1571 assert.Equal(t, expectedTrade.Buyer, trade.Buyer, "invalid trade buyer") 1572 assert.Equal(t, expectedTrade.Seller, trade.Seller, "invalid trade seller") 1573 assert.Equal(t, expectedTrade.Aggressor, trade.Aggressor, "invalid trade aggressor") 1574 } 1575 1576 func expectOrder(t *testing.T, expectedOrder, order *types.Order) { 1577 t.Helper() 1578 // run asserts for order 1579 assert.Equal(t, expectedOrder.MarketID, order.MarketID, "invalid order market id") 1580 assert.Equal(t, expectedOrder.Party, order.Party, "invalid order party id") 1581 assert.Equal(t, expectedOrder.Side, order.Side, "invalid order side") 1582 assert.Equal(t, int(expectedOrder.Price.Uint64()), int(order.Price.Uint64()), "invalid order price") 1583 assert.Equal(t, int(expectedOrder.Size), int(order.Size), "invalid order size") 1584 assert.Equal(t, int(expectedOrder.Remaining), int(order.Remaining), "invalid order remaining") 1585 assert.Equal(t, expectedOrder.TimeInForce, order.TimeInForce, "invalid order tif") 1586 assert.Equal(t, expectedOrder.CreatedAt, order.CreatedAt, "invalid order created at") 1587 } 1588 1589 func TestOrderBook_AmendOrder(t *testing.T) { 1590 market := vgrand.RandomStr(5) 1591 book := getTestOrderBook(t, market) 1592 defer book.Finish() 1593 1594 newOrder := &types.Order{ 1595 Status: types.OrderStatusActive, 1596 Type: types.OrderTypeLimit, 1597 MarketID: market, 1598 ID: "123456", 1599 Party: "A", 1600 Side: types.SideBuy, 1601 Price: num.NewUint(100), 1602 OriginalPrice: num.NewUint(100), 1603 Size: 200, 1604 Remaining: 200, 1605 TimeInForce: types.OrderTimeInForceGTC, 1606 } 1607 1608 confirmation, err := book.ob.SubmitOrder(newOrder) 1609 if err != nil { 1610 t.Log(err) 1611 } 1612 1613 assert.Equal(t, nil, err) 1614 assert.NotNil(t, confirmation) 1615 assert.Equal(t, "123456", confirmation.Order.ID) 1616 assert.Equal(t, 0, len(confirmation.Trades)) 1617 1618 editedOrder := &types.Order{ 1619 Status: types.OrderStatusActive, 1620 Type: types.OrderTypeLimit, 1621 MarketID: market, 1622 ID: "123456", 1623 Party: "A", 1624 Side: types.SideBuy, 1625 Price: num.NewUint(100), 1626 OriginalPrice: num.NewUint(100), 1627 Size: 200, 1628 Remaining: 200, 1629 TimeInForce: types.OrderTimeInForceGTC, 1630 } 1631 1632 err = book.ob.AmendOrder(newOrder, editedOrder) 1633 if err != nil { 1634 t.Log(err) 1635 } 1636 1637 assert.Nil(t, err) 1638 } 1639 1640 func TestOrderBook_AmendOrderInvalidRemaining(t *testing.T) { 1641 market := vgrand.RandomStr(5) 1642 book := getTestOrderBook(t, market) 1643 defer book.Finish() 1644 1645 newOrder := &types.Order{ 1646 Status: types.OrderStatusActive, 1647 Type: types.OrderTypeLimit, 1648 MarketID: market, 1649 ID: "123456", 1650 Party: "A", 1651 Side: types.SideBuy, 1652 Price: num.NewUint(100), 1653 OriginalPrice: num.NewUint(100), 1654 Size: 200, 1655 Remaining: 200, 1656 TimeInForce: types.OrderTimeInForceGTC, 1657 } 1658 1659 trades, getErr := book.ob.GetTrades(newOrder) 1660 assert.NoError(t, getErr) 1661 confirmation, err := book.ob.SubmitOrder(newOrder) 1662 1663 assert.Equal(t, nil, err) 1664 assert.NotNil(t, confirmation) 1665 assert.Equal(t, "123456", confirmation.Order.ID) 1666 assert.Equal(t, 0, len(confirmation.Trades)) 1667 assert.Equal(t, len(trades), len(confirmation.Trades)) 1668 1669 editedOrder := &types.Order{ 1670 Status: types.OrderStatusActive, 1671 Type: types.OrderTypeLimit, 1672 MarketID: market, 1673 Party: "A", 1674 ID: "123456", 1675 Side: types.SideSell, 1676 Price: num.NewUint(100), 1677 OriginalPrice: num.NewUint(100), 1678 Size: 100, 1679 Remaining: 200, 1680 TimeInForce: types.OrderTimeInForceGTC, 1681 } 1682 err = book.ob.AmendOrder(newOrder, editedOrder) 1683 if err != types.OrderErrorInvalidRemainingSize { 1684 t.Log(err) 1685 } 1686 1687 assert.Equal(t, types.OrderErrorInvalidRemainingSize, err) 1688 } 1689 1690 func TestOrderBook_AmendOrderInvalidAmend(t *testing.T) { 1691 market := vgrand.RandomStr(5) 1692 book := getTestOrderBook(t, market) 1693 defer book.Finish() 1694 1695 newOrder := &types.Order{ 1696 Status: types.OrderStatusActive, 1697 Type: types.OrderTypeLimit, 1698 MarketID: market, 1699 ID: "123456", 1700 Side: types.SideBuy, 1701 Price: num.NewUint(100), 1702 OriginalPrice: num.NewUint(100), 1703 Size: 200, 1704 Remaining: 200, 1705 TimeInForce: types.OrderTimeInForceGTC, 1706 } 1707 1708 trades, getErr := book.ob.GetTrades(newOrder) 1709 confirmation, err := book.ob.SubmitOrder(newOrder) 1710 assert.Equal(t, err, getErr) 1711 assert.Equal(t, 0, len(trades)) 1712 1713 fmt.Printf("confirmation : %+v", confirmation) 1714 1715 editedOrder := &types.Order{ 1716 Status: types.OrderStatusActive, 1717 Type: types.OrderTypeLimit, 1718 MarketID: market, 1719 ID: "123456", 1720 Party: "A", 1721 Side: types.SideSell, 1722 Price: num.NewUint(100), 1723 OriginalPrice: num.NewUint(100), 1724 Size: 200, 1725 Remaining: 200, 1726 TimeInForce: types.OrderTimeInForceGTC, 1727 } 1728 1729 err = book.ob.AmendOrder(newOrder, editedOrder) 1730 assert.Equal(t, types.OrderErrorNotFound, err) 1731 } 1732 1733 func TestOrderBook_AmendOrderInvalidAmend1(t *testing.T) { 1734 logger := logging.NewTestLogger() 1735 defer logger.Sync() 1736 logger.Debug("BEGIN AMENDING ORDER") 1737 1738 market := vgrand.RandomStr(5) 1739 book := getTestOrderBook(t, market) 1740 defer book.Finish() 1741 newOrder := &types.Order{ 1742 Status: types.OrderStatusActive, 1743 Type: types.OrderTypeLimit, 1744 MarketID: market, 1745 ID: "123456", 1746 Side: types.SideBuy, 1747 Price: num.NewUint(100), 1748 OriginalPrice: num.NewUint(100), 1749 Party: "A", 1750 Size: 200, 1751 Remaining: 200, 1752 TimeInForce: types.OrderTimeInForceGTC, 1753 } 1754 1755 trades, getErr := book.ob.GetTrades(newOrder) 1756 assert.NoError(t, getErr) 1757 confirmation, err := book.ob.SubmitOrder(newOrder) 1758 1759 assert.Equal(t, nil, err) 1760 assert.NotNil(t, confirmation) 1761 assert.Equal(t, "123456", confirmation.Order.ID) 1762 assert.Equal(t, 0, len(confirmation.Trades)) 1763 assert.Equal(t, len(trades), len(confirmation.Trades)) 1764 1765 editedOrder := &types.Order{ 1766 Status: types.OrderStatusActive, 1767 Type: types.OrderTypeLimit, 1768 MarketID: market, 1769 ID: "123456", 1770 Side: types.SideBuy, 1771 Price: num.NewUint(100), 1772 OriginalPrice: num.NewUint(100), 1773 Party: "B", 1774 Size: 200, 1775 Remaining: 200, 1776 TimeInForce: types.OrderTimeInForceGTC, 1777 } 1778 1779 err = book.ob.AmendOrder(newOrder, editedOrder) 1780 if err != types.OrderErrorAmendFailure { 1781 t.Log(err) 1782 } 1783 1784 assert.Equal(t, types.OrderErrorAmendFailure, err) 1785 } 1786 1787 func TestOrderBook_AmendOrderInvalidAmendOutOfSequence(t *testing.T) { 1788 market := vgrand.RandomStr(5) 1789 book := getTestOrderBook(t, market) 1790 defer book.Finish() 1791 1792 logger := logging.NewTestLogger() 1793 defer logger.Sync() 1794 logger.Debug("BEGIN AMENDING OUT OF SEQUENCE ORDER") 1795 1796 newOrder := &types.Order{ 1797 Status: types.OrderStatusActive, 1798 Type: types.OrderTypeLimit, 1799 MarketID: market, 1800 ID: "123456", 1801 Side: types.SideBuy, 1802 Price: num.NewUint(100), 1803 OriginalPrice: num.NewUint(100), 1804 Party: "A", 1805 Size: 200, 1806 Remaining: 200, 1807 TimeInForce: types.OrderTimeInForceGTC, 1808 CreatedAt: 10, 1809 } 1810 1811 trades, getErr := book.ob.GetTrades(newOrder) 1812 assert.NoError(t, getErr) 1813 confirmation, err := book.ob.SubmitOrder(newOrder) 1814 1815 assert.Equal(t, nil, err) 1816 assert.NotNil(t, confirmation) 1817 assert.Equal(t, "123456", confirmation.Order.ID) 1818 assert.Equal(t, 0, len(confirmation.Trades)) 1819 assert.Equal(t, len(trades), len(confirmation.Trades)) 1820 1821 editedOrder := &types.Order{ 1822 Status: types.OrderStatusActive, 1823 Type: types.OrderTypeLimit, 1824 MarketID: market, 1825 ID: "123456", 1826 Side: types.SideBuy, 1827 Price: num.NewUint(100), 1828 OriginalPrice: num.NewUint(100), 1829 Party: "A", 1830 Size: 200, 1831 Remaining: 200, 1832 TimeInForce: types.OrderTimeInForceGTC, 1833 CreatedAt: 5, 1834 } 1835 1836 err = book.ob.AmendOrder(newOrder, editedOrder) 1837 if err != types.OrderErrorOutOfSequence { 1838 t.Log(err) 1839 } 1840 1841 assert.Equal(t, types.OrderErrorOutOfSequence, err) 1842 } 1843 1844 func TestOrderBook_AmendOrderInvalidAmendSize(t *testing.T) { 1845 market := vgrand.RandomStr(5) 1846 book := getTestOrderBook(t, market) 1847 defer book.Finish() 1848 1849 logger := logging.NewTestLogger() 1850 defer logger.Sync() 1851 logger.Debug("BEGIN AMEND ORDER INVALID SIZE") 1852 1853 newOrder := &types.Order{ 1854 Status: types.OrderStatusActive, 1855 Type: types.OrderTypeLimit, 1856 MarketID: market, 1857 ID: "123456", 1858 Side: types.SideBuy, 1859 Price: num.NewUint(100), 1860 OriginalPrice: num.NewUint(100), 1861 Party: "A", 1862 Size: 200, 1863 Remaining: 200, 1864 TimeInForce: types.OrderTimeInForceGTC, 1865 CreatedAt: 10, 1866 } 1867 1868 trades, getErr := book.ob.GetTrades(newOrder) 1869 assert.NoError(t, getErr) 1870 confirmation, err := book.ob.SubmitOrder(newOrder) 1871 1872 assert.Equal(t, nil, err) 1873 assert.NotNil(t, confirmation) 1874 assert.Equal(t, "123456", confirmation.Order.ID) 1875 assert.Equal(t, 0, len(confirmation.Trades)) 1876 assert.Equal(t, len(trades), len(confirmation.Trades)) 1877 1878 editedOrder := &types.Order{ 1879 Status: types.OrderStatusActive, 1880 Type: types.OrderTypeLimit, 1881 MarketID: market, 1882 ID: "123456", 1883 Side: types.SideBuy, 1884 Price: num.NewUint(100), 1885 OriginalPrice: num.NewUint(100), 1886 Party: "B", 1887 Size: 300, 1888 Remaining: 300, 1889 TimeInForce: types.OrderTimeInForceGTC, 1890 CreatedAt: 10, 1891 } 1892 1893 err = book.ob.AmendOrder(newOrder, editedOrder) 1894 if err != types.OrderErrorAmendFailure { 1895 t.Log(err) 1896 } 1897 1898 assert.Equal(t, types.OrderErrorAmendFailure, err) 1899 } 1900 1901 // ProRata mode OFF which is a default config for vega ME. 1902 func TestOrderBook_SubmitOrderProRataModeOff(t *testing.T) { 1903 market := vgrand.RandomStr(5) 1904 book := getTestOrderBook(t, market) 1905 defer book.Finish() 1906 1907 // logger := logging.NewTestLogger() 1908 // defer logger.Sync() 1909 // logger.Debug("BEGIN PRO-RATA MODE OFF") 1910 1911 const numberOfTimestamps = 2 1912 m := make(map[int64][]*types.Order, numberOfTimestamps) 1913 1914 // sell and buy side orders at timestamp 0 1915 m[0] = []*types.Order{ 1916 // Side Sell 1917 { 1918 ID: "V0000000032-0000000009", 1919 Status: types.OrderStatusActive, 1920 Type: types.OrderTypeLimit, 1921 MarketID: market, 1922 Party: "A", 1923 Side: types.SideSell, 1924 Price: num.NewUint(101), 1925 OriginalPrice: num.NewUint(101), 1926 Size: 100, 1927 Remaining: 100, 1928 TimeInForce: types.OrderTimeInForceGTC, 1929 CreatedAt: 0, 1930 }, 1931 { 1932 ID: "V0000000032-0000000010", 1933 Status: types.OrderStatusActive, 1934 Type: types.OrderTypeLimit, 1935 MarketID: market, 1936 Party: "B", 1937 Side: types.SideSell, 1938 Price: num.NewUint(101), 1939 OriginalPrice: num.NewUint(101), 1940 Size: 100, 1941 Remaining: 100, 1942 TimeInForce: types.OrderTimeInForceGTC, 1943 CreatedAt: 0, 1944 }, 1945 // Side Buy 1946 { 1947 ID: "V0000000032-0000000011", 1948 Status: types.OrderStatusActive, 1949 Type: types.OrderTypeLimit, 1950 MarketID: market, 1951 Party: "C", 1952 Side: types.SideBuy, 1953 Price: num.NewUint(98), 1954 OriginalPrice: num.NewUint(98), 1955 Size: 100, 1956 Remaining: 100, 1957 TimeInForce: types.OrderTimeInForceGTC, 1958 CreatedAt: 0, 1959 }, 1960 { 1961 ID: "V0000000032-0000000012", 1962 Status: types.OrderStatusActive, 1963 Type: types.OrderTypeLimit, 1964 MarketID: market, 1965 Party: "D", 1966 Side: types.SideBuy, 1967 Price: num.NewUint(98), 1968 OriginalPrice: num.NewUint(98), 1969 Size: 100, 1970 Remaining: 100, 1971 TimeInForce: types.OrderTimeInForceGTC, 1972 CreatedAt: 0, 1973 }, 1974 } 1975 1976 // sell and buy orders at timestamp 1 1977 m[1] = []*types.Order{ 1978 // Side Sell 1979 { 1980 ID: "V0000000032-0000000013", 1981 Status: types.OrderStatusActive, 1982 Type: types.OrderTypeLimit, 1983 MarketID: market, 1984 Party: "E", 1985 Side: types.SideSell, 1986 Price: num.NewUint(101), 1987 OriginalPrice: num.NewUint(101), 1988 Size: 100, 1989 Remaining: 100, 1990 TimeInForce: types.OrderTimeInForceGTC, 1991 CreatedAt: 1, 1992 }, 1993 // Side Buy 1994 { 1995 ID: "V0000000032-0000000014", 1996 Status: types.OrderStatusActive, 1997 Type: types.OrderTypeLimit, 1998 MarketID: market, 1999 Party: "F", 2000 Side: types.SideBuy, 2001 Price: num.NewUint(99), 2002 OriginalPrice: num.NewUint(99), 2003 Size: 100, 2004 Remaining: 100, 2005 TimeInForce: types.OrderTimeInForceGTC, 2006 CreatedAt: 1, 2007 }, 2008 } 2009 2010 timestamps := []int64{0, 1} 2011 for _, timestamp := range timestamps { 2012 for _, o := range m[timestamp] { 2013 trades, getErr := book.ob.GetTrades(o) 2014 assert.NoError(t, getErr) 2015 confirmation, err := book.ob.SubmitOrder(o) 2016 // this should not return any errors 2017 assert.Equal(t, nil, err) 2018 // this should not generate any trades 2019 assert.Equal(t, 0, len(confirmation.Trades)) 2020 assert.Equal(t, len(trades), len(confirmation.Trades)) 2021 } 2022 } 2023 2024 scenario := []aggressiveOrderScenario{ 2025 { 2026 // same price level, remaining on the passive 2027 aggressiveOrder: &types.Order{ 2028 ID: "V0000000032-0000000015", 2029 Status: types.OrderStatusActive, 2030 Type: types.OrderTypeLimit, 2031 MarketID: market, 2032 Party: "M", 2033 Side: types.SideBuy, 2034 Price: num.NewUint(101), 2035 OriginalPrice: num.NewUint(101), 2036 Size: 100, 2037 Remaining: 100, 2038 TimeInForce: types.OrderTimeInForceGTC, 2039 CreatedAt: 3, 2040 }, 2041 expectedTrades: []types.Trade{ 2042 { 2043 Type: types.TradeTypeDefault, 2044 MarketID: market, 2045 Price: num.NewUint(101), 2046 Size: 100, 2047 Buyer: "M", 2048 Seller: "A", 2049 Aggressor: types.SideBuy, 2050 }, 2051 }, 2052 expectedPassiveOrdersAffected: []types.Order{ 2053 { 2054 Status: types.OrderStatusActive, 2055 Type: types.OrderTypeLimit, 2056 MarketID: market, 2057 Party: "A", 2058 Side: types.SideSell, 2059 Price: num.NewUint(101), 2060 OriginalPrice: num.NewUint(101), 2061 Size: 100, 2062 Remaining: 0, 2063 TimeInForce: types.OrderTimeInForceGTC, 2064 CreatedAt: 0, 2065 }, 2066 }, 2067 }, 2068 { 2069 // same price level, remaining on the passive 2070 aggressiveOrder: &types.Order{ 2071 Status: types.OrderStatusActive, 2072 Type: types.OrderTypeLimit, 2073 MarketID: market, 2074 Party: "N", 2075 Side: types.SideBuy, 2076 Price: num.NewUint(102), 2077 OriginalPrice: num.NewUint(102), 2078 Size: 200, 2079 Remaining: 200, 2080 TimeInForce: types.OrderTimeInForceGTC, 2081 CreatedAt: 4, 2082 }, 2083 expectedTrades: []types.Trade{ 2084 { 2085 Type: types.TradeTypeDefault, 2086 MarketID: market, 2087 Price: num.NewUint(101), 2088 Size: 100, 2089 Buyer: "N", 2090 Seller: "B", 2091 Aggressor: types.SideBuy, 2092 }, 2093 { 2094 Type: types.TradeTypeDefault, 2095 MarketID: market, 2096 Price: num.NewUint(101), 2097 Size: 100, 2098 Buyer: "N", 2099 Seller: "E", 2100 Aggressor: types.SideBuy, 2101 }, 2102 }, 2103 expectedPassiveOrdersAffected: []types.Order{ 2104 { 2105 Status: types.OrderStatusActive, 2106 Type: types.OrderTypeLimit, 2107 MarketID: market, 2108 Party: "B", 2109 Side: types.SideSell, 2110 Price: num.NewUint(101), 2111 OriginalPrice: num.NewUint(101), 2112 Size: 100, 2113 Remaining: 0, 2114 TimeInForce: types.OrderTimeInForceGTC, 2115 CreatedAt: 0, 2116 }, 2117 { 2118 Status: types.OrderStatusActive, 2119 Type: types.OrderTypeLimit, 2120 MarketID: market, 2121 Party: "E", 2122 Side: types.SideSell, 2123 Price: num.NewUint(101), 2124 OriginalPrice: num.NewUint(101), 2125 Size: 100, 2126 Remaining: 0, 2127 TimeInForce: types.OrderTimeInForceGTC, 2128 CreatedAt: 1, 2129 }, 2130 }, 2131 }, 2132 { 2133 // same price level, NO PRORATA 2134 aggressiveOrder: &types.Order{ 2135 Status: types.OrderStatusActive, 2136 Type: types.OrderTypeLimit, 2137 MarketID: market, 2138 Party: "O", 2139 Side: types.SideSell, 2140 Price: num.NewUint(97), 2141 OriginalPrice: num.NewUint(97), 2142 Size: 250, 2143 Remaining: 250, 2144 TimeInForce: types.OrderTimeInForceGTC, 2145 CreatedAt: 5, 2146 }, 2147 expectedTrades: []types.Trade{ 2148 { 2149 Type: types.TradeTypeDefault, 2150 MarketID: market, 2151 Price: num.NewUint(99), 2152 Size: 100, 2153 Buyer: "F", 2154 Seller: "O", 2155 Aggressor: types.SideSell, 2156 }, 2157 { 2158 Type: types.TradeTypeDefault, 2159 MarketID: market, 2160 Price: num.NewUint(98), 2161 Size: 100, 2162 Buyer: "C", 2163 Seller: "O", 2164 Aggressor: types.SideSell, 2165 }, 2166 { 2167 Type: types.TradeTypeDefault, 2168 MarketID: market, 2169 Price: num.NewUint(98), 2170 Size: 50, 2171 Buyer: "D", 2172 Seller: "O", 2173 Aggressor: types.SideSell, 2174 }, 2175 }, 2176 expectedPassiveOrdersAffected: []types.Order{ 2177 { 2178 Status: types.OrderStatusActive, 2179 Type: types.OrderTypeLimit, 2180 MarketID: market, 2181 Party: "F", 2182 Side: types.SideBuy, 2183 Price: num.NewUint(99), 2184 OriginalPrice: num.NewUint(99), 2185 Size: 100, 2186 Remaining: 0, 2187 TimeInForce: types.OrderTimeInForceGTC, 2188 CreatedAt: 1, 2189 }, 2190 { 2191 Status: types.OrderStatusActive, 2192 Type: types.OrderTypeLimit, 2193 MarketID: market, 2194 Party: "C", 2195 Side: types.SideBuy, 2196 Price: num.NewUint(98), 2197 OriginalPrice: num.NewUint(98), 2198 Size: 100, 2199 Remaining: 0, 2200 TimeInForce: types.OrderTimeInForceGTC, 2201 CreatedAt: 0, 2202 }, 2203 { 2204 Status: types.OrderStatusActive, 2205 Type: types.OrderTypeLimit, 2206 MarketID: market, 2207 Party: "D", 2208 Side: types.SideBuy, 2209 Price: num.NewUint(98), 2210 OriginalPrice: num.NewUint(98), 2211 Size: 100, 2212 Remaining: 50, 2213 TimeInForce: types.OrderTimeInForceGTC, 2214 CreatedAt: 0, 2215 }, 2216 }, 2217 }, 2218 { 2219 // same price level, NO PRORATA 2220 aggressiveOrder: &types.Order{ 2221 Status: types.OrderStatusActive, 2222 Type: types.OrderTypeLimit, 2223 MarketID: market, 2224 Party: "X", 2225 Side: types.SideSell, 2226 Price: num.NewUint(98), 2227 OriginalPrice: num.NewUint(98), 2228 Size: 50, 2229 Remaining: 50, 2230 TimeInForce: types.OrderTimeInForceGTC, 2231 CreatedAt: 6, 2232 }, 2233 expectedTrades: []types.Trade{ 2234 { 2235 Type: types.TradeTypeDefault, 2236 MarketID: market, 2237 Price: num.NewUint(98), 2238 Size: 50, 2239 Buyer: "D", 2240 Seller: "X", 2241 Aggressor: types.SideSell, 2242 }, 2243 }, 2244 expectedPassiveOrdersAffected: []types.Order{ 2245 { 2246 Status: types.OrderStatusActive, 2247 Type: types.OrderTypeLimit, 2248 MarketID: market, 2249 Party: "D", 2250 Side: types.SideBuy, 2251 Price: num.NewUint(98), 2252 OriginalPrice: num.NewUint(98), 2253 Size: 100, 2254 Remaining: 0, 2255 TimeInForce: types.OrderTimeInForceGTC, 2256 CreatedAt: 0, 2257 }, 2258 }, 2259 }, 2260 } 2261 2262 for i, s := range scenario { 2263 fmt.Println() 2264 fmt.Println() 2265 fmt.Printf("SCENARIO %d / %d ------------------------------------------------------------------", i+1, len(scenario)) 2266 fmt.Println() 2267 fmt.Println("aggressor: ", s.aggressiveOrder) 2268 fmt.Println("expectedPassiveOrdersAffected: ", s.expectedPassiveOrdersAffected) 2269 fmt.Println("expectedTrades: ", s.expectedTrades) 2270 fmt.Println() 2271 2272 trades, getErr := book.ob.GetTrades(s.aggressiveOrder) 2273 assert.NoError(t, getErr) 2274 confirmationtypes, err := book.ob.SubmitOrder(s.aggressiveOrder) 2275 2276 // this should not return any errors 2277 assert.Equal(t, nil, err) 2278 2279 // this should not generate any trades 2280 assert.Equal(t, len(s.expectedTrades), len(confirmationtypes.Trades)) 2281 assert.Equal(t, len(confirmationtypes.Trades), len(trades)) 2282 2283 fmt.Println("CONFIRMATION types:") 2284 fmt.Println("-> Aggressive:", confirmationtypes.Order) 2285 fmt.Println("-> Trades :", confirmationtypes.Trades) 2286 fmt.Println("-> PassiveOrdersAffected:", confirmationtypes.PassiveOrdersAffected) 2287 fmt.Printf("Scenario: %d / %d \n", i+1, len(scenario)) 2288 2289 // trades should match expected trades 2290 for i, exp := range s.expectedTrades { 2291 expectTrade(t, &exp, confirmationtypes.Trades[i]) 2292 expectTrade(t, &exp, trades[i]) 2293 } 2294 2295 // orders affected should match expected values 2296 for i, exp := range s.expectedPassiveOrdersAffected { 2297 expectOrder(t, &exp, confirmationtypes.PassiveOrdersAffected[i]) 2298 } 2299 } 2300 } 2301 2302 // Validate that an IOC order that is not fully filled 2303 // is not added to the order book.ob. 2304 func TestOrderBook_PartialFillIOCOrder(t *testing.T) { 2305 market := vgrand.RandomStr(5) 2306 book := getTestOrderBook(t, market) 2307 defer book.Finish() 2308 2309 logger := logging.NewTestLogger() 2310 defer logger.Sync() 2311 logger.Debug("BEGIN PARTIAL FILL IOC ORDER") 2312 2313 orderID := vgcrypto.RandomHash() 2314 newOrder := &types.Order{ 2315 ID: orderID, 2316 Status: types.OrderStatusActive, 2317 Type: types.OrderTypeLimit, 2318 MarketID: market, 2319 Side: types.SideSell, 2320 Price: num.NewUint(100), 2321 OriginalPrice: num.NewUint(100), 2322 Party: "A", 2323 Size: 100, 2324 Remaining: 100, 2325 TimeInForce: types.OrderTimeInForceGTC, 2326 CreatedAt: 10, 2327 } 2328 2329 trades, getErr := book.ob.GetTrades(newOrder) 2330 assert.NoError(t, getErr) 2331 confirmation, err := book.ob.SubmitOrder(newOrder) 2332 2333 assert.Equal(t, nil, err) 2334 assert.NotNil(t, confirmation) 2335 assert.Equal(t, orderID, confirmation.Order.ID) 2336 assert.Equal(t, 0, len(confirmation.Trades)) 2337 assert.Equal(t, len(trades), len(confirmation.Trades)) 2338 2339 iocOrderID := vgcrypto.RandomHash() 2340 iocOrder := &types.Order{ 2341 Status: types.OrderStatusActive, 2342 Type: types.OrderTypeLimit, 2343 MarketID: market, 2344 ID: iocOrderID, 2345 Side: types.SideBuy, 2346 Price: num.NewUint(100), 2347 OriginalPrice: num.NewUint(100), 2348 Party: "B", 2349 Size: 20, 2350 Remaining: 20, 2351 TimeInForce: types.OrderTimeInForceIOC, 2352 CreatedAt: 10, 2353 } 2354 trades, getErr = book.ob.GetTrades(iocOrder) 2355 assert.NoError(t, getErr) 2356 confirmation, err = book.ob.SubmitOrder(iocOrder) 2357 2358 assert.Equal(t, nil, err) 2359 assert.NotNil(t, confirmation) 2360 assert.Equal(t, iocOrderID, confirmation.Order.ID) 2361 assert.Equal(t, 1, len(confirmation.Trades)) 2362 assert.Equal(t, len(trades), len(confirmation.Trades)) 2363 2364 // Check to see if the order still exists (it should not) 2365 nonorder, err := book.ob.GetOrderByID(iocOrderID) 2366 assert.Equal(t, matching.ErrOrderDoesNotExist, err) 2367 assert.Nil(t, nonorder) 2368 } 2369 2370 func makeOrder(t *testing.T, orderbook *tstOB, market string, id string, side types.Side, price uint64, partyid string, size uint64) *types.Order { 2371 t.Helper() 2372 order := getOrder(t, market, id, side, price, partyid, size) 2373 _, err := orderbook.ob.SubmitOrder(order) 2374 assert.Equal(t, err, nil) 2375 return order 2376 } 2377 2378 func getOrder(t *testing.T, market string, id string, side types.Side, price uint64, partyid string, size uint64) *types.Order { 2379 t.Helper() 2380 order := &types.Order{ 2381 Status: types.OrderStatusActive, 2382 Type: types.OrderTypeLimit, 2383 MarketID: market, 2384 ID: id, 2385 Side: side, 2386 Price: num.NewUint(price), 2387 OriginalPrice: num.NewUint(price), 2388 Party: partyid, 2389 Size: size, 2390 Remaining: size, 2391 TimeInForce: types.OrderTimeInForceGTC, 2392 CreatedAt: 10, 2393 } 2394 return order 2395 } 2396 2397 /*****************************************************************************/ 2398 /* GFN/GFA TESTING */ 2399 /*****************************************************************************/ 2400 2401 func TestOrderBook_GFNMarketNoExpiry(t *testing.T) { 2402 market := vgrand.RandomStr(5) 2403 book := getTestOrderBook(t, market) 2404 defer book.Finish() 2405 2406 logger := logging.NewTestLogger() 2407 defer logger.Sync() 2408 2409 // Enter a GFN market order with no expiration time 2410 buyOrder := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 10) 2411 buyOrder.TimeInForce = types.OrderTimeInForceGFN 2412 buyOrder.Type = types.OrderTypeMarket 2413 buyOrder.ExpiresAt = 0 2414 buyOrderConf, err := book.ob.SubmitOrder(buyOrder) 2415 assert.NoError(t, err) 2416 assert.NotNil(t, buyOrderConf) 2417 2418 // Enter a GFN market order with no expiration time 2419 sellOrder := getOrder(t, market, "SellOrder01", types.SideSell, 100, "party01", 10) 2420 sellOrder.TimeInForce = types.OrderTimeInForceGFN 2421 sellOrder.Type = types.OrderTypeMarket 2422 sellOrder.ExpiresAt = 0 2423 sellOrderConf, err := book.ob.SubmitOrder(sellOrder) 2424 assert.NoError(t, err) 2425 assert.NotNil(t, sellOrderConf) 2426 } 2427 2428 func TestOrderBook_GFNMarketWithExpiry(t *testing.T) { 2429 market := vgrand.RandomStr(5) 2430 book := getTestOrderBook(t, market) 2431 defer book.Finish() 2432 2433 logger := logging.NewTestLogger() 2434 defer logger.Sync() 2435 2436 // Enter a GFN market order with an expiration time (which is invalid) 2437 buyOrder := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 10) 2438 buyOrder.TimeInForce = types.OrderTimeInForceGFN 2439 buyOrder.Type = types.OrderTypeMarket 2440 buyOrder.ExpiresAt = 100 2441 buyOrderConf, err := book.ob.SubmitOrder(buyOrder) 2442 assert.Error(t, err, types.ErrInvalidExpirationDatetime) 2443 assert.Nil(t, buyOrderConf) 2444 2445 // Enter a GFN market order with an expiration time (which is invalid) 2446 sellOrder := getOrder(t, market, "SellOrder01", types.SideSell, 100, "party01", 10) 2447 sellOrder.TimeInForce = types.OrderTimeInForceGFN 2448 sellOrder.Type = types.OrderTypeMarket 2449 sellOrder.ExpiresAt = 100 2450 sellOrderConf, err := book.ob.SubmitOrder(sellOrder) 2451 assert.Error(t, err, types.ErrInvalidExpirationDatetime) 2452 assert.Nil(t, sellOrderConf) 2453 } 2454 2455 func TestOrderBook_GFNLimitInstantMatch(t *testing.T) { 2456 market := vgrand.RandomStr(5) 2457 book := getTestOrderBook(t, market) 2458 defer book.Finish() 2459 2460 logger := logging.NewTestLogger() 2461 defer logger.Sync() 2462 2463 // Normal limit buy order to match against 2464 buyOrder := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 10) 2465 buyOrderConf, err := book.ob.SubmitOrder(buyOrder) 2466 assert.NoError(t, err) 2467 assert.NotNil(t, buyOrderConf) 2468 2469 // Enter a GFN market order with an expiration time (which is invalid) 2470 sellOrder := getOrder(t, market, "SellOrder01", types.SideSell, 100, "party02", 10) 2471 sellOrder.TimeInForce = types.OrderTimeInForceGFN 2472 sellOrder.Type = types.OrderTypeLimit 2473 sellOrderConf, err := book.ob.SubmitOrder(sellOrder) 2474 assert.NoError(t, err) 2475 assert.NotNil(t, sellOrderConf) 2476 } 2477 2478 // AUCTION TESTING. 2479 func TestOrderBook_AuctionGFNAreRejected(t *testing.T) { 2480 market := vgrand.RandomStr(5) 2481 book := getTestOrderBook(t, market) 2482 defer book.Finish() 2483 2484 logger := logging.NewTestLogger() 2485 defer logger.Sync() 2486 2487 // Switch to auction mode 2488 book.ob.EnterAuction() 2489 assert.True(t, book.ob.InAuction()) 2490 2491 // Try to add an order of type GFN which should be rejected 2492 order := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 10) 2493 order.TimeInForce = types.OrderTimeInForceGFN 2494 orderConf, err := book.ob.SubmitOrder(order) 2495 assert.Equal(t, err, types.OrderErrorInvalidTimeInForce) 2496 assert.Nil(t, orderConf) 2497 } 2498 2499 func TestOrderBook_ContinuousGFAAreRejected(t *testing.T) { 2500 market := vgrand.RandomStr(5) 2501 book := getTestOrderBook(t, market) 2502 defer book.Finish() 2503 2504 logger := logging.NewTestLogger() 2505 defer logger.Sync() 2506 2507 // We start in continuous mode 2508 assert.False(t, book.ob.InAuction()) 2509 2510 // Try to add an order of type GFA which should be rejected 2511 order := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 10) 2512 order.TimeInForce = types.OrderTimeInForceGFA 2513 orderConf, err := book.ob.SubmitOrder(order) 2514 assert.Equal(t, err, types.OrderErrorInvalidTimeInForce) 2515 assert.Nil(t, orderConf) 2516 } 2517 2518 func TestOrderBook_GFNOrdersCancelledInAuction(t *testing.T) { 2519 market := vgrand.RandomStr(5) 2520 book := getTestOrderBook(t, market) 2521 defer book.Finish() 2522 2523 logger := logging.NewTestLogger() 2524 defer logger.Sync() 2525 2526 // We start in continuous mode 2527 assert.False(t, book.ob.InAuction()) 2528 2529 // Add a GFN order 2530 order := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 10) 2531 order.TimeInForce = types.OrderTimeInForceGFN 2532 orderConf, err := book.ob.SubmitOrder(order) 2533 assert.NoError(t, err) 2534 assert.NotNil(t, orderConf) 2535 2536 // Switch to auction and makes sure the order is cancelled 2537 orders := book.ob.EnterAuction() 2538 assert.Equal(t, len(orders), 1) 2539 assert.Equal(t, book.ob.GetTotalNumberOfOrders(), int64(1)) 2540 } 2541 2542 func TestOrderBook_GFAOrdersCancelledInContinuous(t *testing.T) { 2543 market := vgrand.RandomStr(5) 2544 book := getTestOrderBook(t, market) 2545 defer book.Finish() 2546 2547 logger := logging.NewTestLogger() 2548 defer logger.Sync() 2549 2550 // Flip straight to auction mode 2551 _ = book.ob.EnterAuction() 2552 assert.True(t, book.ob.InAuction()) 2553 2554 // Add a GFA order 2555 order := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 10) 2556 order.TimeInForce = types.OrderTimeInForceGFA 2557 orderConf, err := book.ob.SubmitOrder(order) 2558 assert.NoError(t, err) 2559 assert.NotNil(t, orderConf) 2560 2561 // Switch to continuous mode and makes sure the order is cancelled 2562 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2563 assert.NoError(t, err) 2564 assert.Equal(t, len(uncrossedOrders), 0) 2565 assert.Equal(t, book.ob.GetTotalNumberOfOrders(), int64(1)) 2566 assert.Equal(t, len(cancels), 1) 2567 } 2568 2569 func TestOrderBook_IndicativePriceAndVolumeState(t *testing.T) { 2570 market := vgrand.RandomStr(5) 2571 book := getTestOrderBook(t, market) 2572 defer book.Finish() 2573 2574 logger := logging.NewTestLogger() 2575 defer logger.Sync() 2576 2577 // We start in continuous trading mode 2578 assert.False(t, book.ob.InAuction()) 2579 assert.Equal(t, book.ob.GetTotalNumberOfOrders(), int64(0)) 2580 2581 // Get indicative auction price and volume which should be zero as we are out of auction 2582 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2583 assert.Equal(t, price.Uint64(), uint64(0)) 2584 assert.Equal(t, volume, uint64(0)) 2585 assert.Equal(t, side, types.SideUnspecified) 2586 price = book.ob.GetIndicativePrice() 2587 assert.Equal(t, price.Uint64(), uint64(0)) 2588 2589 // Switch to auction mode 2590 book.ob.EnterAuction() 2591 assert.True(t, book.ob.InAuction()) 2592 assert.Equal(t, book.ob.GetTotalNumberOfOrders(), int64(0)) 2593 2594 // Get indicative auction price and volume 2595 price, volume, side = book.ob.GetIndicativePriceAndVolume() 2596 assert.Equal(t, price.Uint64(), uint64(0)) 2597 assert.Equal(t, volume, uint64(0)) 2598 assert.Equal(t, side, types.SideUnspecified) 2599 price = book.ob.GetIndicativePrice() 2600 assert.Equal(t, price.Uint64(), uint64(0)) 2601 2602 // Leave auction and uncross the book 2603 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2604 assert.Nil(t, err) 2605 assert.False(t, book.ob.InAuction()) 2606 assert.Equal(t, book.ob.GetTotalNumberOfOrders(), int64(0)) 2607 assert.Equal(t, len(uncrossedOrders), 0) 2608 assert.Equal(t, len(cancels), 0) 2609 } 2610 2611 func TestOrderBook_IndicativePriceAndVolumeEmpty(t *testing.T) { 2612 market := vgrand.RandomStr(5) 2613 book := getTestOrderBook(t, market) 2614 defer book.Finish() 2615 2616 logger := logging.NewTestLogger() 2617 defer logger.Sync() 2618 2619 // Switch to auction mode 2620 book.ob.EnterAuction() 2621 assert.True(t, book.ob.InAuction()) 2622 2623 // No trades! 2624 2625 // Get indicative auction price and volume 2626 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2627 assert.Equal(t, price.Uint64(), uint64(0)) 2628 assert.Equal(t, volume, uint64(0)) 2629 assert.Equal(t, side, types.SideUnspecified) 2630 price = book.ob.GetIndicativePrice() 2631 assert.Equal(t, price.Uint64(), uint64(0)) 2632 2633 // Leave auction and uncross the book 2634 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2635 assert.False(t, book.ob.InAuction()) 2636 assert.Nil(t, err) 2637 assert.Equal(t, len(uncrossedOrders), 0) 2638 assert.Equal(t, len(cancels), 0) 2639 } 2640 2641 func TestOrderBook_IndicativePriceAndVolumeOnlyBuySide(t *testing.T) { 2642 market := vgrand.RandomStr(5) 2643 book := getTestOrderBook(t, market) 2644 defer book.Finish() 2645 2646 logger := logging.NewTestLogger() 2647 defer logger.Sync() 2648 2649 // Switch to auction mode 2650 book.ob.EnterAuction() 2651 2652 // Trades on just one side of the book 2653 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 100, "party01", 10) 2654 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 99, "party01", 10) 2655 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 98, "party01", 10) 2656 2657 // Get indicative auction price and volume 2658 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2659 assert.Equal(t, price.Uint64(), uint64(0)) 2660 assert.Equal(t, volume, uint64(0)) 2661 assert.Equal(t, side, types.SideUnspecified) 2662 price = book.ob.GetIndicativePrice() 2663 assert.Equal(t, price.Uint64(), uint64(0)) 2664 2665 // Leave auction and uncross the book 2666 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2667 assert.Nil(t, err) 2668 assert.Equal(t, len(uncrossedOrders), 0) 2669 assert.Equal(t, len(cancels), 0) 2670 2671 // All of the orders should remain on the book 2672 assert.Equal(t, book.ob.GetTotalNumberOfOrders(), int64(3)) 2673 } 2674 2675 func TestOrderBook_IndicativePriceAndVolumeOnlySellSide(t *testing.T) { 2676 market := vgrand.RandomStr(5) 2677 book := getTestOrderBook(t, market) 2678 defer book.Finish() 2679 2680 logger := logging.NewTestLogger() 2681 defer logger.Sync() 2682 2683 // Switch to auction mode 2684 book.ob.EnterAuction() 2685 2686 // Trades on just one side of the book 2687 makeOrder(t, book, market, "SellOrder01", types.SideSell, 100, "party01", 10) 2688 makeOrder(t, book, market, "SellOrder02", types.SideSell, 99, "party01", 10) 2689 makeOrder(t, book, market, "SellOrder03", types.SideSell, 98, "party01", 10) 2690 2691 // Get indicative auction price and volume 2692 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2693 assert.Equal(t, price.Uint64(), uint64(0)) 2694 assert.Equal(t, volume, uint64(0)) 2695 assert.Equal(t, side, types.SideUnspecified) 2696 price = book.ob.GetIndicativePrice() 2697 assert.Equal(t, price.Uint64(), uint64(0)) 2698 2699 // Leave auction and uncross the book 2700 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2701 assert.Nil(t, err) 2702 assert.Equal(t, len(uncrossedOrders), 0) 2703 assert.Equal(t, len(cancels), 0) 2704 2705 // All of the orders should remain on the book 2706 assert.Equal(t, book.ob.GetTotalNumberOfOrders(), int64(3)) 2707 } 2708 2709 func TestOrderBook_IndicativePriceAndVolume1(t *testing.T) { 2710 market := vgrand.RandomStr(5) 2711 book := getTestOrderBook(t, market) 2712 defer book.Finish() 2713 2714 logger := logging.NewTestLogger() 2715 defer logger.Sync() 2716 2717 // Switch to auction mode 2718 book.ob.EnterAuction() 2719 2720 // Populate buy side 2721 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 101, "party01", 20) 2722 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 100, "party01", 10) 2723 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 99, "party01", 20) 2724 makeOrder(t, book, market, "BuyOrder04", types.SideBuy, 98, "party01", 10) 2725 2726 // Populate sell side 2727 makeOrder(t, book, market, "SellOrder01", types.SideSell, 100, "party02", 10) 2728 makeOrder(t, book, market, "SellOrder02", types.SideSell, 101, "party02", 15) 2729 makeOrder(t, book, market, "SellOrder03", types.SideSell, 102, "party02", 5) 2730 makeOrder(t, book, market, "SellOrder04", types.SideSell, 103, "party02", 10) 2731 2732 // Get indicative auction price and volume 2733 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2734 assert.Equal(t, price.Uint64(), uint64(101)) 2735 assert.Equal(t, volume, uint64(20)) 2736 assert.Equal(t, side, types.SideBuy) 2737 price = book.ob.GetIndicativePrice() 2738 assert.Equal(t, price.Uint64(), uint64(101)) 2739 2740 // Get indicative trades 2741 trades, err := book.ob.GetIndicativeTrades() 2742 assert.NoError(t, err) 2743 for _, x := range trades { 2744 assert.Equal(t, price, x.Price) 2745 } 2746 2747 // Leave auction and uncross the book 2748 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2749 assert.Nil(t, err) 2750 assert.Equal(t, len(uncrossedOrders), 1) 2751 assert.Equal(t, len(cancels), 0) 2752 for _, o := range uncrossedOrders { 2753 for _, x := range o.Trades { 2754 assert.Equal(t, price, x.Price) 2755 } 2756 } 2757 } 2758 2759 func TestOrderBook_IndicativePriceAndVolume2(t *testing.T) { 2760 market := vgrand.RandomStr(5) 2761 book := getTestOrderBook(t, market) 2762 defer book.Finish() 2763 2764 logger := logging.NewTestLogger() 2765 defer logger.Sync() 2766 2767 // Switch to auction mode 2768 book.ob.EnterAuction() 2769 2770 // Populate buy side 2771 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 101, "party01", 30) 2772 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 100, "party01", 10) 2773 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 99, "party01", 20) 2774 makeOrder(t, book, market, "BuyOrder04", types.SideBuy, 98, "party01", 10) 2775 makeOrder(t, book, market, "BuyOrder05", types.SideBuy, 97, "party01", 5) 2776 2777 // Populate sell side 2778 makeOrder(t, book, market, "SellOrder01", types.SideSell, 100, "party02", 30) 2779 makeOrder(t, book, market, "SellOrder02", types.SideSell, 101, "party02", 15) 2780 makeOrder(t, book, market, "SellOrder03", types.SideSell, 102, "party02", 5) 2781 makeOrder(t, book, market, "SellOrder04", types.SideSell, 103, "party02", 10) 2782 2783 // Get indicative auction price and volume 2784 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2785 assert.Equal(t, uint64(100), price.Uint64()) 2786 assert.Equal(t, uint64(30), volume) 2787 assert.Equal(t, types.SideBuy, side) 2788 price = book.ob.GetIndicativePrice() 2789 assert.Equal(t, uint64(100), price.Uint64()) 2790 2791 // Get indicative trades 2792 trades, err := book.ob.GetIndicativeTrades() 2793 assert.NoError(t, err) 2794 for _, x := range trades { 2795 assert.Equal(t, price, x.Price) 2796 } 2797 2798 // Leave auction and uncross the book 2799 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2800 assert.Nil(t, err) 2801 assert.Equal(t, 1, len(uncrossedOrders)) 2802 assert.Equal(t, 0, len(cancels)) 2803 2804 for _, o := range uncrossedOrders { 2805 for _, x := range o.Trades { 2806 assert.Equal(t, price, x.Price) 2807 } 2808 } 2809 } 2810 2811 func TestOrderBook_IndicativePriceAndVolume3(t *testing.T) { 2812 market := vgrand.RandomStr(5) 2813 book := getTestOrderBook(t, market) 2814 defer book.Finish() 2815 2816 logger := logging.NewTestLogger() 2817 defer logger.Sync() 2818 2819 // Switch to auction mode 2820 book.ob.EnterAuction() 2821 2822 // Populate buy side 2823 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 104, "party01", 10) 2824 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 103, "party01", 20) 2825 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 102, "party01", 15) 2826 2827 // Populate sell side 2828 makeOrder(t, book, market, "SellOrder01", types.SideSell, 98, "party02", 10) 2829 makeOrder(t, book, market, "SellOrder02", types.SideSell, 97, "party02", 20) 2830 makeOrder(t, book, market, "SellOrder03", types.SideSell, 96, "party02", 15) 2831 2832 // Get indicative auction price and volume 2833 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2834 assert.Equal(t, 100, int(price.Uint64())) 2835 assert.Equal(t, 45, int(volume)) 2836 assert.Equal(t, types.SideBuy, side) 2837 price = book.ob.GetIndicativePrice() 2838 assert.Equal(t, 100, int(price.Uint64())) 2839 2840 // Get indicative trades 2841 trades, err := book.ob.GetIndicativeTrades() 2842 assert.NoError(t, err) 2843 for _, x := range trades { 2844 assert.Equal(t, price, x.Price) 2845 } 2846 2847 // Leave auction and uncross the book 2848 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2849 assert.Nil(t, err) 2850 assert.Equal(t, 3, len(uncrossedOrders)) 2851 assert.Equal(t, 0, len(cancels)) 2852 2853 for _, o := range uncrossedOrders { 2854 for _, x := range o.Trades { 2855 assert.Equal(t, price, x.Price) 2856 } 2857 } 2858 } 2859 2860 func TestOrderBook_IndicativePriceAndVolume4(t *testing.T) { 2861 market := vgrand.RandomStr(5) 2862 book := getTestOrderBook(t, market) 2863 defer book.Finish() 2864 2865 logger := logging.NewTestLogger() 2866 defer logger.Sync() 2867 2868 // Switch to auction mode 2869 book.ob.EnterAuction() 2870 2871 // Populate buy side 2872 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 99, "party01", 10) 2873 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 98, "party01", 25) 2874 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 97, "party01", 5) 2875 2876 // Populate sell side 2877 makeOrder(t, book, market, "SellOrder01", types.SideSell, 102, "party02", 30) 2878 makeOrder(t, book, market, "SellOrder02", types.SideSell, 101, "party02", 15) 2879 makeOrder(t, book, market, "SellOrder03", types.SideSell, 100, "party02", 5) 2880 2881 // Get indicative auction price and volume 2882 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2883 assert.Equal(t, uint64(0), price.Uint64()) 2884 assert.Equal(t, uint64(0), volume) 2885 assert.Equal(t, types.SideUnspecified, side) 2886 price = book.ob.GetIndicativePrice() 2887 assert.Equal(t, uint64(0), price.Uint64()) 2888 2889 // Get indicative trades 2890 trades, err := book.ob.GetIndicativeTrades() 2891 assert.NoError(t, err) 2892 for _, x := range trades { 2893 assert.Equal(t, price, x.Price) 2894 } 2895 2896 // Leave auction and uncross the book 2897 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2898 assert.Nil(t, err) 2899 assert.Equal(t, len(uncrossedOrders), 0) 2900 assert.Equal(t, len(cancels), 0) 2901 2902 for _, o := range uncrossedOrders { 2903 for _, x := range o.Trades { 2904 assert.Equal(t, price, x.Price) 2905 } 2906 } 2907 } 2908 2909 func TestOrderBook_IndicativePriceAndVolume5(t *testing.T) { 2910 market := vgrand.RandomStr(5) 2911 book := getTestOrderBook(t, market) 2912 defer book.Finish() 2913 2914 logger := logging.NewTestLogger() 2915 defer logger.Sync() 2916 2917 // Switch to auction mode 2918 book.ob.EnterAuction() 2919 2920 // Populate buy side 2921 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 103, "party01", 10) 2922 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 102, "party01", 9) 2923 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 101, "party01", 8) 2924 makeOrder(t, book, market, "BuyOrder04", types.SideBuy, 100, "party01", 7) 2925 makeOrder(t, book, market, "BuyOrder05", types.SideBuy, 99, "party01", 6) 2926 makeOrder(t, book, market, "BuyOrder06", types.SideBuy, 98, "party01", 5) 2927 makeOrder(t, book, market, "BuyOrder07", types.SideBuy, 97, "party01", 4) 2928 makeOrder(t, book, market, "BuyOrder08", types.SideBuy, 96, "party01", 3) 2929 makeOrder(t, book, market, "BuyOrder09", types.SideBuy, 95, "party01", 2) 2930 makeOrder(t, book, market, "BuyOrder10", types.SideBuy, 94, "party01", 1) 2931 2932 // Populate sell side 2933 makeOrder(t, book, market, "SellOrder01", types.SideSell, 105, "party02", 1) 2934 makeOrder(t, book, market, "SellOrder02", types.SideSell, 104, "party02", 2) 2935 makeOrder(t, book, market, "SellOrder03", types.SideSell, 103, "party02", 3) 2936 makeOrder(t, book, market, "SellOrder04", types.SideSell, 102, "party02", 4) 2937 makeOrder(t, book, market, "SellOrder05", types.SideSell, 101, "party02", 5) 2938 makeOrder(t, book, market, "SellOrder06", types.SideSell, 100, "party02", 6) 2939 makeOrder(t, book, market, "SellOrder07", types.SideSell, 99, "party02", 7) 2940 makeOrder(t, book, market, "SellOrder08", types.SideSell, 98, "party02", 8) 2941 makeOrder(t, book, market, "SellOrder09", types.SideSell, 97, "party02", 9) 2942 makeOrder(t, book, market, "SellOrder10", types.SideSell, 96, "party02", 10) 2943 2944 // Get indicative auction price and volume 2945 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2946 assert.Equal(t, uint64(99), price.Uint64()) 2947 assert.Equal(t, uint64(34), volume) 2948 assert.Equal(t, types.SideBuy, side) 2949 price = book.ob.GetIndicativePrice() 2950 assert.Equal(t, uint64(99), price.Uint64()) 2951 2952 // Get indicative trades 2953 trades, err := book.ob.GetIndicativeTrades() 2954 assert.NoError(t, err) 2955 for _, x := range trades { 2956 assert.Equal(t, price, x.Price) 2957 } 2958 2959 // Leave auction and uncross the book 2960 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 2961 assert.Nil(t, err) 2962 assert.Equal(t, 4, len(uncrossedOrders)) 2963 assert.Equal(t, 0, len(cancels)) 2964 2965 for _, o := range uncrossedOrders { 2966 for _, x := range o.Trades { 2967 assert.Equal(t, price, x.Price) 2968 } 2969 } 2970 } 2971 2972 // Set up an auction so that the sell side is processed when we uncross. 2973 func TestOrderBook_IndicativePriceAndVolume6(t *testing.T) { 2974 market := vgrand.RandomStr(5) 2975 book := getTestOrderBook(t, market) 2976 defer book.Finish() 2977 2978 logger := logging.NewTestLogger() 2979 defer logger.Sync() 2980 2981 // Switch to auction mode 2982 book.ob.EnterAuction() 2983 2984 // Populate buy side 2985 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 103, "party01", 10) 2986 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 102, "party01", 9) 2987 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 101, "party01", 8) 2988 makeOrder(t, book, market, "BuyOrder04", types.SideBuy, 100, "party01", 7) 2989 2990 // Populate sell side 2991 makeOrder(t, book, market, "SellOrder01", types.SideSell, 99, "party02", 1) 2992 makeOrder(t, book, market, "SellOrder02", types.SideSell, 98, "party02", 2) 2993 makeOrder(t, book, market, "SellOrder03", types.SideSell, 97, "party02", 3) 2994 makeOrder(t, book, market, "SellOrder04", types.SideSell, 96, "party02", 4) 2995 2996 // Get indicative auction price and volume 2997 price, volume, side := book.ob.GetIndicativePriceAndVolume() 2998 assert.Equal(t, 101, int(price.Uint64())) 2999 assert.Equal(t, 10, int(volume)) 3000 assert.Equal(t, types.SideBuy, side) 3001 price = book.ob.GetIndicativePrice() 3002 assert.Equal(t, 101, int(price.Uint64())) 3003 3004 // Get indicative trades 3005 trades, err := book.ob.GetIndicativeTrades() 3006 assert.NoError(t, err) 3007 for _, x := range trades { 3008 assert.Equal(t, price, x.Price) 3009 } 3010 3011 // Leave auction and uncross the book 3012 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 3013 assert.Nil(t, err) 3014 assert.Equal(t, len(uncrossedOrders), 1) 3015 assert.Equal(t, len(cancels), 0) 3016 3017 for _, o := range uncrossedOrders { 3018 for _, x := range o.Trades { 3019 assert.Equal(t, price, x.Price) 3020 } 3021 } 3022 } 3023 3024 // Check that multiple orders per price level work. 3025 func TestOrderBook_IndicativePriceAndVolume7(t *testing.T) { 3026 market := vgrand.RandomStr(5) 3027 book := getTestOrderBook(t, market) 3028 defer book.Finish() 3029 3030 logger := logging.NewTestLogger() 3031 defer logger.Sync() 3032 3033 // Switch to auction mode 3034 book.ob.EnterAuction() 3035 3036 // Populate buy side 3037 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 103, "party01", 10) 3038 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 103, "party01", 1) 3039 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 102, "party01", 9) 3040 makeOrder(t, book, market, "BuyOrder04", types.SideBuy, 102, "party01", 1) 3041 makeOrder(t, book, market, "BuyOrder05", types.SideBuy, 101, "party01", 8) 3042 makeOrder(t, book, market, "BuyOrder06", types.SideBuy, 101, "party01", 1) 3043 makeOrder(t, book, market, "BuyOrder07", types.SideBuy, 100, "party01", 7) 3044 makeOrder(t, book, market, "BuyOrder08", types.SideBuy, 100, "party01", 1) 3045 3046 // Populate sell side 3047 makeOrder(t, book, market, "SellOrder01", types.SideSell, 99, "party02", 10) 3048 makeOrder(t, book, market, "SellOrder02", types.SideSell, 98, "party02", 10) 3049 makeOrder(t, book, market, "SellOrder03", types.SideSell, 97, "party02", 10) 3050 makeOrder(t, book, market, "SellOrder04", types.SideSell, 96, "party02", 7) 3051 3052 // Get indicative auction price and volume 3053 price, volume, side := book.ob.GetIndicativePriceAndVolume() 3054 assert.Equal(t, uint64(99), price.Uint64()) 3055 assert.Equal(t, uint64(37), volume) 3056 assert.Equal(t, types.SideSell, side) 3057 price = book.ob.GetIndicativePrice() 3058 assert.Equal(t, uint64(99), price.Uint64()) 3059 3060 // Get indicative trades 3061 trades, err := book.ob.GetIndicativeTrades() 3062 assert.NoError(t, err) 3063 for _, x := range trades { 3064 assert.Equal(t, price, x.Price) 3065 } 3066 3067 // Leave auction and uncross the book 3068 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 3069 assert.Nil(t, err) 3070 assert.Equal(t, 4, len(uncrossedOrders)) 3071 assert.Equal(t, 0, len(cancels)) 3072 3073 for _, o := range uncrossedOrders { 3074 for _, x := range o.Trades { 3075 assert.Equal(t, price, x.Price) 3076 } 3077 } 3078 } 3079 3080 func TestOrderBook_IndicativePriceAndVolume8(t *testing.T) { 3081 market := vgrand.RandomStr(5) 3082 book := getTestOrderBook(t, market) 3083 defer book.Finish() 3084 3085 logger := logging.NewTestLogger() 3086 defer logger.Sync() 3087 3088 // Switch to auction mode 3089 book.ob.EnterAuction() 3090 3091 // Populate buy side 3092 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 103, "party01", 10) 3093 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 103, "party01", 1) 3094 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 102, "party01", 9) 3095 makeOrder(t, book, market, "BuyOrder04", types.SideBuy, 102, "party01", 1) 3096 makeOrder(t, book, market, "BuyOrder05", types.SideBuy, 101, "party01", 8) 3097 makeOrder(t, book, market, "BuyOrder06", types.SideBuy, 101, "party01", 1) 3098 makeOrder(t, book, market, "BuyOrder07", types.SideBuy, 100, "party01", 7) 3099 makeOrder(t, book, market, "BuyOrder08", types.SideBuy, 100, "party01", 1) 3100 3101 // Populate sell side 3102 makeOrder(t, book, market, "SellOrder01", types.SideSell, 99, "party02", 10) 3103 makeOrder(t, book, market, "SellOrder02", types.SideSell, 98, "party02", 10) 3104 makeOrder(t, book, market, "SellOrder03", types.SideSell, 97, "party02", 10) 3105 makeOrder(t, book, market, "SellOrder04", types.SideSell, 96, "party02", 9) 3106 3107 // Get indicative auction price and volume 3108 price, volume, side := book.ob.GetIndicativePriceAndVolume() 3109 assert.Equal(t, 99, int(price.Uint64())) 3110 assert.Equal(t, 38, int(volume)) 3111 assert.Equal(t, types.SideBuy, side) 3112 price = book.ob.GetIndicativePrice() 3113 assert.Equal(t, 99, int(price.Uint64())) 3114 3115 // Get indicative trades 3116 trades, err := book.ob.GetIndicativeTrades() 3117 assert.NoError(t, err) 3118 for _, x := range trades { 3119 assert.Equal(t, price, x.Price) 3120 } 3121 3122 // Leave auction and uncross the book 3123 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 3124 assert.Nil(t, err) 3125 assert.Equal(t, 8, len(uncrossedOrders)) 3126 assert.Equal(t, 0, len(cancels)) 3127 3128 for _, o := range uncrossedOrders { 3129 for _, x := range o.Trades { 3130 assert.Equal(t, price, x.Price) 3131 } 3132 } 3133 } 3134 3135 func TestOrderBook_IndicativePriceAndVolume9(t *testing.T) { 3136 market := vgrand.RandomStr(5) 3137 book := getTestOrderBook(t, market) 3138 defer book.Finish() 3139 3140 logger := logging.NewTestLogger() 3141 defer logger.Sync() 3142 3143 // Switch to auction mode 3144 book.ob.EnterAuction() 3145 3146 // Populate buy side 3147 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 110, "party01", 1) 3148 makeOrder(t, book, market, "SellOrder01", types.SideSell, 110, "party02", 1) 3149 3150 makeOrder(t, book, market, "BuyOrder01-2", types.SideBuy, 111, "party01", 1) 3151 makeOrder(t, book, market, "SellOrder01-2", types.SideSell, 111, "party02", 1) 3152 3153 makeOrder(t, book, market, "BuyOrder01-3", types.SideBuy, 133, "party01", 2) 3154 makeOrder(t, book, market, "SellOrder01-3", types.SideSell, 133, "party02", 2) 3155 3156 makeOrder(t, book, market, "BuyOrder01-4", types.SideBuy, 303, "party01", 10) 3157 makeOrder(t, book, market, "SellOrder01-4", types.SideSell, 303, "party02", 10) 3158 3159 // Get indicative auction price and volume 3160 price, volume, side := book.ob.GetIndicativePriceAndVolume() 3161 assert.Equal(t, 303, int(price.Uint64())) 3162 assert.Equal(t, 10, int(volume)) 3163 assert.Equal(t, types.SideBuy, side) 3164 price = book.ob.GetIndicativePrice() 3165 assert.Equal(t, 303, int(price.Uint64())) 3166 3167 // Get indicative trades 3168 trades, err := book.ob.GetIndicativeTrades() 3169 assert.NoError(t, err) 3170 for _, x := range trades { 3171 assert.Equal(t, price, x.Price) 3172 } 3173 3174 // Leave auction and uncross the book 3175 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 3176 assert.Nil(t, err) 3177 assert.Equal(t, 1, len(uncrossedOrders)) 3178 assert.Equal(t, 0, len(cancels)) 3179 3180 for _, o := range uncrossedOrders { 3181 for _, x := range o.Trades { 3182 assert.Equal(t, price, x.Price) 3183 } 3184 } 3185 } 3186 3187 // check behaviour consistent in the presence of wash trades. 3188 func TestOrderBook_IndicativePriceAndVolume10(t *testing.T) { 3189 market := vgrand.RandomStr(5) 3190 book := getTestOrderBook(t, market) 3191 defer book.Finish() 3192 3193 logger := logging.NewTestLogger() 3194 defer logger.Sync() 3195 3196 // Switch to auction mode 3197 book.ob.EnterAuction() 3198 3199 // Populate buy side 3200 makeOrder(t, book, market, "BuyOrder01", types.SideBuy, 103, "party01", 10) 3201 makeOrder(t, book, market, "BuyOrder02", types.SideBuy, 102, "party01", 9) 3202 makeOrder(t, book, market, "BuyOrder03", types.SideBuy, 101, "party01", 8) 3203 makeOrder(t, book, market, "BuyOrder04", types.SideBuy, 100, "party01", 7) 3204 3205 // Populate sell side 3206 makeOrder(t, book, market, "SellOrder01", types.SideSell, 99, "party02", 1) 3207 makeOrder(t, book, market, "SellOrder02", types.SideSell, 98, "party01", 1) 3208 makeOrder(t, book, market, "SellOrder03", types.SideSell, 98, "party02", 1) 3209 makeOrder(t, book, market, "SellOrder04", types.SideSell, 97, "party02", 3) 3210 makeOrder(t, book, market, "SellOrder05", types.SideSell, 96, "party02", 4) 3211 3212 // Get indicative auction price and volume 3213 price, volume, side := book.ob.GetIndicativePriceAndVolume() 3214 assert.Equal(t, 101, int(price.Uint64())) 3215 assert.Equal(t, 10, int(volume)) 3216 assert.Equal(t, types.SideBuy, side) 3217 price = book.ob.GetIndicativePrice() 3218 assert.Equal(t, 101, int(price.Uint64())) 3219 3220 // Get indicative trades 3221 trades, err := book.ob.GetIndicativeTrades() 3222 assert.NoError(t, err) 3223 for _, x := range trades { 3224 assert.Equal(t, price, x.Price) 3225 } 3226 3227 // Leave auction and uncross the book 3228 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 3229 assert.Nil(t, err) 3230 assert.Equal(t, len(uncrossedOrders), 1) 3231 assert.Equal(t, len(cancels), 0) 3232 3233 nTrades := 0 3234 for _, o := range uncrossedOrders { 3235 for _, x := range o.Trades { 3236 nTrades++ 3237 assert.Equal(t, price, x.Price) 3238 } 3239 } 3240 assert.Equal(t, len(trades), nTrades) 3241 } 3242 3243 func TestOrderBook_UncrossTest1(t *testing.T) { 3244 market := vgrand.RandomStr(5) 3245 book := getTestOrderBook(t, market) 3246 defer book.Finish() 3247 3248 logger := logging.NewTestLogger() 3249 defer logger.Sync() 3250 3251 // Switch to auction mode 3252 book.ob.EnterAuction() 3253 3254 bo1 := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 5) 3255 bo1.TimeInForce = types.OrderTimeInForceGFA 3256 book.ob.SubmitOrder(bo1) 3257 3258 so1 := getOrder(t, market, "SellOrder01", types.SideSell, 100, "party02", 5) 3259 so1.TimeInForce = types.OrderTimeInForceGFA 3260 book.ob.SubmitOrder(so1) 3261 3262 bo2 := getOrder(t, market, "BuyOrder02", types.SideBuy, 100, "party01", 5) 3263 bo2.TimeInForce = types.OrderTimeInForceGFA 3264 book.ob.SubmitOrder(bo2) 3265 3266 so2 := getOrder(t, market, "SellOrder02", types.SideSell, 101, "party02", 5) 3267 so2.TimeInForce = types.OrderTimeInForceGFA 3268 book.ob.SubmitOrder(so2) 3269 3270 // Get indicative auction price and volume 3271 price, volume, side := book.ob.GetIndicativePriceAndVolume() 3272 assert.Equal(t, price.Uint64(), uint64(100)) 3273 assert.Equal(t, volume, uint64(5)) 3274 assert.Equal(t, side, types.SideSell) 3275 price = book.ob.GetIndicativePrice() 3276 assert.Equal(t, price.Uint64(), uint64(100)) 3277 3278 // Get indicative trades 3279 trades, err := book.ob.GetIndicativeTrades() 3280 assert.NoError(t, err) 3281 for _, x := range trades { 3282 assert.Equal(t, price, x.Price) 3283 } 3284 3285 // Leave auction and uncross the book 3286 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 3287 assert.Nil(t, err) 3288 assert.Equal(t, len(uncrossedOrders), 1) 3289 assert.Equal(t, len(cancels), 2) 3290 3291 for _, o := range uncrossedOrders { 3292 for _, x := range o.Trades { 3293 assert.Equal(t, price, x.Price) 3294 } 3295 } 3296 } 3297 3298 // this is a test for issue 2060 to ensure we process FOK orders properly. 3299 func TestOrderBook_NetworkOrderSuccess(t *testing.T) { 3300 market := vgrand.RandomStr(5) 3301 book := getTestOrderBook(t, market) 3302 defer book.Finish() 3303 3304 orders := []*types.Order{ 3305 { 3306 Status: types.OrderStatusActive, 3307 Type: types.OrderTypeLimit, 3308 MarketID: market, 3309 ID: "123456", 3310 Side: types.SideBuy, 3311 Price: num.NewUint(100), 3312 OriginalPrice: num.NewUint(100), 3313 Party: "A", 3314 Size: 100, 3315 Remaining: 100, 3316 TimeInForce: types.OrderTimeInForceGTC, 3317 CreatedAt: 10, 3318 }, 3319 { 3320 Status: types.OrderStatusActive, 3321 Type: types.OrderTypeLimit, 3322 MarketID: market, 3323 ID: "234561", 3324 Side: types.SideBuy, 3325 Price: num.NewUint(1), 3326 OriginalPrice: num.NewUint(1), 3327 Party: "B", 3328 Size: 100, 3329 Remaining: 100, 3330 TimeInForce: types.OrderTimeInForceGTC, 3331 CreatedAt: 11, 3332 }, 3333 } 3334 3335 // now we add the trades to the book 3336 for _, o := range orders { 3337 cnfm, err := book.ob.SubmitOrder(o) 3338 assert.NoError(t, err) 3339 assert.Len(t, cnfm.Trades, 0) 3340 } 3341 3342 // no price for network order 3343 // we want to consume the whole book 3344 netorder := &types.Order{ 3345 Status: types.OrderStatusActive, 3346 Type: types.OrderTypeNetwork, 3347 MarketID: market, 3348 ID: "345612", 3349 Side: types.SideSell, 3350 Party: "C", 3351 Size: 200, 3352 Remaining: 200, 3353 TimeInForce: types.OrderTimeInForceFOK, 3354 CreatedAt: 12, 3355 } 3356 3357 cnfm, err := book.ob.SubmitOrder(netorder) 3358 assert.NoError(t, err) 3359 assert.Equal(t, types.OrderStatusFilled, netorder.Status) 3360 assert.Equal(t, 50, int(netorder.Price.Uint64())) 3361 assert.Equal(t, 0, int(netorder.Remaining)) 3362 _ = cnfm 3363 } 3364 3365 func TestOrderBook_GetTradesInLineWithSubmitOrderDuringAuction(t *testing.T) { 3366 market := vgrand.RandomStr(5) 3367 book := getTestOrderBook(t, market) 3368 3369 orders := book.ob.EnterAuction() 3370 assert.Equal(t, 0, len(orders)) 3371 order1Id := vgcrypto.RandomHash() 3372 order2Id := vgcrypto.RandomHash() 3373 3374 order1 := &types.Order{ 3375 Status: types.OrderStatusActive, 3376 Type: types.OrderTypeLimit, 3377 MarketID: market, 3378 ID: order1Id, 3379 Side: types.SideSell, 3380 Price: num.NewUint(100), 3381 OriginalPrice: num.NewUint(100), 3382 Party: "A", 3383 Size: 100, 3384 Remaining: 100, 3385 TimeInForce: types.OrderTimeInForceGTC, 3386 CreatedAt: 10, 3387 } 3388 3389 trades, getErr := book.ob.GetTrades(order1) 3390 assert.NoError(t, getErr) 3391 confirmation, err := book.ob.SubmitOrder(order1) 3392 3393 assert.Equal(t, nil, err) 3394 assert.NotNil(t, confirmation) 3395 assert.Equal(t, order1Id, confirmation.Order.ID) 3396 assert.Equal(t, 0, len(confirmation.Trades)) 3397 assert.Equal(t, len(trades), len(confirmation.Trades)) 3398 3399 order2 := &types.Order{ 3400 Status: types.OrderStatusActive, 3401 Type: types.OrderTypeLimit, 3402 MarketID: market, 3403 ID: order2Id, 3404 Side: types.SideBuy, 3405 Price: num.NewUint(100), 3406 OriginalPrice: num.NewUint(100), 3407 Party: "B", 3408 Size: 20, 3409 Remaining: 20, 3410 TimeInForce: types.OrderTimeInForceGTC, 3411 CreatedAt: 10, 3412 } 3413 trades, getErr = book.ob.GetTrades(order2) 3414 assert.NoError(t, getErr) 3415 confirmation, err = book.ob.SubmitOrder(order2) 3416 3417 assert.Equal(t, nil, err) 3418 assert.NotNil(t, confirmation) 3419 assert.Equal(t, order2Id, confirmation.Order.ID) 3420 assert.Equal(t, 0, len(confirmation.Trades)) 3421 assert.Equal(t, len(trades), len(confirmation.Trades)) 3422 3423 // Confirm both orders still on the book 3424 order, err := book.ob.GetOrderByID(order1Id) 3425 assert.NotNil(t, order) 3426 assert.Nil(t, err) 3427 order, err = book.ob.GetOrderByID(order2Id) 3428 assert.NotNil(t, order) 3429 assert.Nil(t, err) 3430 } 3431 3432 func TestOrderBook_AuctionUncrossWashTrades(t *testing.T) { 3433 market := vgrand.RandomStr(5) 3434 book := getTestOrderBook(t, market) 3435 defer book.Finish() 3436 3437 logger := logging.NewTestLogger() 3438 defer logger.Sync() 3439 3440 // Switch to auction mode 3441 book.ob.EnterAuction() 3442 3443 bo1 := getOrder(t, market, "BuyOrder01", types.SideBuy, 100, "party01", 5) 3444 bo1.TimeInForce = types.OrderTimeInForceGFA 3445 book.ob.SubmitOrder(bo1) 3446 3447 so1 := getOrder(t, market, "SellOrder01", types.SideSell, 100, "party01", 5) 3448 so1.TimeInForce = types.OrderTimeInForceGFA 3449 book.ob.SubmitOrder(so1) 3450 3451 // Get indicative auction price and volume 3452 price, volume, side := book.ob.GetIndicativePriceAndVolume() 3453 assert.Equal(t, price.Uint64(), uint64(100)) 3454 assert.Equal(t, volume, uint64(5)) 3455 assert.Equal(t, side, types.SideBuy) 3456 3457 // Get indicative trades 3458 trades, err := book.ob.GetIndicativeTrades() 3459 assert.NoError(t, err) 3460 assert.Equal(t, len(trades), 1) 3461 3462 // Leave auction and uncross the book 3463 uncrossedOrders, cancels, err := book.ob.LeaveAuction(time.Now()) 3464 assert.Nil(t, err) 3465 assert.Equal(t, len(uncrossedOrders), 1) 3466 assert.Equal(t, len(uncrossedOrders[0].Trades), 1) 3467 assert.Equal(t, len(cancels), 0) 3468 3469 // Assure indicative trade has same (relevant) data as the actual trade 3470 // because the trades are generated when calling LeaveAuction, the aggressor will be unspecified. 3471 assert.Equal(t, uncrossedOrders[0].Trades[0].Aggressor, types.SideUnspecified) 3472 // and thus the aggressor side will not match the value we get from the indicative trades 3473 assert.NotEqual(t, uncrossedOrders[0].Trades[0].Aggressor, trades[0].Aggressor) 3474 assert.Equal(t, uncrossedOrders[0].Trades[0].Buyer, trades[0].Buyer) 3475 assert.Equal(t, uncrossedOrders[0].Trades[0].Seller, trades[0].Seller) 3476 assert.Equal(t, uncrossedOrders[0].Trades[0].Size, trades[0].Size) 3477 assert.Equal(t, uncrossedOrders[0].Trades[0].Price, trades[0].Price) 3478 3479 // Assure trade is indeed a wash trade 3480 assert.Equal(t, trades[0].Buyer, trades[0].Seller) 3481 } 3482 3483 func TestOrderBook_AuctionUncrossTamlyn(t *testing.T) { 3484 market := vgrand.RandomStr(5) 3485 book := getTestOrderBook(t, market) 3486 defer book.Finish() 3487 3488 logger := logging.NewTestLogger() 3489 defer logger.Sync() 3490 3491 // Switch to auction mode 3492 book.ob.EnterAuction() 3493 3494 order1 := getOrder(t, market, "Order1", types.SideBuy, 16, "Tamlyn", 100) 3495 order1.TimeInForce = types.OrderTimeInForceGFA 3496 conf, err := book.ob.SubmitOrder(order1) 3497 require.NoError(t, err) 3498 assert.NotNil(t, conf) 3499 3500 order2 := getOrder(t, market, "Order2", types.SideSell, 20, "Tamlyn", 100) 3501 order2.TimeInForce = types.OrderTimeInForceGFA 3502 conf, err = book.ob.SubmitOrder(order2) 3503 require.NoError(t, err) 3504 assert.NotNil(t, conf) 3505 3506 order3 := getOrder(t, market, "Order3", types.SideSell, 3, "Tamlyn", 100) 3507 order3.TimeInForce = types.OrderTimeInForceGFA 3508 conf, err = book.ob.SubmitOrder(order3) 3509 require.NoError(t, err) 3510 assert.NotNil(t, conf) 3511 3512 order4 := getOrder(t, market, "Order4", types.SideSell, 18, "David", 100) 3513 order4.TimeInForce = types.OrderTimeInForceGFA 3514 conf, err = book.ob.SubmitOrder(order4) 3515 require.NoError(t, err) 3516 assert.NotNil(t, conf) 3517 3518 order5 := getOrder(t, market, "Order5", types.SideBuy, 1000, "Tamlyn", 100) 3519 order5.TimeInForce = types.OrderTimeInForceGFA 3520 conf, err = book.ob.SubmitOrder(order5) 3521 require.NoError(t, err) 3522 assert.NotNil(t, conf) 3523 3524 order6 := getOrder(t, market, "Order6", types.SideBuy, 2000, "David", 100) 3525 order6.TimeInForce = types.OrderTimeInForceGFA 3526 conf, err = book.ob.SubmitOrder(order6) 3527 require.NoError(t, err) 3528 assert.NotNil(t, conf) 3529 3530 order7 := getOrder(t, market, "Order7", types.SideSell, 14, "Tamlyn", 15) 3531 order7.TimeInForce = types.OrderTimeInForceGFA 3532 conf, err = book.ob.SubmitOrder(order7) 3533 require.NoError(t, err) 3534 assert.NotNil(t, conf) 3535 3536 order8 := getOrder(t, market, "Order8", types.SideBuy, 14, "Tamlyn", 2) 3537 order8.TimeInForce = types.OrderTimeInForceGFA 3538 conf, err = book.ob.SubmitOrder(order8) 3539 require.NoError(t, err) 3540 assert.NotNil(t, conf) 3541 3542 order9 := getOrder(t, market, "Order9", types.SideSell, 1, "David", 10) 3543 order9.TimeInForce = types.OrderTimeInForceGFA 3544 conf, err = book.ob.SubmitOrder(order9) 3545 require.NoError(t, err) 3546 assert.NotNil(t, conf) 3547 3548 // Get indicative auction price and volume 3549 // price, volume, side := book.ob.GetIndicativePriceAndVolume() 3550 // assert.Equal(t, price, uint64(100)) 3551 // assert.Equal(t, volume, uint64(5)) 3552 // assert.Equal(t, side, types.Side_SIDE_BUY) 3553 3554 // Leave auction and uncross the book 3555 // uncrossedOrders, cancels, err := book.ob.leaveAuction(time.Now()) 3556 // assert.Nil(t, err) 3557 // assert.Equal(t, len(uncrossedOrders), 1) 3558 // assert.Equal(t, len(cancels), 0) 3559 } 3560 3561 // Add some pegged orders to the order book and check they are parked when going into auction. 3562 func TestOrderBook_PeggedOrders(t *testing.T) { 3563 market := vgrand.RandomStr(5) 3564 book := getTestOrderBook(t, market) 3565 defer book.Finish() 3566 3567 logger := logging.NewTestLogger() 3568 defer logger.Sync() 3569 3570 require.Equal(t, uint64(0), book.ob.GetPeggedOrdersCount()) 3571 3572 // We need some orders on the book to get a valid bestbis/bestask/mid price 3573 makeOrder(t, book, market, "PriceSetterBuy", types.SideBuy, 100, "party01", 1) 3574 makeOrder(t, book, market, "PriceSetterSell", types.SideSell, 101, "party01", 1) 3575 3576 require.Equal(t, uint64(0), book.ob.GetPeggedOrdersCount()) 3577 3578 bestask, err := book.ob.GetBestAskPrice() 3579 assert.NoError(t, err) 3580 bestbid, err := book.ob.GetBestBidPrice() 3581 assert.NoError(t, err) 3582 assert.Equal(t, bestask.Uint64(), uint64(101)) 3583 assert.Equal(t, bestbid.Uint64(), uint64(100)) 3584 3585 orderID := crypto.RandomHash() 3586 bp1 := getOrder(t, market, orderID, types.SideBuy, 100, "party01", 5) 3587 bp1.PeggedOrder = &types.PeggedOrder{ 3588 Reference: types.PeggedReferenceMid, 3589 Offset: num.NewUint(3), 3590 } 3591 book.ob.SubmitOrder(bp1) 3592 3593 require.Equal(t, uint64(1), book.ob.GetPeggedOrdersCount()) 3594 3595 sp1 := getOrder(t, market, "SellPeg1", types.SideSell, 100, "party01", 5) 3596 sp1.PeggedOrder = &types.PeggedOrder{ 3597 Reference: types.PeggedReferenceMid, 3598 Offset: num.NewUint(3), 3599 } 3600 book.ob.SubmitOrder(sp1) 3601 3602 // wash trade, doesn't go through so still expect 1 3603 require.Equal(t, uint64(1), book.ob.GetPeggedOrdersCount()) 3604 3605 // Leave auction and uncross the book 3606 cancels := book.ob.EnterAuction() 3607 assert.Equal(t, len(cancels), 0) 3608 3609 book.ob.CancelOrder(bp1) 3610 require.Equal(t, uint64(0), book.ob.GetPeggedOrdersCount()) 3611 } 3612 3613 func TestOrderBook_BidAndAskPresentAfterAuction(t *testing.T) { 3614 market := vgrand.RandomStr(5) 3615 book := getTestOrderBook(t, market) 3616 defer book.Finish() 3617 3618 logger := logging.NewTestLogger() 3619 defer logger.Sync() 3620 3621 // Switch to auction mode 3622 book.ob.EnterAuction() 3623 assert.True(t, book.ob.InAuction()) 3624 3625 require.Equal(t, false, book.ob.BidAndAskPresentAfterAuction()) 3626 require.Equal(t, false, book.ob.CanUncross()) 3627 3628 matchingPrice := uint64(100) 3629 party1 := "party1" 3630 party2 := "party2" 3631 makeOrder(t, book, market, vgcrypto.RandomHash(), types.SideBuy, matchingPrice-1, party1, 1) 3632 3633 require.Equal(t, false, book.ob.BidAndAskPresentAfterAuction()) 3634 require.Equal(t, false, book.ob.CanUncross()) 3635 3636 makeOrder(t, book, market, vgcrypto.RandomHash(), types.SideSell, matchingPrice+1, party2, 1) 3637 3638 require.Equal(t, true, book.ob.BidAndAskPresentAfterAuction()) 3639 require.Equal(t, false, book.ob.CanUncross()) 3640 3641 makeOrder(t, book, market, vgcrypto.RandomHash(), types.SideBuy, matchingPrice, party1, 1) 3642 3643 require.Equal(t, true, book.ob.BidAndAskPresentAfterAuction()) 3644 require.Equal(t, false, book.ob.CanUncross()) 3645 3646 makeOrder(t, book, market, vgcrypto.RandomHash(), types.SideSell, matchingPrice, party2, 1) 3647 3648 require.Equal(t, true, book.ob.BidAndAskPresentAfterAuction()) 3649 require.Equal(t, true, book.ob.CanUncross()) 3650 3651 _, err := book.ob.CancelAllOrders(party1) 3652 require.NoError(t, err) 3653 _, err = book.ob.CancelAllOrders(party2) 3654 require.NoError(t, err) 3655 3656 require.Equal(t, int64(0), book.ob.GetTotalNumberOfOrders()) 3657 3658 makeOrder(t, book, market, vgcrypto.RandomHash(), types.SideBuy, matchingPrice, party1, 1) 3659 3660 require.Equal(t, false, book.ob.BidAndAskPresentAfterAuction()) 3661 require.Equal(t, false, book.ob.CanUncross()) 3662 3663 makeOrder(t, book, market, vgcrypto.RandomHash(), types.SideSell, matchingPrice, party2, 1) 3664 3665 require.Equal(t, false, book.ob.BidAndAskPresentAfterAuction()) 3666 require.Equal(t, false, book.ob.CanUncross()) 3667 } 3668 3669 func TestOrderBook_AuctionUncrossWashTrades2(t *testing.T) { 3670 market := vgrand.RandomStr(5) 3671 book := getTestOrderBook(t, market) 3672 defer book.Finish() 3673 3674 logger := logging.NewTestLogger() 3675 defer logger.Sync() 3676 3677 // Switch to auction mode 3678 book.ob.EnterAuction() 3679 3680 tt0_0 := getOrder(t, market, "tt_0_0", types.SideBuy, 90, "tt_0", 1000) 3681 _, err := book.ob.SubmitOrder(tt0_0) 3682 require.NoError(t, err) 3683 tt0_1 := getOrder(t, market, "tt_0_1", types.SideSell, 200, "tt_0", 1000) 3684 _, err = book.ob.SubmitOrder(tt0_1) 3685 require.NoError(t, err) 3686 3687 tt1_0 := getOrder(t, market, "tt_1_0", types.SideSell, 110, "tt_1", 50) 3688 _, err = book.ob.SubmitOrder(tt1_0) 3689 require.NoError(t, err) 3690 tt2_0 := getOrder(t, market, "tt_2_0", types.SideBuy, 110, "tt_2", 20) 3691 _, err = book.ob.SubmitOrder(tt2_0) 3692 require.NoError(t, err) 3693 tt3_0 := getOrder(t, market, "tt_3_0", types.SideBuy, 110, "tt_3", 30) 3694 _, err = book.ob.SubmitOrder(tt3_0) 3695 require.NoError(t, err) 3696 3697 indicativeTrades, err := book.ob.GetIndicativeTrades() 3698 require.NoError(t, err) 3699 require.Equal(t, 2, len(indicativeTrades)) 3700 3701 require.Equal(t, tt1_0.Party, indicativeTrades[0].Seller) 3702 require.Equal(t, tt2_0.Party, indicativeTrades[0].Buyer) 3703 require.Equal(t, tt2_0.Size, indicativeTrades[0].Size) 3704 require.Equal(t, tt2_0.Price, indicativeTrades[0].Price) 3705 3706 require.Equal(t, tt1_0.Party, indicativeTrades[1].Seller) 3707 require.Equal(t, tt3_0.Party, indicativeTrades[1].Buyer) 3708 require.Equal(t, tt3_0.Size, indicativeTrades[1].Size) 3709 require.Equal(t, tt3_0.Price, indicativeTrades[1].Price) 3710 3711 // Add wash trades 3712 tt4_0 := getOrder(t, market, "tt_4_0", types.SideSell, 110, "tt_4", 40) 3713 _, err = book.ob.SubmitOrder(tt4_0) 3714 require.NoError(t, err) 3715 tt4_1 := getOrder(t, market, "tt_4_1", types.SideBuy, 110, "tt_4", 40) 3716 _, err = book.ob.SubmitOrder(tt4_1) 3717 require.NoError(t, err) 3718 3719 indicativeTrades, err = book.ob.GetIndicativeTrades() 3720 require.NoError(t, err) 3721 // Expecting one more indicative trade now 3722 require.Equal(t, 3, len(indicativeTrades)) 3723 3724 // The first two should stay as they were 3725 require.Equal(t, tt1_0.Party, indicativeTrades[0].Seller) 3726 require.Equal(t, tt2_0.Party, indicativeTrades[0].Buyer) 3727 require.Equal(t, tt2_0.Size, indicativeTrades[0].Size) 3728 require.Equal(t, tt2_0.Price, indicativeTrades[0].Price) 3729 3730 require.Equal(t, tt1_0.Party, indicativeTrades[1].Seller) 3731 require.Equal(t, tt3_0.Party, indicativeTrades[1].Buyer) 3732 require.Equal(t, tt3_0.Size, indicativeTrades[1].Size) 3733 require.Equal(t, tt3_0.Price, indicativeTrades[1].Price) 3734 3735 // The third one should be the wash trade 3736 require.Equal(t, tt4_0.Party, indicativeTrades[2].Seller) 3737 require.Equal(t, tt4_1.Party, indicativeTrades[2].Buyer) 3738 require.Equal(t, tt4_0.Size, indicativeTrades[2].Size) 3739 require.Equal(t, tt4_0.Price, indicativeTrades[2].Price) 3740 3741 confs, ordersToCancel, err := book.ob.LeaveAuction(time.Now()) 3742 require.NoError(t, err) 3743 require.Equal(t, 3, len(confs)) 3744 require.Equal(t, 0, len(ordersToCancel)) 3745 3746 for i, c := range confs { 3747 require.Equal(t, 1, len(c.Trades)) 3748 require.Equal(t, c.Trades[0].Buyer, indicativeTrades[i].Buyer) 3749 require.Equal(t, c.Trades[0].Seller, indicativeTrades[i].Seller) 3750 require.Equal(t, c.Trades[0].Size, indicativeTrades[i].Size) 3751 require.Equal(t, c.Trades[0].Price, indicativeTrades[i].Price) 3752 } 3753 } 3754 3755 // just generates random orders with the given prices. Uses parties provided by accessing 3756 // parties[i%len(parties)], where i is the current index in the buy/sell prices slice. 3757 // if parties is empty, []string{"A", "B"} is used. 3758 func getTestOrders(t *testing.T, market string, fixedSize uint64, buyPrices, sellPrices []uint64) []*types.Order { 3759 t.Helper() 3760 parties := []string{"A", "B"} 3761 orders := make([]*types.Order, 0, len(buyPrices)+len(sellPrices)) 3762 for i, p := range buyPrices { 3763 size := fixedSize 3764 if size == 0 { 3765 size = uint64(rand.Int63n(10-1) + 1) 3766 } 3767 orders = append(orders, &types.Order{ 3768 ID: vgcrypto.RandomHash(), 3769 Status: types.OrderStatusActive, 3770 Type: types.OrderTypeLimit, 3771 MarketID: market, 3772 Party: parties[i%len(parties)], 3773 Side: types.SideBuy, 3774 Price: num.NewUint(p), 3775 OriginalPrice: num.NewUint(p), 3776 Size: size, 3777 Remaining: size, 3778 TimeInForce: types.OrderTimeInForceGTC, 3779 }) 3780 } 3781 for i, p := range sellPrices { 3782 size := fixedSize 3783 if size == 0 { 3784 size = uint64(rand.Int63n(10-1) + 1) 3785 } 3786 orders = append(orders, &types.Order{ 3787 ID: vgcrypto.RandomHash(), 3788 Status: types.OrderStatusActive, 3789 Type: types.OrderTypeLimit, 3790 MarketID: market, 3791 Party: parties[i%len(parties)], 3792 Side: types.SideSell, 3793 Price: num.NewUint(p), 3794 OriginalPrice: num.NewUint(p), 3795 Size: size, 3796 Remaining: size, 3797 TimeInForce: types.OrderTimeInForceGTC, 3798 }) 3799 } 3800 return orders 3801 } 3802 3803 func TestVwapEmptySide(t *testing.T) { 3804 ob := getTestOrderBook(t, "market1") 3805 _, err := ob.ob.VWAP(0, types.SideBuy) 3806 require.Error(t, err) 3807 _, err = ob.ob.VWAP(0, types.SideSell) 3808 require.Error(t, err) 3809 3810 _, err = ob.ob.VWAP(10, types.SideBuy) 3811 require.Error(t, err) 3812 _, err = ob.ob.VWAP(10, types.SideSell) 3813 require.Error(t, err) 3814 } 3815 3816 func TestVwapZeroVolume(t *testing.T) { 3817 ob := getTestOrderBook(t, "market1") 3818 3819 buyPrices := []uint64{ 3820 90, 3821 } 3822 sellPrices := []uint64{ 3823 100, 3824 } 3825 3826 orders := getTestOrders(t, "market1", 10, buyPrices, sellPrices) 3827 for _, o := range orders { 3828 _, err := ob.ob.SubmitOrder(o) 3829 assert.NoError(t, err) 3830 } 3831 3832 // when the volume passed is 0 3833 vwap, err := ob.ob.VWAP(0, types.SideBuy) 3834 require.NoError(t, err) 3835 require.Equal(t, "90", vwap.String()) 3836 vwap, err = ob.ob.VWAP(0, types.SideSell) 3837 require.NoError(t, err) 3838 require.Equal(t, "100", vwap.String()) 3839 } 3840 3841 func TestVwapNotEnoughVolume(t *testing.T) { 3842 ob := getTestOrderBook(t, "market1") 3843 3844 buyPrices := []uint64{ 3845 90, 3846 95, 3847 100, 3848 } 3849 sellPrices := []uint64{ 3850 200, 3851 210, 3852 220, 3853 } 3854 3855 orders := getTestOrders(t, "market1", 10, buyPrices, sellPrices) 3856 for _, o := range orders { 3857 _, err := ob.ob.SubmitOrder(o) 3858 assert.NoError(t, err) 3859 } 3860 3861 // there's 30 in the order book 3862 _, err := ob.ob.VWAP(40, types.SideBuy) 3863 require.Error(t, err) 3864 _, err = ob.ob.VWAP(40, types.SideSell) 3865 require.Error(t, err) 3866 } 3867 3868 func TestVWAP(t *testing.T) { 3869 ob := getTestOrderBook(t, "market1") 3870 3871 buyPrices := []uint64{ 3872 60, 3873 70, 3874 100, 3875 } 3876 sellPrices := []uint64{ 3877 200, 3878 210, 3879 220, 3880 } 3881 3882 orders := getTestOrders(t, "market1", 10, buyPrices, sellPrices) 3883 for _, o := range orders { 3884 _, err := ob.ob.SubmitOrder(o) 3885 assert.NoError(t, err) 3886 } 3887 3888 // Bid side 3889 // ========= 3890 vwap, err := ob.ob.VWAP(5, types.SideBuy) 3891 require.NoError(t, err) 3892 require.Equal(t, "100", vwap.String()) 3893 3894 vwap, err = ob.ob.VWAP(10, types.SideBuy) 3895 require.NoError(t, err) 3896 require.Equal(t, "100", vwap.String()) 3897 3898 // (100 * 10 + 70 * 5)/15 3899 vwap, err = ob.ob.VWAP(15, types.SideBuy) 3900 require.NoError(t, err) 3901 require.Equal(t, "90", vwap.String()) 3902 3903 // (100 + 70)/2 3904 vwap, err = ob.ob.VWAP(20, types.SideBuy) 3905 require.NoError(t, err) 3906 require.Equal(t, "85", vwap.String()) 3907 3908 // (100 * 10 + 70 * 10 + 60 * 5)/25 3909 vwap, err = ob.ob.VWAP(25, types.SideBuy) 3910 require.NoError(t, err) 3911 require.Equal(t, "80", vwap.String()) 3912 3913 // (100 + 70 + 60)/3 3914 vwap, err = ob.ob.VWAP(30, types.SideBuy) 3915 require.NoError(t, err) 3916 require.Equal(t, "76", vwap.String()) 3917 3918 // Ask side 3919 // ========= 3920 vwap, err = ob.ob.VWAP(5, types.SideSell) 3921 require.NoError(t, err) 3922 require.Equal(t, "200", vwap.String()) 3923 3924 vwap, err = ob.ob.VWAP(10, types.SideSell) 3925 require.NoError(t, err) 3926 require.Equal(t, "200", vwap.String()) 3927 3928 // (200 * 10 + 210 * 5)/15 3929 vwap, err = ob.ob.VWAP(15, types.SideSell) 3930 require.NoError(t, err) 3931 require.Equal(t, "203", vwap.String()) 3932 3933 // (200 + 210)/2 3934 vwap, err = ob.ob.VWAP(20, types.SideSell) 3935 require.NoError(t, err) 3936 require.Equal(t, "205", vwap.String()) 3937 3938 // (200 * 10 + 210 * 10 + 220 * 5)/25 3939 vwap, err = ob.ob.VWAP(25, types.SideSell) 3940 require.NoError(t, err) 3941 require.Equal(t, "208", vwap.String()) 3942 3943 // (200 + 210 + 220)/3 3944 vwap, err = ob.ob.VWAP(30, types.SideSell) 3945 require.NoError(t, err) 3946 require.Equal(t, "210", vwap.String()) 3947 }