code.vegaprotocol.io/vega@v0.79.0/core/positions/engine_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 positions_test 17 18 import ( 19 "context" 20 "encoding/hex" 21 "math" 22 "testing" 23 "time" 24 25 "code.vegaprotocol.io/vega/core/events" 26 "code.vegaprotocol.io/vega/core/integration/stubs" 27 "code.vegaprotocol.io/vega/core/positions" 28 "code.vegaprotocol.io/vega/core/types" 29 "code.vegaprotocol.io/vega/libs/num" 30 "code.vegaprotocol.io/vega/logging" 31 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 ) 35 36 func TestUpdatePosition(t *testing.T) { 37 t.Run("Update position regular", testUpdatePositionRegular) 38 } 39 40 func TestRegisterLargeOrder(t *testing.T) { 41 const ( 42 buysize int64 = 123 43 sellsize int64 = 456 44 ) 45 ctx := context.Background() 46 e := getTestEngine(t) 47 orderBuy := types.Order{ 48 Party: "test_party", 49 Side: types.SideBuy, 50 Size: uint64(buysize), 51 Remaining: uint64(buysize), 52 Price: num.UintZero(), 53 } 54 55 assert.NoError(t, e.ValidateOrder(&orderBuy)) 56 _ = e.RegisterOrder(ctx, &orderBuy) 57 58 // now say we're going to do another MASSIVE one 59 orderBuy2 := types.Order{ 60 Party: "test_party", 61 Side: types.SideBuy, 62 Size: math.MaxInt64, 63 Remaining: math.MaxInt64, 64 Price: num.UintZero(), 65 } 66 assert.Error(t, e.ValidateOrder(&orderBuy2)) // should fail because if we registered it'll overflow 67 require.Panics(t, func() { 68 e.RegisterOrder(ctx, &orderBuy2) // should panic and not silently overflow 69 }) 70 } 71 72 func TestAmendLargeOrder(t *testing.T) { 73 const ( 74 buysize int64 = 123 75 sellsize int64 = 456 76 ) 77 ctx := context.Background() 78 e := getTestEngine(t) 79 orderBuy := types.Order{ 80 Party: "test_party", 81 Side: types.SideBuy, 82 Size: uint64(buysize), 83 Remaining: uint64(buysize), 84 Price: num.UintZero(), 85 } 86 87 assert.NoError(t, e.ValidateOrder(&orderBuy)) 88 _ = e.RegisterOrder(ctx, &orderBuy) 89 90 // now say we're going to do an amend to a massive one 91 orderBuy2 := types.Order{ 92 Party: "test_party", 93 Side: types.SideBuy, 94 Size: math.MaxUint64, 95 Remaining: math.MaxUint64, 96 Price: num.UintZero(), 97 } 98 assert.Error(t, e.ValidateAmendOrder(&orderBuy, &orderBuy2)) // should fail because if we registered it'll overflow 99 require.Panics(t, func() { 100 e.RegisterOrder(ctx, &orderBuy2) // should panic and not silently overflow 101 }) 102 } 103 104 func TestGetOpenInterest(t *testing.T) { 105 engine := getTestEngine(t) 106 assert.Empty(t, engine.Positions()) 107 var ( 108 buyer = "buyer_id" 109 buyer2 = "buyer_id2" 110 seller = "seller_id" 111 size uint64 = 10 112 price = num.NewUint(10000) 113 ) 114 passive1 := &types.Order{ 115 Party: buyer, 116 Remaining: size, 117 Price: price, 118 Side: types.SideBuy, 119 } 120 passive2 := &types.Order{ 121 Party: buyer2, 122 Remaining: size, 123 Price: price, 124 Side: types.SideBuy, 125 } 126 aggressive := &types.Order{ 127 Party: seller, 128 Remaining: size * 2, 129 Price: price, 130 Side: types.SideSell, 131 } 132 engine.RegisterOrder(context.TODO(), passive1) 133 engine.RegisterOrder(context.TODO(), passive2) 134 engine.RegisterOrder(context.TODO(), aggressive) 135 136 trade := types.Trade{ 137 Type: types.TradeTypeDefault, 138 ID: "trade_id", 139 MarketID: "market_id", 140 Price: num.NewUint(10000), 141 Size: size, 142 Buyer: buyer, 143 Seller: seller, 144 BuyOrder: "buy_order_id", 145 SellOrder: "sell_order_id", 146 Timestamp: time.Now().Unix(), 147 } 148 _ = engine.Update(context.Background(), &trade, passive1, aggressive) 149 trade = types.Trade{ 150 Type: types.TradeTypeDefault, 151 ID: "trade_id", 152 MarketID: "market_id", 153 Price: num.NewUint(10000), 154 Size: size, 155 Buyer: buyer2, 156 Seller: seller, 157 BuyOrder: "buy_order_id", 158 SellOrder: "sell_order_id", 159 Timestamp: time.Now().Unix(), 160 } 161 _ = engine.Update(context.Background(), &trade, passive2, aggressive) 162 // 3 positions 163 // 2 at + 10 164 // 1 at -20 165 // we should get an open interest of 20 166 openInterest := engine.GetOpenInterest() 167 assert.Equal(t, 20, int(openInterest)) 168 } 169 170 func testUpdatePositionRegular(t *testing.T) { 171 engine := getTestEngine(t) 172 assert.Empty(t, engine.Positions()) 173 var ( 174 buyer = "buyer_id" 175 seller = "seller_id" 176 size uint64 = 10 177 price = num.NewUint(10000) 178 ) 179 passive := &types.Order{ 180 Party: buyer, 181 Remaining: size, 182 Price: price, 183 Side: types.SideBuy, 184 } 185 aggressive := &types.Order{ 186 Party: seller, 187 Remaining: size, 188 Price: price, 189 Side: types.SideSell, 190 } 191 engine.RegisterOrder(context.TODO(), passive) 192 engine.RegisterOrder(context.TODO(), aggressive) 193 194 trade := types.Trade{ 195 Type: types.TradeTypeDefault, 196 ID: "trade_id", 197 MarketID: "market_id", 198 Price: price, 199 Size: size, 200 Buyer: buyer, 201 Seller: seller, 202 BuyOrder: "buy_order_id", 203 SellOrder: "sell_order_id", 204 Timestamp: time.Now().Unix(), 205 } 206 positions := engine.Update(context.Background(), &trade, passive, aggressive) 207 pos := engine.Positions() 208 assert.Equal(t, 2, len(pos)) 209 assert.Equal(t, 2, len(positions)) 210 for _, p := range pos { 211 if p.Party() == buyer { 212 assert.Equal(t, int64(size), p.Size()) 213 assert.Equal(t, num.UintZero(), p.VWBuy()) 214 } else { 215 assert.Equal(t, int64(-size), p.Size()) 216 assert.Equal(t, num.UintZero(), p.VWSell()) 217 } 218 } 219 } 220 221 func TestRemoveDistressedEmpty(t *testing.T) { 222 data := []events.MarketPosition{ 223 mp{ 224 party: "test", 225 size: 1, 226 price: num.NewUint(1000), 227 }, 228 } 229 e := getTestEngine(t) 230 ret := e.RemoveDistressed(data) 231 assert.Empty(t, ret) 232 } 233 234 func TestRegisterUnregisterOrder(t *testing.T) { 235 t.Run("Test successful order register", testRegisterOrderSuccessful) 236 t.Run("Test successful order unregister", testUnregisterOrderSuccessful) 237 t.Run("Test unsuccessful order unregister", testUnregisterOrderUnsuccessful) 238 } 239 240 func testRegisterOrderSuccessful(t *testing.T) { 241 const ( 242 buysize int64 = 123 243 sellsize int64 = 456 244 ) 245 e := getTestEngine(t) 246 orderBuy := types.Order{ 247 Party: "test_party", 248 Side: types.SideBuy, 249 Size: uint64(buysize), 250 Remaining: uint64(buysize), 251 Price: num.UintZero(), 252 } 253 pos := e.RegisterOrder(context.TODO(), &orderBuy) 254 assert.Equal(t, buysize, pos.Buy()) 255 assert.Zero(t, pos.Sell()) 256 assert.True(t, pos.Price().IsZero()) 257 assert.Zero(t, pos.Size()) 258 positions := e.Positions() 259 assert.Equal(t, 1, len(positions)) 260 assert.Equal(t, pos.Buy(), positions[0].Buy()) 261 262 orderSell := types.Order{ 263 Party: "test_party", 264 Side: types.SideSell, 265 Size: uint64(sellsize), 266 Remaining: uint64(sellsize), 267 Price: num.UintZero(), 268 } 269 pos = e.RegisterOrder(context.TODO(), &orderSell) 270 assert.Equal(t, buysize, pos.Buy()) 271 assert.Equal(t, sellsize, pos.Sell()) 272 assert.True(t, pos.Price().IsZero()) 273 assert.Zero(t, pos.Size()) 274 positions = e.Positions() 275 assert.Equal(t, 1, len(positions)) 276 assert.Equal(t, pos.Buy(), positions[0].Buy()) 277 assert.Equal(t, pos.Sell(), positions[0].Sell()) 278 } 279 280 func testUnregisterOrderSuccessful(t *testing.T) { 281 const ( 282 buysize int64 = 123 283 sellsize int64 = 456 284 ) 285 e := getTestEngine(t) 286 orderBuy := types.Order{ 287 Party: "test_party", 288 Side: types.SideBuy, 289 Size: uint64(buysize), 290 Remaining: uint64(buysize), 291 Price: num.UintZero(), 292 } 293 pos := e.RegisterOrder(context.TODO(), &orderBuy) 294 assert.Equal(t, buysize, pos.Buy()) 295 296 pos = e.UnregisterOrder(context.TODO(), &orderBuy) 297 assert.Zero(t, pos.Buy()) 298 299 orderSell := types.Order{ 300 Party: "test_party", 301 Side: types.SideSell, 302 Size: uint64(sellsize), 303 Remaining: uint64(sellsize), 304 Price: num.UintZero(), 305 } 306 pos = e.RegisterOrder(context.TODO(), &orderSell) 307 assert.Zero(t, pos.Buy()) 308 assert.Equal(t, sellsize, pos.Sell()) 309 310 pos = e.UnregisterOrder(context.TODO(), &orderSell) 311 assert.Zero(t, pos.Buy()) 312 assert.Zero(t, pos.Sell()) 313 } 314 315 func testUnregisterOrderUnsuccessful(t *testing.T) { 316 e := getTestEngine(t) 317 orderBuy := types.Order{ 318 Party: "test_party", 319 Side: types.SideBuy, 320 Size: uint64(999), 321 Remaining: uint64(999), 322 Price: num.UintZero(), 323 } 324 require.Panics(t, func() { 325 _ = e.UnregisterOrder(context.TODO(), &orderBuy) 326 }) 327 } 328 329 func getTestEngine(t *testing.T) *positions.SnapshotEngine { 330 t.Helper() 331 broker := stubs.NewBrokerStub() 332 333 return positions.NewSnapshotEngine( 334 logging.NewTestLogger(), positions.NewDefaultConfig(), 335 "test_market", 336 broker, 337 ) 338 } 339 340 func TestGetOpenInterestGivenTrades(t *testing.T) { 341 // A, B represents partys who already have positions 342 // C, D represents partys who don't have positions (but there are entries in "trades" array that contain their trades) 343 344 cases := []struct { 345 ExistingPositions []*types.Trade 346 Trades []*types.Trade 347 ExpectedOI uint64 348 }{ 349 // Both parties already have positions 350 { // A: + 100, B: -100 => OI: 100 351 ExistingPositions: []*types.Trade{ 352 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 353 }, 354 ExpectedOI: 100, 355 }, 356 { // A: + 100 - 10, B: -100 + 10=> OI: 90 357 ExistingPositions: []*types.Trade{ 358 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 359 }, 360 Trades: []*types.Trade{ 361 {Seller: "A", Buyer: "B", Size: 10, Price: num.UintZero()}, 362 }, 363 ExpectedOI: 90, 364 }, 365 { 366 // A: + 100 + 10, B: -100 - 10 => OI: 110 367 ExistingPositions: []*types.Trade{ 368 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 369 }, 370 Trades: []*types.Trade{ 371 {Seller: "B", Buyer: "A", Size: 10, Price: num.UintZero()}, 372 }, 373 ExpectedOI: 110, 374 }, 375 { 376 // Same as above + wash trade -> should leave OI unchanged 377 ExistingPositions: []*types.Trade{ 378 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 379 }, 380 Trades: []*types.Trade{ 381 {Seller: "B", Buyer: "A", Size: 10, Price: num.UintZero()}, 382 {Seller: "A", Buyer: "A", Size: 13, Price: num.UintZero()}, 383 }, 384 ExpectedOI: 110, 385 }, 386 { 387 // Same as above + wash trade -> should leave OI unchanged 388 ExistingPositions: []*types.Trade{}, 389 Trades: []*types.Trade{ 390 {Seller: "A", Buyer: "B", Size: 20, Price: num.UintZero()}, 391 {Seller: "A", Buyer: "C", Size: 30, Price: num.UintZero()}, 392 {Seller: "D", Buyer: "D", Size: 40, Price: num.UintZero()}, 393 }, 394 ExpectedOI: 50, 395 }, 396 // There at least 1 new party 397 { 398 // A: + 100 + 10, B: -100, C: -10 => OI: 110 399 ExistingPositions: []*types.Trade{ 400 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 401 }, 402 Trades: []*types.Trade{ 403 {Seller: "C", Buyer: "A", Size: 10, Price: num.UintZero()}, 404 }, 405 ExpectedOI: 110, 406 }, 407 { 408 // A: + 100 - 10, B: -100, C: +10 => OI: 100 409 ExistingPositions: []*types.Trade{ 410 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 411 }, 412 Trades: []*types.Trade{ 413 {Seller: "A", Buyer: "C", Size: 10, Price: num.UintZero()}, 414 }, 415 ExpectedOI: 100, 416 }, 417 // None of the parties have positions yet 418 { 419 // C: +10, D:-10 => OI: 10 420 Trades: []*types.Trade{ 421 {Seller: "D", Buyer: "C", Size: 10, Price: num.UintZero()}, 422 }, 423 ExpectedOI: 10, 424 }, 425 { 426 ExistingPositions: []*types.Trade{ 427 {Seller: "C", Buyer: "A", Size: 100, Price: num.UintZero()}, 428 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 429 }, 430 Trades: []*types.Trade{ 431 {Seller: "B", Buyer: "A", Size: 5, Price: num.UintZero()}, 432 }, 433 ExpectedOI: 200, 434 }, 435 { 436 ExistingPositions: []*types.Trade{ 437 {Seller: "C", Buyer: "A", Size: 100, Price: num.UintZero()}, 438 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 439 }, 440 Trades: []*types.Trade{ 441 {Seller: "A", Buyer: "B", Size: 5, Price: num.UintZero()}, 442 }, 443 ExpectedOI: 200, 444 }, 445 { 446 ExistingPositions: []*types.Trade{ 447 {Seller: "C", Buyer: "A", Size: 100, Price: num.UintZero()}, 448 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 449 }, 450 Trades: []*types.Trade{ 451 {Seller: "C", Buyer: "B", Size: 5, Price: num.UintZero()}, 452 }, 453 ExpectedOI: 205, 454 }, 455 { 456 ExistingPositions: []*types.Trade{ 457 {Seller: "C", Buyer: "A", Size: 100, Price: num.UintZero()}, 458 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 459 }, 460 Trades: []*types.Trade{ 461 {Seller: "B", Buyer: "C", Size: 5, Price: num.UintZero()}, 462 }, 463 ExpectedOI: 195, 464 }, 465 { 466 ExistingPositions: []*types.Trade{ 467 {Seller: "C", Buyer: "A", Size: 100, Price: num.UintZero()}, 468 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 469 }, 470 Trades: []*types.Trade{ 471 {Seller: "D", Buyer: "C", Size: 500, Price: num.UintZero()}, 472 }, 473 ExpectedOI: 500, 474 }, 475 { 476 ExistingPositions: []*types.Trade{ 477 {Seller: "C", Buyer: "A", Size: 100, Price: num.UintZero()}, 478 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 479 }, 480 Trades: []*types.Trade{ 481 {Seller: "D", Buyer: "C", Size: 5, Price: num.UintZero()}, 482 }, 483 ExpectedOI: 200, 484 }, 485 { 486 ExistingPositions: []*types.Trade{ 487 {Seller: "C", Buyer: "A", Size: 10, Price: num.UintZero()}, 488 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 489 }, 490 Trades: []*types.Trade{ 491 {Seller: "A", Buyer: "B", Size: 5, Price: num.UintZero()}, 492 }, 493 ExpectedOI: 110, 494 }, 495 { 496 ExistingPositions: []*types.Trade{ 497 {Seller: "C", Buyer: "A", Size: 10, Price: num.UintZero()}, 498 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 499 }, 500 Trades: []*types.Trade{ 501 {Seller: "B", Buyer: "A", Size: 5, Price: num.UintZero()}, 502 }, 503 ExpectedOI: 110, 504 }, 505 { 506 ExistingPositions: []*types.Trade{ 507 {Seller: "C", Buyer: "A", Size: 10, Price: num.UintZero()}, 508 {Seller: "C", Buyer: "B", Size: 100, Price: num.UintZero()}, 509 }, 510 Trades: []*types.Trade{ 511 {Seller: "A", Buyer: "B", Size: 200, Price: num.UintZero()}, 512 }, 513 ExpectedOI: 300, 514 }, 515 { 516 ExistingPositions: []*types.Trade{ 517 {Seller: "A", Buyer: "C", Size: 100, Price: num.UintZero()}, 518 {Seller: "C", Buyer: "B", Size: 300, Price: num.UintZero()}, 519 }, 520 Trades: []*types.Trade{ 521 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 522 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 523 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 524 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 525 }, 526 ExpectedOI: 300, 527 }, 528 { 529 ExistingPositions: []*types.Trade{ 530 {Seller: "A", Buyer: "C", Size: 100, Price: num.UintZero()}, 531 {Seller: "C", Buyer: "B", Size: 300, Price: num.UintZero()}, 532 }, 533 Trades: []*types.Trade{ 534 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 535 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 536 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 537 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 538 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 539 }, 540 ExpectedOI: 400, 541 }, 542 { 543 ExistingPositions: []*types.Trade{ 544 {Seller: "A", Buyer: "C", Size: 100, Price: num.UintZero()}, 545 {Seller: "C", Buyer: "B", Size: 300, Price: num.UintZero()}, 546 }, 547 Trades: []*types.Trade{ 548 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 549 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 550 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 551 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 552 {Seller: "A", Buyer: "B", Size: 100, Price: num.UintZero()}, 553 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 554 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 555 }, 556 ExpectedOI: 400, 557 }, 558 { 559 ExistingPositions: []*types.Trade{ 560 {Seller: "A", Buyer: "C", Size: 100, Price: num.UintZero()}, 561 {Seller: "C", Buyer: "B", Size: 300, Price: num.UintZero()}, 562 }, 563 Trades: []*types.Trade{ 564 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 565 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 566 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 567 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 568 {Seller: "A", Buyer: "C", Size: 100, Price: num.UintZero()}, 569 {Seller: "D", Buyer: "A", Size: 100, Price: num.UintZero()}, 570 {Seller: "B", Buyer: "A", Size: 100, Price: num.UintZero()}, 571 }, 572 ExpectedOI: 400, 573 }, 574 } 575 576 for _, tc := range cases { 577 e := getTestEngine(t) 578 579 for _, tr := range tc.ExistingPositions { 580 passive := registerOrder(e, types.SideBuy, tr.Buyer, tr.Price, tr.Size) 581 aggressive := registerOrder(e, types.SideSell, tr.Seller, tr.Price, tr.Size) 582 e.Update(context.Background(), tr, passive, aggressive) 583 } 584 585 oiGivenTrades := e.GetOpenInterestGivenTrades(tc.Trades) 586 587 for _, tr := range tc.Trades { 588 passive := registerOrder(e, types.SideBuy, tr.Buyer, tr.Price, tr.Size) 589 aggressive := registerOrder(e, types.SideSell, tr.Seller, tr.Price, tr.Size) 590 e.Update(context.Background(), tr, passive, aggressive) 591 } 592 593 // Now check it matches once those trades are registered as positions 594 oiAfterUpdatingPositions := e.GetOpenInterest() 595 t.Run("", func(t *testing.T) { 596 require.Equal(t, tc.ExpectedOI, oiGivenTrades) 597 require.Equal(t, tc.ExpectedOI, oiAfterUpdatingPositions) 598 }) 599 } 600 } 601 602 type mp struct { 603 size, buy, sell int64 604 party string 605 price *num.Uint 606 } 607 608 func (m mp) AverageEntryPrice() *num.Uint { 609 return num.UintZero() 610 } 611 612 func (m mp) Party() string { 613 return m.party 614 } 615 616 func (m mp) Size() int64 { 617 return m.size 618 } 619 620 func (m mp) Buy() int64 { 621 return m.buy 622 } 623 624 func (m mp) Sell() int64 { 625 return m.sell 626 } 627 628 func (m mp) Price() *num.Uint { 629 return m.price 630 } 631 632 func (m mp) ClearPotentials() {} 633 634 func (m mp) BuySumProduct() *num.Uint { 635 return num.UintZero() 636 } 637 638 func (m mp) SellSumProduct() *num.Uint { 639 return num.UintZero() 640 } 641 642 func (m mp) VWBuy() *num.Uint { 643 return num.UintZero() 644 } 645 646 func (m mp) VWSell() *num.Uint { 647 return num.UintZero() 648 } 649 650 func TestHash(t *testing.T) { 651 e := getTestEngine(t) 652 orders := []*types.Order{ 653 { 654 Party: "test_party_1", 655 Side: types.SideBuy, 656 Size: uint64(100), 657 Remaining: uint64(100), 658 Price: num.UintZero(), 659 }, 660 { 661 Party: "test_party_2", 662 Side: types.SideBuy, 663 Size: uint64(200), 664 Remaining: uint64(200), 665 Price: num.UintZero(), 666 }, 667 } 668 669 matchingPrice := num.NewUint(10000) 670 tradeSize := uint64(15) 671 passiveOrder := &types.Order{ 672 ID: "buy_order_id", 673 Party: "test_party_3", 674 Side: types.SideBuy, 675 Size: tradeSize, 676 Remaining: tradeSize, 677 Price: matchingPrice, 678 } 679 680 aggresiveOrder := &types.Order{ 681 ID: "sell_order_id", 682 Party: "test_party_1", 683 Side: types.SideSell, 684 Size: tradeSize, 685 Remaining: tradeSize, 686 Price: matchingPrice, 687 } 688 689 orders = append(orders, passiveOrder, aggresiveOrder) 690 691 for _, order := range orders { 692 e.RegisterOrder(context.TODO(), order) 693 } 694 695 trade := types.Trade{ 696 Type: types.TradeTypeDefault, 697 ID: "trade_id", 698 MarketID: "market_id", 699 Price: matchingPrice, 700 Size: tradeSize, 701 Buyer: passiveOrder.Party, 702 Seller: aggresiveOrder.Party, 703 BuyOrder: passiveOrder.ID, 704 SellOrder: aggresiveOrder.ID, 705 Timestamp: time.Now().Unix(), 706 } 707 e.Update(context.Background(), &trade, passiveOrder, aggresiveOrder) 708 709 hash := e.Hash() 710 require.Equal(t, 711 "05f6edb5f12dff7edd911da41da5962631283a01e13a717d193109454d22d10a", 712 hex.EncodeToString(hash), 713 "It should match against the known hash", 714 ) 715 716 // compute the hash 100 times for determinism verification 717 for i := 0; i < 100; i++ { 718 got := e.Hash() 719 require.Equal(t, hash, got) 720 } 721 } 722 723 func registerOrder(e *positions.SnapshotEngine, side types.Side, party string, price *num.Uint, size uint64) *types.Order { 724 order := &types.Order{ 725 Party: party, 726 Side: side, 727 Price: price, 728 Remaining: size, 729 } 730 e.RegisterOrder(context.TODO(), order) 731 return order 732 } 733 734 func TestCalcVWAP(t *testing.T) { 735 // no previous size, new long position acquired at 100 736 require.Equal(t, num.NewUint(100), positions.CalcVWAP(num.UintZero(), 0, 10, num.NewUint(100))) 737 738 // position decreased, not flipping sides 739 require.Equal(t, num.NewUint(100), positions.CalcVWAP(num.NewUint(100), 10, -5, num.NewUint(25))) 740 741 // position closed 742 require.Equal(t, num.UintZero(), positions.CalcVWAP(num.NewUint(100), 10, -10, num.NewUint(25))) 743 744 // no previous size, new short position acquired at 100 745 require.Equal(t, num.NewUint(100), positions.CalcVWAP(num.UintZero(), 0, -10, num.NewUint(100))) 746 747 // position decreased, not flipping sides 748 require.Equal(t, num.NewUint(100), positions.CalcVWAP(num.NewUint(100), -10, 5, num.NewUint(25))) 749 750 // position closed 751 require.Equal(t, num.UintZero(), positions.CalcVWAP(num.NewUint(100), -10, 10, num.NewUint(25))) 752 753 // long position increased => (100 * 10 + 25 * 5) / 15 754 require.Equal(t, num.NewUint(75), positions.CalcVWAP(num.NewUint(100), 10, 5, num.NewUint(25))) 755 756 // short position increased => (100 * -10 + 25 * -5) / 15 757 require.Equal(t, num.NewUint(75), positions.CalcVWAP(num.NewUint(100), -10, -5, num.NewUint(25))) 758 759 // flipping from long to short => (100 * 10 + 15 * 15)/25 760 require.Equal(t, num.NewUint(49), positions.CalcVWAP(num.NewUint(100), -10, 25, num.NewUint(15))) 761 762 // flipping from short to long => (100 * 10 + 15 * 15)/25 763 require.Equal(t, num.NewUint(49), positions.CalcVWAP(num.NewUint(100), 10, -25, num.NewUint(15))) 764 }