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  }