code.vegaprotocol.io/vega@v0.79.0/core/matching/side_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 17 18 import ( 19 "testing" 20 21 "code.vegaprotocol.io/vega/core/types" 22 "code.vegaprotocol.io/vega/libs/num" 23 "code.vegaprotocol.io/vega/logging" 24 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func getTestSide(side types.Side) *OrderBookSide { 29 return &OrderBookSide{ 30 log: logging.NewTestLogger(), 31 levels: []*PriceLevel{}, 32 side: side, 33 } 34 } 35 36 func TestMemoryAllocationPriceLevelRemoveOrder(t *testing.T) { 37 side := getTestSide(types.SideSell) 38 o := &types.Order{ 39 ID: "order1", 40 MarketID: "testmarket", 41 Party: "A", 42 Side: types.SideSell, 43 Price: num.NewUint(100), 44 OriginalPrice: num.NewUint(100), 45 Size: 1, 46 Remaining: 1, 47 TimeInForce: types.OrderTimeInForceGTC, 48 } 49 // add the order to the side 50 side.addOrder(o) 51 assert.Len(t, side.levels, 1) 52 53 o2 := &types.Order{ 54 ID: "order2", 55 MarketID: "testmarket", 56 Party: "C", 57 Side: types.SideSell, 58 Price: num.NewUint(101), 59 OriginalPrice: num.NewUint(101), 60 Size: 1, 61 Remaining: 1, 62 TimeInForce: types.OrderTimeInForceGTC, 63 } 64 65 // add the order to the side 66 side.addOrder(o2) 67 assert.Len(t, side.levels, 2) 68 69 // remove it and check the length of the array 70 // remove second order 71 side.RemoveOrder(o2) 72 assert.Len(t, side.levels, 1) 73 } 74 75 func TestMemoryAllocationGetPriceLevelReturnAPriceLevelIfItAlreadyExists(t *testing.T) { 76 // test for a sell side 77 side := getTestSide(types.SideSell) 78 assert.Len(t, side.levels, 0) 79 pl := side.getPriceLevel(num.NewUint(100)) 80 assert.Len(t, side.levels, 1) 81 assert.NotNil(t, pl) 82 pl = side.getPriceLevel(num.NewUint(101)) 83 assert.Len(t, side.levels, 2) 84 assert.NotNil(t, pl) 85 pl = side.getPriceLevel(num.NewUint(102)) 86 assert.Len(t, side.levels, 3) 87 assert.NotNil(t, pl) 88 pl = side.getPriceLevel(num.NewUint(103)) 89 assert.Len(t, side.levels, 4) 90 assert.NotNil(t, pl) 91 pl = side.getPriceLevel(num.NewUint(104)) 92 assert.Len(t, side.levels, 5) 93 assert.NotNil(t, pl) 94 95 // get existing one in bounds now 96 pl = side.getPriceLevel(num.NewUint(102)) 97 assert.Len(t, side.levels, 5) 98 assert.NotNil(t, pl) 99 pl = side.getPriceLevel(num.NewUint(100)) 100 assert.Len(t, side.levels, 5) 101 assert.NotNil(t, pl) 102 pl = side.getPriceLevel(num.NewUint(104)) 103 assert.Len(t, side.levels, 5) 104 assert.NotNil(t, pl) 105 106 // test for a buy side 107 side = getTestSide(types.SideBuy) 108 assert.Len(t, side.levels, 0) 109 pl = side.getPriceLevel(num.NewUint(100)) 110 assert.Len(t, side.levels, 1) 111 assert.NotNil(t, pl) 112 pl = side.getPriceLevel(num.NewUint(101)) 113 assert.Len(t, side.levels, 2) 114 assert.NotNil(t, pl) 115 pl = side.getPriceLevel(num.NewUint(102)) 116 assert.Len(t, side.levels, 3) 117 assert.NotNil(t, pl) 118 pl = side.getPriceLevel(num.NewUint(103)) 119 assert.Len(t, side.levels, 4) 120 assert.NotNil(t, pl) 121 pl = side.getPriceLevel(num.NewUint(104)) 122 assert.Len(t, side.levels, 5) 123 assert.NotNil(t, pl) 124 125 // get existing one in bounds now 126 pl = side.getPriceLevel(num.NewUint(102)) 127 assert.Len(t, side.levels, 5) 128 assert.NotNil(t, pl) 129 pl = side.getPriceLevel(num.NewUint(100)) 130 assert.Len(t, side.levels, 5) 131 assert.NotNil(t, pl) 132 pl = side.getPriceLevel(num.NewUint(104)) 133 assert.Len(t, side.levels, 5) 134 assert.NotNil(t, pl) 135 } 136 137 func TestMemoryAllocationPriceLevelUncrossSide(t *testing.T) { 138 side := getTestSide(types.SideSell) 139 o := &types.Order{ 140 ID: "order1", 141 MarketID: "testmarket", 142 Party: "A", 143 Side: types.SideSell, 144 Price: num.NewUint(100), 145 OriginalPrice: num.NewUint(100), 146 Size: 1, 147 Remaining: 1, 148 TimeInForce: types.OrderTimeInForceGTC, 149 } 150 // add the order to the side 151 side.addOrder(o) 152 assert.Len(t, side.levels, 1) 153 154 o2 := &types.Order{ 155 ID: "order2", 156 MarketID: "testmarket", 157 Party: "C", 158 Side: types.SideSell, 159 Price: num.NewUint(101), 160 OriginalPrice: num.NewUint(101), 161 Size: 1, 162 Remaining: 1, 163 TimeInForce: types.OrderTimeInForceGTC, 164 } 165 166 // add the order to the side 167 side.addOrder(o2) 168 assert.Len(t, side.levels, 2) 169 170 aggressiveOrder := &types.Order{ 171 ID: "order3", 172 MarketID: "testmarket", 173 Party: "X", 174 Side: types.SideBuy, 175 Price: num.NewUint(100), 176 OriginalPrice: num.NewUint(100), 177 Size: 1, 178 Remaining: 1, 179 TimeInForce: types.OrderTimeInForceGTC, 180 } 181 side.uncross(aggressiveOrder, true) 182 assert.Len(t, side.levels, 1) 183 } 184 185 func getPopulatedTestSide(side types.Side) *OrderBookSide { 186 obs := getTestSide(side) 187 188 type testOrder struct { 189 ID string 190 Price uint64 191 Size uint64 192 } 193 194 testOrders := []testOrder{ 195 {"Order01", 100, 1}, 196 {"Order02", 100, 1}, 197 {"Order03", 100, 1}, 198 {"Order04", 101, 1}, 199 {"Order05", 101, 1}, 200 {"Order06", 101, 1}, 201 } 202 203 for _, order := range testOrders { 204 o := &types.Order{ 205 ID: order.ID, 206 MarketID: "testmarket", 207 Party: "A", 208 Side: side, 209 Price: num.NewUint(order.Price), 210 OriginalPrice: num.NewUint(order.Price), 211 Size: order.Size, 212 Remaining: order.Size, 213 TimeInForce: types.OrderTimeInForceGTC, 214 } 215 // add the order to the side 216 obs.addOrder(o) 217 } 218 return obs 219 } 220 221 func getPopulatedTestSideWithPegs(side types.Side) *OrderBookSide { 222 obs := getTestSide(side) 223 224 type testOrder struct { 225 ID string 226 Price uint64 227 Size uint64 228 Offset uint64 229 } 230 231 testOrders := []testOrder{ 232 {"Order01", 100, 1, 5}, 233 {"Order02", 101, 1, 0}, 234 {"Order03", 102, 1, 0}, 235 {"Order04", 103, 1, 8}, 236 {"Order05", 104, 1, 0}, 237 {"Order06", 105, 1, 0}, 238 } 239 240 for _, order := range testOrders { 241 o := &types.Order{ 242 ID: order.ID, 243 MarketID: "testmarket", 244 Party: "A", 245 Side: side, 246 Price: num.NewUint(order.Price), 247 OriginalPrice: num.NewUint(order.Price), 248 Size: order.Size, 249 Remaining: order.Size, 250 TimeInForce: types.OrderTimeInForceGTC, 251 } 252 if order.Offset != 0 { 253 o.PeggedOrder = &types.PeggedOrder{ 254 Reference: types.PeggedReferenceMid, 255 Offset: num.NewUint(order.Offset), 256 } 257 } 258 // add the order to the side 259 obs.addOrder(o) 260 } 261 return obs 262 } 263 264 func getPopulatedTestSideWithOnlyPegs(side types.Side) *OrderBookSide { 265 obs := getTestSide(side) 266 267 type testOrder struct { 268 ID string 269 Price uint64 270 Size uint64 271 Offset uint64 272 } 273 274 testOrders := []testOrder{ 275 {"Order01", 100, 1, 5}, 276 {"Order02", 101, 1, 6}, 277 {"Order03", 102, 1, 7}, 278 {"Order04", 103, 1, 8}, 279 } 280 281 for _, order := range testOrders { 282 o := &types.Order{ 283 ID: order.ID, 284 MarketID: "testmarket", 285 Party: "A", 286 Side: side, 287 Price: num.NewUint(order.Price), 288 OriginalPrice: num.NewUint(order.Price), 289 Size: order.Size, 290 Remaining: order.Size, 291 TimeInForce: types.OrderTimeInForceGTC, 292 PeggedOrder: &types.PeggedOrder{ 293 Reference: types.PeggedReferenceMid, 294 Offset: num.NewUint(order.Offset), 295 }, 296 } 297 // add the order to the side 298 obs.addOrder(o) 299 } 300 return obs 301 } 302 303 func getEmptyTestSide() *OrderBookSide { 304 return getTestSide(types.SideSell) 305 } 306 307 func TestExtractOrdersFullLevel(t *testing.T) { 308 side := getPopulatedTestSide(types.SideSell) 309 310 assert.Len(t, side.levels, 2) 311 312 orders := side.ExtractOrders(num.NewUint(100), 3, true) 313 assert.Len(t, side.levels, 1) 314 assert.Len(t, orders, 3) 315 assert.EqualValues(t, 3, side.getOrderCount()) 316 } 317 318 func TestExtractOrdersPartialLevel(t *testing.T) { 319 side := getPopulatedTestSide(types.SideSell) 320 321 assert.Len(t, side.levels, 2) 322 323 orders := side.ExtractOrders(num.NewUint(100), 2, true) 324 assert.Len(t, side.levels, 2) 325 assert.Len(t, orders, 2) 326 assert.EqualValues(t, 4, side.getOrderCount()) 327 } 328 329 func TestExtractOrdersCrossLevel(t *testing.T) { 330 side := getPopulatedTestSide(types.SideSell) 331 332 assert.Len(t, side.levels, 2) 333 334 orders := side.ExtractOrders(num.NewUint(101), 5, true) 335 assert.Len(t, side.levels, 1) 336 assert.Len(t, orders, 5) 337 assert.EqualValues(t, 1, side.getOrderCount()) 338 } 339 340 func TestExtractOrdersWrongVolume(t *testing.T) { 341 // Attempt to extract more volume than we have on the book 342 side := getPopulatedTestSide(types.SideSell) 343 assert.Panics(t, func() { side.ExtractOrders(num.NewUint(101), 30, true) }) 344 345 // Attempt to extract more than we have at this price level 346 side = getPopulatedTestSide(types.SideSell) 347 assert.Panics(t, func() { side.ExtractOrders(num.NewUint(100), 4, true) }) 348 } 349 350 func TestExtractOrdersZeroVolume(t *testing.T) { 351 // Attempt to extract 0 volume of orders 352 side := getPopulatedTestSide(types.SideSell) 353 assert.Len(t, side.ExtractOrders(num.NewUint(101), 0, true), 0) 354 } 355 356 func TestBestStatic(t *testing.T) { 357 // Empty book 358 emptySide := getEmptyTestSide() 359 _, err := emptySide.BestStaticPrice() 360 assert.Error(t, err) 361 362 _, _, err = emptySide.BestStaticPriceAndVolume() 363 assert.Error(t, err) 364 365 // Book with normal and pegs 366 side := getPopulatedTestSideWithPegs(types.SideSell) 367 368 price, err := side.BestStaticPrice() 369 assert.NoError(t, err) 370 assert.EqualValues(t, 101, int(price.Uint64())) 371 372 price, volume, err := side.BestStaticPriceAndVolume() 373 assert.NoError(t, err) 374 assert.EqualValues(t, 101, int(price.Uint64())) 375 assert.EqualValues(t, 1, volume) 376 377 // Book with only pegs 378 pegsSide := getPopulatedTestSideWithOnlyPegs(types.SideSell) 379 _, err = pegsSide.BestStaticPrice() 380 assert.Error(t, err) 381 382 _, _, err = pegsSide.BestStaticPriceAndVolume() 383 assert.Error(t, err) 384 } 385 386 func TestGetPriceLevelIfExists(t *testing.T) { 387 buySide := getPopulatedTestSideWithPegs(types.SideBuy) 388 sellSide := getPopulatedTestSideWithPegs(types.SideSell) 389 390 // Check we can get valid price levels 391 bpl := buySide.getPriceLevelIfExists(num.NewUint(100)) 392 assert.NotNil(t, bpl) 393 spl := sellSide.getPriceLevelIfExists(num.NewUint(100)) 394 assert.NotNil(t, spl) 395 396 // Now try to get a level that does not exist 397 bpl = buySide.getPriceLevelIfExists(num.NewUint(200)) 398 assert.Nil(t, bpl) 399 spl = sellSide.getPriceLevelIfExists(num.NewUint(200)) 400 assert.Nil(t, spl) 401 } 402 403 func TestGetVolume(t *testing.T) { 404 buySide := getPopulatedTestSideWithPegs(types.SideBuy) 405 sellSide := getPopulatedTestSideWithPegs(types.SideSell) 406 407 // Actual levels 408 volume, err := buySide.GetVolume(num.NewUint(101)) 409 assert.NoError(t, err) 410 assert.EqualValues(t, 1, volume) 411 412 volume, err = sellSide.GetVolume(num.NewUint(101)) 413 assert.NoError(t, err) 414 assert.EqualValues(t, 1, volume) 415 416 // Invalid levels 417 volume, err = buySide.GetVolume(num.NewUint(200)) 418 assert.Error(t, err) 419 assert.EqualValues(t, 0, volume) 420 421 volume, err = sellSide.GetVolume(num.NewUint(200)) 422 assert.Error(t, err) 423 assert.EqualValues(t, 0, volume) 424 425 // Check total volumes 426 totBuyVol := buySide.getTotalVolume() 427 assert.EqualValues(t, 6, totBuyVol) 428 429 totSellVol := buySide.getTotalVolume() 430 assert.EqualValues(t, 6, totSellVol) 431 } 432 433 func TestFakeUncrossNormal(t *testing.T) { 434 buySide := getPopulatedTestSideWithPegs(types.SideBuy) 435 436 order := types.Order{ 437 ID: "Id", 438 Price: num.UintZero(), 439 OriginalPrice: num.UintZero(), 440 Side: types.SideSell, 441 Size: 5, 442 Remaining: 5, 443 TimeInForce: types.OrderTimeInForceFOK, 444 Type: types.OrderTypeMarket, 445 } 446 447 checkWashTrades := false 448 fakeTrades, err := buySide.fakeUncross(&order, checkWashTrades) 449 assert.Len(t, fakeTrades, 5) 450 assert.NoError(t, err) 451 452 trades, _, _, err := buySide.uncross(&order, checkWashTrades) 453 assert.Len(t, trades, 5) 454 assert.NoError(t, err) 455 456 for i := 0; i < len(trades); i++ { 457 assert.Equal(t, trades[i], fakeTrades[i]) 458 } 459 } 460 461 func TestFakeUncrossSelfTradeFOKMarketOrder(t *testing.T) { 462 buySide := getPopulatedTestSideWithPegs(types.SideBuy) 463 464 order := types.Order{ 465 ID: "Id", 466 Party: "A", 467 Price: num.UintZero(), 468 OriginalPrice: num.UintZero(), 469 Side: types.SideSell, 470 Size: 5, 471 Remaining: 5, 472 TimeInForce: types.OrderTimeInForceFOK, 473 Type: types.OrderTypeMarket, 474 } 475 476 checkWashTrades := false 477 fakeTrades, err1 := buySide.fakeUncross(&order, checkWashTrades) 478 assert.Len(t, fakeTrades, 0) 479 assert.Error(t, err1) 480 481 trades, _, _, err2 := buySide.uncross(&order, checkWashTrades) 482 assert.Len(t, trades, 0) 483 assert.Error(t, err2) 484 485 assert.Equal(t, err1, err2) 486 } 487 488 func TestFakeUncrossSelfTradeNonFOKLimitOrder_DontCheckWashTrades(t *testing.T) { 489 buySide := getPopulatedTestSideWithPegs(types.SideBuy) 490 491 order := types.Order{ 492 ID: "Id", 493 Party: "A", 494 Price: num.NewUint(105), 495 OriginalPrice: num.NewUint(105), 496 Side: types.SideSell, 497 Size: 5, 498 Remaining: 5, 499 TimeInForce: types.OrderTimeInForceGTC, 500 Type: types.OrderTypeLimit, 501 } 502 503 checkWashTrades := false 504 fakeTrades, err := buySide.fakeUncross(&order, checkWashTrades) 505 assert.Len(t, fakeTrades, 1) 506 assert.NoError(t, err) 507 assert.Equal(t, fakeTrades[0].SellOrder, order.ID) 508 509 trades, _, _, err := buySide.uncross(&order, checkWashTrades) 510 assert.Len(t, trades, 1) 511 assert.NoError(t, err) 512 513 assert.Equal(t, trades[0], fakeTrades[0]) 514 } 515 516 func TestFakeUncrossSelfTradeNonFOKLimitOrder_CheckWashTrades(t *testing.T) { 517 buySide := getPopulatedTestSideWithPegs(types.SideBuy) 518 519 order := types.Order{ 520 ID: "Id", 521 Party: "A", 522 Price: num.NewUint(105), 523 OriginalPrice: num.NewUint(105), 524 Side: types.SideSell, 525 Size: 5, 526 Remaining: 5, 527 TimeInForce: types.OrderTimeInForceGTC, 528 Type: types.OrderTypeLimit, 529 } 530 531 checkWashTrades := true 532 fakeTrades, err1 := buySide.fakeUncross(&order, checkWashTrades) 533 assert.Len(t, fakeTrades, 0) 534 assert.Error(t, err1) 535 assert.Equal(t, "party attempted to submit wash trade", err1.Error()) 536 537 trades, _, _, err2 := buySide.uncross(&order, checkWashTrades) 538 assert.Len(t, trades, 0) 539 assert.Error(t, err2) 540 assert.Equal(t, "party attempted to submit wash trade", err2.Error()) 541 assert.Equal(t, err1.Error(), err2.Error()) 542 } 543 544 func TestFakeUncrossNotEnoughVolume(t *testing.T) { 545 buySide := getPopulatedTestSideWithPegs(types.SideBuy) 546 547 order := types.Order{ 548 ID: "Id", 549 Price: num.UintZero(), 550 OriginalPrice: num.UintZero(), 551 Side: types.SideSell, 552 Size: 7, 553 Remaining: 7, 554 TimeInForce: types.OrderTimeInForceFOK, 555 Type: types.OrderTypeMarket, 556 } 557 558 checkWashTrades := false 559 fakeTrades, err := buySide.fakeUncross(&order, checkWashTrades) 560 assert.Len(t, fakeTrades, 0) 561 assert.NoError(t, err) 562 563 trades, _, _, err := buySide.uncross(&order, checkWashTrades) 564 assert.Len(t, trades, 0) 565 assert.NoError(t, err) 566 } 567 568 func TestFakeUncrossAuction(t *testing.T) { 569 buySide := getPopulatedTestSide(types.SideBuy) 570 571 order1 := &types.Order{ 572 ID: "Id", 573 Party: "A", 574 Price: num.NewUint(99), 575 OriginalPrice: num.NewUint(99), 576 Side: types.SideSell, 577 Size: 3, 578 Remaining: 3, 579 TimeInForce: types.OrderTimeInForceGTC, 580 Type: types.OrderTypeLimit, 581 } 582 583 order2 := &types.Order{ 584 ID: "Id", 585 Party: "B", 586 Price: num.NewUint(99), 587 OriginalPrice: num.NewUint(99), 588 Side: types.SideSell, 589 Size: 3, 590 Remaining: 3, 591 TimeInForce: types.OrderTimeInForceGTC, 592 Type: types.OrderTypeLimit, 593 } 594 595 orders := []*types.Order{order1, order2} 596 597 fakeTrades, err := buySide.fakeUncrossAuction(orders) 598 assert.Len(t, fakeTrades, 6) 599 assert.NoError(t, err) 600 601 trades := []*types.Trade{} 602 for _, order := range orders { 603 trds, _, _, err := buySide.uncross(order, false) 604 assert.NoError(t, err) 605 trades = append(trades, trds...) 606 } 607 assert.Len(t, trades, 6) 608 assert.NoError(t, err) 609 610 for i, tr := range trades { 611 assert.Equal(t, tr, fakeTrades[i]) 612 } 613 }