code.vegaprotocol.io/vega@v0.79.0/core/subscribers/market_depth_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 subscribers_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  
    22  	"code.vegaprotocol.io/vega/core/events"
    23  	"code.vegaprotocol.io/vega/core/subscribers"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/libs/num"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  )
    29  
    30  func getTestMDB(t *testing.T, ctx context.Context) *subscribers.MarketDepthBuilder {
    31  	t.Helper()
    32  	return subscribers.NewMarketDepthBuilder(ctx, nil, true)
    33  }
    34  
    35  func buildOrder(id string, side types.Side, orderType types.OrderType, price uint64, size uint64, remaining uint64) *types.Order {
    36  	order := &types.Order{
    37  		ID:            id,
    38  		Side:          side,
    39  		Type:          orderType,
    40  		Price:         num.NewUint(price),
    41  		OriginalPrice: num.NewUint(price),
    42  		Size:          size,
    43  		Remaining:     remaining,
    44  		TimeInForce:   types.OrderTimeInForceGTC,
    45  		Status:        types.OrderStatusActive,
    46  		MarketID:      "M",
    47  	}
    48  	return order
    49  }
    50  
    51  //nolint:unparam
    52  func newPeggedOrder(reference types.PeggedReference, offset uint64) *types.PeggedOrder {
    53  	return &types.PeggedOrder{
    54  		Reference: reference,
    55  		Offset:    num.NewUint(offset),
    56  	}
    57  }
    58  
    59  func TestBuyPriceLevels(t *testing.T) {
    60  	ctx := context.Background()
    61  	mdb := getTestMDB(t, ctx)
    62  
    63  	order1 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 9, 9)
    64  	event1 := events.NewOrderEvent(ctx, order1)
    65  	mdb.Push(event1)
    66  
    67  	order2 := buildOrder("Order2", types.SideBuy, types.OrderTypeLimit, 102, 7, 7)
    68  	event2 := events.NewOrderEvent(ctx, order2)
    69  	mdb.Push(event2)
    70  
    71  	order3 := buildOrder("Order3", types.SideBuy, types.OrderTypeLimit, 101, 8, 8)
    72  	event3 := events.NewOrderEvent(ctx, order3)
    73  	mdb.Push(event3)
    74  
    75  	order4 := buildOrder("Order4", types.SideBuy, types.OrderTypeLimit, 99, 10, 10)
    76  	event4 := events.NewOrderEvent(ctx, order4)
    77  	mdb.Push(event4)
    78  
    79  	// add some icebergs too
    80  	order5 := buildOrder("Order5", types.SideBuy, types.OrderTypeLimit, 98, 10, 10)
    81  	order5.IcebergOrder = &types.IcebergOrder{ReservedRemaining: 50}
    82  	event5 := events.NewOrderEvent(ctx, order5)
    83  	mdb.Push(event5)
    84  
    85  	assert.Equal(t, 5, mdb.GetBuyPriceLevels("M"))
    86  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
    87  	assert.Equal(t, int64(5), mdb.GetOrderCount("M"))
    88  
    89  	assert.Equal(t, uint64(7), mdb.GetVolumeAtPrice("M", types.SideBuy, 102))
    90  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 102))
    91  
    92  	assert.Equal(t, uint64(8), mdb.GetVolumeAtPrice("M", types.SideBuy, 101))
    93  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 101))
    94  
    95  	assert.Equal(t, uint64(9), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
    96  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
    97  
    98  	assert.Equal(t, uint64(10), mdb.GetVolumeAtPrice("M", types.SideBuy, 99))
    99  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 99))
   100  
   101  	assert.Equal(t, uint64(60), mdb.GetVolumeAtPrice("M", types.SideBuy, 98))
   102  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 98))
   103  }
   104  
   105  func TestSellPriceLevels(t *testing.T) {
   106  	ctx := context.Background()
   107  	mdb := getTestMDB(t, ctx)
   108  
   109  	order1 := buildOrder("Order1", types.SideSell, types.OrderTypeLimit, 100, 9, 9)
   110  	event1 := events.NewOrderEvent(ctx, order1)
   111  	mdb.Push(event1)
   112  
   113  	order2 := buildOrder("Order2", types.SideSell, types.OrderTypeLimit, 102, 7, 7)
   114  	event2 := events.NewOrderEvent(ctx, order2)
   115  	mdb.Push(event2)
   116  
   117  	order3 := buildOrder("Order3", types.SideSell, types.OrderTypeLimit, 101, 8, 8)
   118  	event3 := events.NewOrderEvent(ctx, order3)
   119  	mdb.Push(event3)
   120  
   121  	order4 := buildOrder("Order4", types.SideSell, types.OrderTypeLimit, 99, 10, 10)
   122  	event4 := events.NewOrderEvent(ctx, order4)
   123  	mdb.Push(event4)
   124  
   125  	// add some icebergs too
   126  	order5 := buildOrder("Order5", types.SideSell, types.OrderTypeLimit, 98, 10, 10)
   127  	order5.IcebergOrder = &types.IcebergOrder{ReservedRemaining: 50}
   128  	event5 := events.NewOrderEvent(ctx, order5)
   129  	mdb.Push(event5)
   130  
   131  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   132  	assert.Equal(t, 5, mdb.GetSellPriceLevels("M"))
   133  	assert.Equal(t, int64(5), mdb.GetOrderCount("M"))
   134  
   135  	assert.Equal(t, uint64(7), mdb.GetVolumeAtPrice("M", types.SideSell, 102))
   136  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideSell, 102))
   137  
   138  	assert.Equal(t, uint64(8), mdb.GetVolumeAtPrice("M", types.SideSell, 101))
   139  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideSell, 101))
   140  
   141  	assert.Equal(t, uint64(9), mdb.GetVolumeAtPrice("M", types.SideSell, 100))
   142  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideSell, 100))
   143  
   144  	assert.Equal(t, uint64(10), mdb.GetVolumeAtPrice("M", types.SideSell, 99))
   145  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideSell, 99))
   146  
   147  	assert.Equal(t, uint64(60), mdb.GetVolumeAtPrice("M", types.SideSell, 98))
   148  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideSell, 98))
   149  }
   150  
   151  func TestAddOrderToEmptyBook(t *testing.T) {
   152  	ctx := context.Background()
   153  	mdb := getTestMDB(t, ctx)
   154  
   155  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   156  	event := events.NewOrderEvent(ctx, order)
   157  	mdb.Push(event)
   158  
   159  	assert.Equal(t, 1, mdb.GetBuyPriceLevels("M"))
   160  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   161  	assert.Equal(t, int64(1), mdb.GetOrderCount("M"))
   162  
   163  	assert.Equal(t, uint64(10), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   164  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   165  }
   166  
   167  func TestCancelOrder(t *testing.T) {
   168  	ctx := context.Background()
   169  	mdb := getTestMDB(t, ctx)
   170  
   171  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   172  	event := events.NewOrderEvent(ctx, order)
   173  	mdb.Push(event)
   174  
   175  	cancelorder := *order
   176  	cancelorder.Status = types.OrderStatusCancelled
   177  	event2 := events.NewOrderEvent(ctx, &cancelorder)
   178  	mdb.Push(event2)
   179  
   180  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   181  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   182  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   183  
   184  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   185  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   186  }
   187  
   188  func TestCancelIcebergOrder(t *testing.T) {
   189  	ctx := context.Background()
   190  	mdb := getTestMDB(t, ctx)
   191  
   192  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   193  	order.IcebergOrder = &types.IcebergOrder{ReservedRemaining: 50}
   194  	event := events.NewOrderEvent(ctx, order)
   195  	mdb.Push(event)
   196  
   197  	cancelorder := *order
   198  	cancelorder.Status = types.OrderStatusCancelled
   199  	event2 := events.NewOrderEvent(ctx, &cancelorder)
   200  	mdb.Push(event2)
   201  
   202  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   203  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   204  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   205  
   206  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   207  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   208  }
   209  
   210  func TestStoppedOrder(t *testing.T) {
   211  	ctx := context.Background()
   212  	mdb := getTestMDB(t, ctx)
   213  
   214  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   215  	event := events.NewOrderEvent(ctx, order)
   216  	mdb.Push(event)
   217  
   218  	cancelorder := *order
   219  	cancelorder.Status = types.OrderStatusStopped
   220  	event2 := events.NewOrderEvent(ctx, &cancelorder)
   221  	mdb.Push(event2)
   222  
   223  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   224  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   225  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   226  
   227  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   228  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   229  }
   230  
   231  func TestExpiredOrder(t *testing.T) {
   232  	ctx := context.Background()
   233  	mdb := getTestMDB(t, ctx)
   234  
   235  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   236  	event := events.NewOrderEvent(ctx, order)
   237  	mdb.Push(event)
   238  
   239  	cancelorder := *order
   240  	cancelorder.Status = types.OrderStatusExpired
   241  	event2 := events.NewOrderEvent(ctx, &cancelorder)
   242  	mdb.Push(event2)
   243  
   244  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   245  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   246  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   247  
   248  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   249  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   250  }
   251  
   252  func TestAmendOrderPrice(t *testing.T) {
   253  	ctx := context.Background()
   254  	mdb := getTestMDB(t, ctx)
   255  
   256  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   257  	event := events.NewOrderEvent(ctx, order)
   258  	mdb.Push(event)
   259  
   260  	order2 := buildOrder("Order2", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   261  	event2 := events.NewOrderEvent(ctx, order2)
   262  	mdb.Push(event2)
   263  
   264  	// Amend the price to force a change in price level
   265  	amendorder := *order
   266  	amendorder.Price = num.NewUint(90)
   267  	amendorder.OriginalPrice = num.NewUint(90)
   268  	event3 := events.NewOrderEvent(ctx, &amendorder)
   269  	mdb.Push(event3)
   270  
   271  	assert.Equal(t, 2, mdb.GetBuyPriceLevels("M"))
   272  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   273  	assert.Equal(t, int64(2), mdb.GetOrderCount("M"))
   274  
   275  	assert.Equal(t, uint64(10), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   276  	assert.Equal(t, uint64(10), mdb.GetVolumeAtPrice("M", types.SideBuy, 90))
   277  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   278  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 90))
   279  }
   280  
   281  func TestAmendOrderVolumeUp(t *testing.T) {
   282  	ctx := context.Background()
   283  	mdb := getTestMDB(t, ctx)
   284  
   285  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   286  	event := events.NewOrderEvent(ctx, order)
   287  	mdb.Push(event)
   288  
   289  	amendorder := *order
   290  	amendorder.Size = 20
   291  	amendorder.Remaining = 20
   292  	event2 := events.NewOrderEvent(ctx, &amendorder)
   293  	mdb.Push(event2)
   294  
   295  	assert.Equal(t, 1, mdb.GetBuyPriceLevels("M"))
   296  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   297  	assert.Equal(t, int64(1), mdb.GetOrderCount("M"))
   298  
   299  	assert.Equal(t, uint64(20), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   300  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   301  }
   302  
   303  func TestAmendOrderVolumeDown(t *testing.T) {
   304  	ctx := context.Background()
   305  	mdb := getTestMDB(t, ctx)
   306  
   307  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   308  	event := events.NewOrderEvent(ctx, order)
   309  	mdb.Push(event)
   310  
   311  	amendorder := *order
   312  	amendorder.Size = 5
   313  	amendorder.Remaining = 5
   314  	event2 := events.NewOrderEvent(ctx, &amendorder)
   315  	mdb.Push(event2)
   316  
   317  	assert.Equal(t, 1, mdb.GetBuyPriceLevels("M"))
   318  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   319  	assert.Equal(t, int64(1), mdb.GetOrderCount("M"))
   320  
   321  	assert.Equal(t, uint64(5), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   322  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   323  }
   324  
   325  func TestAmendOrderVolumeDownToZero(t *testing.T) {
   326  	ctx := context.Background()
   327  	mdb := getTestMDB(t, ctx)
   328  
   329  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   330  	event := events.NewOrderEvent(ctx, order)
   331  	mdb.Push(event)
   332  
   333  	amendorder := *order
   334  	amendorder.Size = 0
   335  	amendorder.Remaining = 0
   336  	event2 := events.NewOrderEvent(ctx, &amendorder)
   337  	mdb.Push(event2)
   338  
   339  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   340  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   341  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   342  
   343  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   344  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   345  }
   346  
   347  func TestPartialFill(t *testing.T) {
   348  	ctx := context.Background()
   349  	mdb := getTestMDB(t, ctx)
   350  
   351  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   352  	event := events.NewOrderEvent(ctx, order)
   353  	mdb.Push(event)
   354  
   355  	pforder := *order
   356  	pforder.Remaining = 5
   357  	event2 := events.NewOrderEvent(ctx, &pforder)
   358  	mdb.Push(event2)
   359  
   360  	assert.Equal(t, 1, mdb.GetBuyPriceLevels("M"))
   361  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   362  	assert.Equal(t, int64(1), mdb.GetOrderCount("M"))
   363  
   364  	assert.Equal(t, uint64(5), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   365  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   366  }
   367  
   368  func TestIOCPartialFill(t *testing.T) {
   369  	ctx := context.Background()
   370  	mdb := getTestMDB(t, ctx)
   371  
   372  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 5)
   373  	order.Status = types.OrderStatusPartiallyFilled
   374  	order.TimeInForce = types.OrderTimeInForceIOC
   375  	event := events.NewOrderEvent(ctx, order)
   376  	mdb.Push(event)
   377  
   378  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   379  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   380  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   381  
   382  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   383  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   384  }
   385  
   386  func TestFullyFill(t *testing.T) {
   387  	ctx := context.Background()
   388  	mdb := getTestMDB(t, ctx)
   389  
   390  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   391  	event := events.NewOrderEvent(ctx, order)
   392  	mdb.Push(event)
   393  
   394  	fforder := *order
   395  	fforder.Remaining = 0
   396  	fforder.Status = types.OrderStatusFilled
   397  	event2 := events.NewOrderEvent(ctx, &fforder)
   398  	mdb.Push(event2)
   399  
   400  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   401  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   402  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   403  
   404  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   405  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   406  }
   407  
   408  func TestMarketOrder(t *testing.T) {
   409  	ctx := context.Background()
   410  	mdb := getTestMDB(t, ctx)
   411  
   412  	// market orders should not stay on the book
   413  	marketorder := buildOrder("Order1", types.SideBuy, types.OrderTypeMarket, 100, 10, 10)
   414  	event1 := events.NewOrderEvent(ctx, marketorder)
   415  	mdb.Push(event1)
   416  
   417  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   418  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   419  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   420  
   421  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   422  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   423  }
   424  
   425  func TestFOKOrder(t *testing.T) {
   426  	ctx := context.Background()
   427  	mdb := getTestMDB(t, ctx)
   428  
   429  	// FOK orders do not stay on the book
   430  	fokorder := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   431  	fokorder.TimeInForce = types.OrderTimeInForceFOK
   432  	event := events.NewOrderEvent(ctx, fokorder)
   433  	mdb.Push(event)
   434  
   435  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   436  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   437  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   438  
   439  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   440  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   441  }
   442  
   443  func TestIOCOrder(t *testing.T) {
   444  	ctx := context.Background()
   445  	mdb := getTestMDB(t, ctx)
   446  
   447  	// IOC orders do not stay on the book
   448  	iocorder := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   449  	iocorder.TimeInForce = types.OrderTimeInForceIOC
   450  	event := events.NewOrderEvent(ctx, iocorder)
   451  	mdb.Push(event)
   452  
   453  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   454  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   455  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   456  
   457  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   458  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   459  }
   460  
   461  func TestRejectedOrder(t *testing.T) {
   462  	ctx := context.Background()
   463  	mdb := getTestMDB(t, ctx)
   464  
   465  	// Rejected orders should be ignored
   466  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   467  	order.Status = types.OrderStatusRejected
   468  	event := events.NewOrderEvent(ctx, order)
   469  	mdb.Push(event)
   470  
   471  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   472  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   473  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   474  
   475  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   476  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   477  }
   478  
   479  func TestInvalidOrder(t *testing.T) {
   480  	ctx := context.Background()
   481  	mdb := getTestMDB(t, ctx)
   482  
   483  	// Invalid orders should be ignored
   484  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   485  	order.Status = types.OrderStatusUnspecified
   486  	event := events.NewOrderEvent(ctx, order)
   487  	mdb.Push(event)
   488  
   489  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   490  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   491  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   492  
   493  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   494  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   495  }
   496  
   497  func TestPartialMatchOrders(t *testing.T) {
   498  	ctx := context.Background()
   499  	mdb := getTestMDB(t, ctx)
   500  
   501  	order1 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   502  	event1 := events.NewOrderEvent(ctx, order1)
   503  	mdb.Push(event1)
   504  	order2 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 8)
   505  	event2 := events.NewOrderEvent(ctx, order2)
   506  	mdb.Push(event2)
   507  
   508  	order3 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 5)
   509  	event3 := events.NewOrderEvent(ctx, order3)
   510  	mdb.Push(event3)
   511  	order4 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 1)
   512  	event4 := events.NewOrderEvent(ctx, order4)
   513  	mdb.Push(event4)
   514  
   515  	assert.Equal(t, 1, mdb.GetBuyPriceLevels("M"))
   516  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   517  	assert.Equal(t, int64(1), mdb.GetOrderCount("M"))
   518  
   519  	assert.Equal(t, uint64(1), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   520  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   521  }
   522  
   523  func TestFullyMatchOrders(t *testing.T) {
   524  	ctx := context.Background()
   525  	mdb := getTestMDB(t, ctx)
   526  
   527  	order1 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   528  	event1 := events.NewOrderEvent(ctx, order1)
   529  	mdb.Push(event1)
   530  	order2 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 8)
   531  	event2 := events.NewOrderEvent(ctx, order2)
   532  	mdb.Push(event2)
   533  
   534  	order3 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 5)
   535  	event3 := events.NewOrderEvent(ctx, order3)
   536  	mdb.Push(event3)
   537  	order4 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 0)
   538  	order4.Status = types.OrderStatusFilled
   539  	event4 := events.NewOrderEvent(ctx, order4)
   540  	mdb.Push(event4)
   541  
   542  	assert.Equal(t, 0, mdb.GetBuyPriceLevels("M"))
   543  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   544  	assert.Equal(t, int64(0), mdb.GetOrderCount("M"))
   545  
   546  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   547  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   548  }
   549  
   550  func TestRemovingPriceLevels(t *testing.T) {
   551  	ctx := context.Background()
   552  	mdb := getTestMDB(t, ctx)
   553  
   554  	order1 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 101, 10, 10)
   555  	event1 := events.NewOrderEvent(ctx, order1)
   556  	mdb.Push(event1)
   557  	order2 := buildOrder("Order2", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   558  	event2 := events.NewOrderEvent(ctx, order2)
   559  	mdb.Push(event2)
   560  	order3 := buildOrder("Order3", types.SideBuy, types.OrderTypeLimit, 102, 10, 10)
   561  	event3 := events.NewOrderEvent(ctx, order3)
   562  	mdb.Push(event3)
   563  
   564  	order4 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 101, 10, 0)
   565  	order4.Status = types.OrderStatusFilled
   566  	event4 := events.NewOrderEvent(ctx, order4)
   567  	mdb.Push(event4)
   568  
   569  	assert.Equal(t, 2, mdb.GetBuyPriceLevels("M"))
   570  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   571  	assert.Equal(t, int64(2), mdb.GetOrderCount("M"))
   572  
   573  	assert.Equal(t, uint64(0), mdb.GetVolumeAtPrice("M", types.SideBuy, 101))
   574  	assert.Equal(t, uint64(0), mdb.GetOrderCountAtPrice("M", types.SideBuy, 101))
   575  }
   576  
   577  func TestMarketDepthFields(t *testing.T) {
   578  	ctx := context.Background()
   579  	mdb := getTestMDB(t, ctx)
   580  
   581  	order1 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 101, 10, 10)
   582  	event1 := events.NewOrderEvent(ctx, order1)
   583  	mdb.Push(event1)
   584  
   585  	md, err := mdb.GetMarketDepth(ctx, "M", 0)
   586  	assert.Nil(t, err)
   587  	assert.NotNil(t, md)
   588  
   589  	assert.Equal(t, "M", md.MarketId)
   590  	assert.Equal(t, 1, len(md.GetBuy()))
   591  
   592  	priceLevels := md.GetBuy()
   593  	pl := priceLevels[0]
   594  	assert.NotNil(t, pl)
   595  	assert.Equal(t, uint64(1), pl.NumberOfOrders)
   596  	assert.Equal(t, "101", pl.Price)
   597  	assert.Equal(t, uint64(10), pl.Volume)
   598  }
   599  
   600  func TestParkingOrder(t *testing.T) {
   601  	ctx := context.Background()
   602  	mdb := getTestMDB(t, ctx)
   603  
   604  	// Create a valid and live pegged order
   605  	order1 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 101, 10, 10)
   606  	order1.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   607  	event1 := events.NewOrderEvent(ctx, order1)
   608  	mdb.Push(event1)
   609  
   610  	// Park it
   611  	order2 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 0, 10, 10)
   612  	order2.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   613  	order2.Status = types.OrderStatusParked
   614  	event2 := events.NewOrderEvent(ctx, order2)
   615  	mdb.Push(event2)
   616  
   617  	md, err := mdb.GetMarketDepth(ctx, "M", 0)
   618  	assert.Nil(t, err)
   619  	assert.NotNil(t, md)
   620  
   621  	assert.Equal(t, "M", md.MarketId)
   622  	assert.Equal(t, 0, len(md.GetBuy()))
   623  	assert.Equal(t, 0, len(md.GetSell()))
   624  
   625  	// Unpark it
   626  	order3 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 101, 10, 10)
   627  	order3.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   628  	order3.Status = types.OrderStatusActive
   629  	event3 := events.NewOrderEvent(ctx, order3)
   630  	mdb.Push(event3)
   631  
   632  	md2, err := mdb.GetMarketDepth(ctx, "M", 0)
   633  	assert.Nil(t, err)
   634  	assert.NotNil(t, md2)
   635  
   636  	assert.Equal(t, "M", md2.MarketId)
   637  	assert.Equal(t, 1, len(md2.GetBuy()))
   638  	assert.Equal(t, 0, len(md2.GetSell()))
   639  }
   640  
   641  func TestParkedOrder(t *testing.T) {
   642  	ctx := context.Background()
   643  	mdb := getTestMDB(t, ctx)
   644  
   645  	// Create a parked pegged order which should not go on the depth book
   646  	order1 := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 101, 10, 10)
   647  	order1.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   648  	order1.Status = types.OrderStatusParked
   649  	event1 := events.NewOrderEvent(ctx, order1)
   650  	mdb.Push(event1)
   651  
   652  	md, err := mdb.GetMarketDepth(ctx, "M", 0)
   653  	assert.Nil(t, err)
   654  	assert.NotNil(t, md)
   655  
   656  	assert.Equal(t, "M", md.MarketId)
   657  	assert.Equal(t, 0, len(md.GetBuy()))
   658  	assert.Equal(t, 0, len(md.GetSell()))
   659  }
   660  
   661  func TestParkedOrder2(t *testing.T) {
   662  	ctx := context.Background()
   663  	mdb := getTestMDB(t, ctx)
   664  
   665  	// Create parked pegged order
   666  	order1 := buildOrder("Pegged1", types.SideBuy, types.OrderTypeLimit, 0, 10, 10)
   667  	order1.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   668  	order1.Status = types.OrderStatusParked
   669  	event1 := events.NewOrderEvent(ctx, order1)
   670  	mdb.Push(event1)
   671  
   672  	// Create normal order
   673  	order2 := buildOrder("Normal1", types.SideBuy, types.OrderTypeLimit, 100, 1, 1)
   674  	event2 := events.NewOrderEvent(ctx, order2)
   675  	mdb.Push(event2)
   676  
   677  	// Unpark pegged order
   678  	order3 := buildOrder("Pegged1", types.SideBuy, types.OrderTypeLimit, 99, 10, 10)
   679  	order3.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   680  	order3.Status = types.OrderStatusActive
   681  	event3 := events.NewOrderEvent(ctx, order3)
   682  	mdb.Push(event3)
   683  
   684  	// Cancel normal order
   685  	order4 := buildOrder("Normal1", types.SideBuy, types.OrderTypeLimit, 100, 1, 1)
   686  	order4.Status = types.OrderStatusCancelled
   687  	event4 := events.NewOrderEvent(ctx, order4)
   688  	mdb.Push(event4)
   689  
   690  	// Park pegged order
   691  	order5 := buildOrder("Pegged1", types.SideBuy, types.OrderTypeLimit, 99, 10, 10)
   692  	order5.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   693  	order5.Status = types.OrderStatusParked
   694  	event5 := events.NewOrderEvent(ctx, order5)
   695  	mdb.Push(event5)
   696  
   697  	// Create normal order
   698  	order6 := buildOrder("Normal2", types.SideBuy, types.OrderTypeLimit, 100, 1, 1)
   699  	event6 := events.NewOrderEvent(ctx, order6)
   700  	mdb.Push(event6)
   701  
   702  	// Unpark pegged order
   703  	order7 := buildOrder("Pegged1", types.SideBuy, types.OrderTypeLimit, 99, 10, 10)
   704  	order7.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   705  	order7.Status = types.OrderStatusActive
   706  	event7 := events.NewOrderEvent(ctx, order7)
   707  	mdb.Push(event7)
   708  
   709  	// Fill normal order
   710  	order8 := buildOrder("Normal2", types.SideBuy, types.OrderTypeLimit, 100, 1, 0)
   711  	order8.Status = types.OrderStatusFilled
   712  	event8 := events.NewOrderEvent(ctx, order8)
   713  	mdb.Push(event8)
   714  
   715  	// Create new matching order
   716  	order9 := buildOrder("Normal3", types.SideSell, types.OrderTypeLimit, 100, 1, 0)
   717  	order9.Status = types.OrderStatusFilled
   718  	event9 := events.NewOrderEvent(ctx, order9)
   719  	mdb.Push(event9)
   720  
   721  	// Park pegged order
   722  	order10 := buildOrder("Pegged1", types.SideBuy, types.OrderTypeLimit, 99, 10, 10)
   723  	order10.PeggedOrder = newPeggedOrder(types.PeggedReferenceBestBid, 1)
   724  	order10.Status = types.OrderStatusParked
   725  	event10 := events.NewOrderEvent(ctx, order10)
   726  	mdb.Push(event10)
   727  
   728  	md, err := mdb.GetMarketDepth(ctx, "M", 0)
   729  	assert.Nil(t, err)
   730  	assert.NotNil(t, md)
   731  
   732  	assert.Equal(t, "M", md.MarketId)
   733  	assert.Equal(t, 0, len(md.GetBuy()))
   734  	assert.Equal(t, 0, len(md.GetSell()))
   735  }
   736  
   737  func TestIcebergRefresh(t *testing.T) {
   738  	ctx := context.Background()
   739  	mdb := getTestMDB(t, ctx)
   740  
   741  	order := buildOrder("Order1", types.SideBuy, types.OrderTypeLimit, 100, 10, 10)
   742  	order.IcebergOrder = &types.IcebergOrder{ReservedRemaining: 50}
   743  	event := events.NewOrderEvent(ctx, order)
   744  	mdb.Push(event)
   745  
   746  	// pretend the iceberg refreshed, so its remaining increased and reserve went down
   747  	amendorder := *order
   748  	amendorder.Size = 60
   749  	amendorder.Remaining = 15
   750  	amendorder.IcebergOrder.ReservedRemaining = 40
   751  	event2 := events.NewOrderEvent(ctx, &amendorder)
   752  	mdb.Push(event2)
   753  
   754  	assert.Equal(t, 1, mdb.GetBuyPriceLevels("M"))
   755  	assert.Equal(t, 0, mdb.GetSellPriceLevels("M"))
   756  	assert.Equal(t, int64(1), mdb.GetOrderCount("M"))
   757  
   758  	assert.Equal(t, uint64(55), mdb.GetVolumeAtPrice("M", types.SideBuy, 100))
   759  	assert.Equal(t, uint64(1), mdb.GetOrderCountAtPrice("M", types.SideBuy, 100))
   760  }