code.vegaprotocol.io/vega@v0.79.0/core/matching/iceberg_orders_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  	vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
    23  	"code.vegaprotocol.io/vega/libs/num"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/require"
    27  )
    28  
    29  func submitPeggedIcebergOrder(t *testing.T, book *tstOB, size, peak, minPeak uint64) (*types.Order, *types.OrderConfirmation) {
    30  	t.Helper()
    31  	o := &types.Order{
    32  		ID:            vgcrypto.RandomHash(),
    33  		Status:        types.OrderStatusActive,
    34  		MarketID:      book.marketID,
    35  		Party:         "A",
    36  		Side:          types.SideBuy,
    37  		Price:         num.NewUint(100),
    38  		OriginalPrice: num.NewUint(100),
    39  		Size:          size,
    40  		Remaining:     size,
    41  		TimeInForce:   types.OrderTimeInForceGTT,
    42  		Type:          types.OrderTypeLimit,
    43  		ExpiresAt:     10,
    44  		PeggedOrder: &types.PeggedOrder{
    45  			Reference: types.PeggedReferenceMid,
    46  			Offset:    num.UintOne(),
    47  		},
    48  		IcebergOrder: &types.IcebergOrder{
    49  			PeakSize:           peak,
    50  			MinimumVisibleSize: minPeak,
    51  		},
    52  	}
    53  	confirm, err := book.SubmitOrder(o)
    54  	require.NoError(t, err)
    55  	return o, confirm
    56  }
    57  
    58  func submitIcebergOrder(t *testing.T, book *tstOB, size, peak, minPeak uint64, addToBook bool) (*types.Order, *types.OrderConfirmation) {
    59  	t.Helper()
    60  	o := &types.Order{
    61  		ID:            vgcrypto.RandomHash(),
    62  		Status:        types.OrderStatusActive,
    63  		MarketID:      book.marketID,
    64  		Party:         "A",
    65  		Side:          types.SideBuy,
    66  		Price:         num.NewUint(100),
    67  		OriginalPrice: num.NewUint(100),
    68  		Size:          size,
    69  		Remaining:     size,
    70  		TimeInForce:   types.OrderTimeInForceGTT,
    71  		Type:          types.OrderTypeLimit,
    72  		ExpiresAt:     10,
    73  		IcebergOrder: &types.IcebergOrder{
    74  			PeakSize:           peak,
    75  			MinimumVisibleSize: minPeak,
    76  		},
    77  	}
    78  	confirm, err := book.SubmitOrder(o)
    79  	require.NoError(t, err)
    80  
    81  	if addToBook {
    82  		// aggressive iceberg orders do not naturally sit on the book and are added a different way so
    83  		// we do that here
    84  		o.Remaining = peak
    85  		o.IcebergOrder.ReservedRemaining = size - peak
    86  		// book.SubmitIcebergOrder(o)
    87  	}
    88  	return o, confirm
    89  }
    90  
    91  func submitCrossedOrder(t *testing.T, book *tstOB, size uint64) (*types.Order, *types.OrderConfirmation) {
    92  	t.Helper()
    93  	o := &types.Order{
    94  		ID:            vgcrypto.RandomHash(),
    95  		Status:        types.OrderStatusActive,
    96  		MarketID:      book.marketID,
    97  		Party:         "B",
    98  		Side:          types.SideSell,
    99  		Price:         num.NewUint(100),
   100  		OriginalPrice: num.NewUint(100),
   101  		Size:          size,
   102  		Remaining:     size,
   103  		TimeInForce:   types.OrderTimeInForceGTC,
   104  		Type:          types.OrderTypeLimit,
   105  	}
   106  	confirm, err := book.SubmitOrder(o)
   107  	require.NoError(t, err)
   108  	return o, confirm
   109  }
   110  
   111  func submitCrossedWashOrder(t *testing.T, book *tstOB, size uint64) (*types.Order, *types.OrderConfirmation) {
   112  	t.Helper()
   113  	o := &types.Order{
   114  		ID:            vgcrypto.RandomHash(),
   115  		Status:        types.OrderStatusActive,
   116  		MarketID:      book.marketID,
   117  		Party:         "A",
   118  		Side:          types.SideSell,
   119  		Price:         num.NewUint(100),
   120  		OriginalPrice: num.NewUint(100),
   121  		Size:          size,
   122  		Remaining:     size,
   123  		TimeInForce:   types.OrderTimeInForceGTC,
   124  		Type:          types.OrderTypeLimit,
   125  	}
   126  	confirm, err := book.SubmitOrder(o)
   127  	require.NoError(t, err)
   128  	return o, confirm
   129  }
   130  
   131  func getTradesCrossedOrder(t *testing.T, book *tstOB, size uint64) []*types.Trade {
   132  	t.Helper()
   133  	o := &types.Order{
   134  		ID:            vgcrypto.RandomHash(),
   135  		Status:        types.OrderStatusActive,
   136  		MarketID:      book.marketID,
   137  		Party:         "B",
   138  		Side:          types.SideSell,
   139  		Price:         num.NewUint(100),
   140  		OriginalPrice: num.NewUint(100),
   141  		Size:          size,
   142  		Remaining:     size,
   143  		TimeInForce:   types.OrderTimeInForceGTC,
   144  		Type:          types.OrderTypeLimit,
   145  	}
   146  	trades, err := book.GetTrades(o)
   147  	require.NoError(t, err)
   148  	return trades
   149  }
   150  
   151  func TestIcebergsFakeUncross(t *testing.T) {
   152  	market := "testMarket"
   153  	book := getTestOrderBook(t, market)
   154  	defer book.Finish()
   155  
   156  	// submit an iceberg order that sits on the book
   157  	iceberg, confirm := submitIcebergOrder(t, book, 100, 4, 2, false)
   158  	assert.Equal(t, 0, len(confirm.Trades))
   159  
   160  	// check it is now on the book
   161  	_, err := book.GetOrderByID(iceberg.ID)
   162  	assert.NoError(t, err)
   163  	assert.Equal(t, uint64(100), book.getTotalBuyVolume())
   164  
   165  	// check the peaks are proper
   166  	assert.Equal(t, uint64(4), iceberg.Remaining)
   167  	assert.Equal(t, uint64(96), iceberg.IcebergOrder.ReservedRemaining)
   168  
   169  	// submit an order bigger than the peak
   170  	trades := getTradesCrossedOrder(t, book, 10)
   171  	assert.Equal(t, 1, len(trades))
   172  	assert.Equal(t, uint64(10), trades[0].Size)
   173  
   174  	// now submit it for real, and check refresh happens
   175  	o, confirm := submitCrossedOrder(t, book, 10)
   176  	assert.Equal(t, 1, len(confirm.Trades))
   177  	assert.Equal(t, uint64(10), trades[0].Size)
   178  	assert.Equal(t, uint64(0), o.Remaining)
   179  }
   180  
   181  func TestIcebergFullPeakConsumedExactly(t *testing.T) {
   182  	market := "testMarket"
   183  	book := getTestOrderBook(t, market)
   184  	defer book.Finish()
   185  
   186  	// submit an iceberg order that sits on the book
   187  	iceberg, confirm := submitIcebergOrder(t, book, 100, 40, 10, false)
   188  	assert.Equal(t, 0, len(confirm.Trades))
   189  
   190  	// check it is now on the book
   191  	_, err := book.GetOrderByID(iceberg.ID)
   192  	assert.NoError(t, err)
   193  	assert.Equal(t, uint64(100), book.getTotalBuyVolume())
   194  
   195  	trades := getTradesCrossedOrder(t, book, 40)
   196  	assert.Equal(t, 1, len(trades))
   197  	assert.Equal(t, uint64(40), trades[0].Size)
   198  
   199  	// now submit it and check it gets filled
   200  	o, confirm := submitCrossedOrder(t, book, 40)
   201  	assert.Equal(t, 1, len(confirm.Trades))
   202  	assert.Equal(t, types.OrderStatusFilled, o.Status)
   203  
   204  	// check that the iceberg has been refreshed and book volume is back at 40
   205  	assert.Equal(t, 1, book.getNumberOfBuyLevels())
   206  	assert.Equal(t, uint64(60), book.getTotalBuyVolume())
   207  	assert.Equal(t, uint64(40), iceberg.Remaining)
   208  	assert.Equal(t, uint64(20), iceberg.IcebergOrder.ReservedRemaining)
   209  }
   210  
   211  func TestIcebergPeakAboveMinimum(t *testing.T) {
   212  	market := "testMarket"
   213  	book := getTestOrderBook(t, market)
   214  	defer book.Finish()
   215  
   216  	iceberg, confirm := submitIcebergOrder(t, book, 100, 4, 2, true)
   217  	assert.Equal(t, 0, len(confirm.Trades))
   218  
   219  	// submit order that only takes a little off the peak
   220  	_, confirm = submitCrossedOrder(t, book, 1)
   221  	assert.Equal(t, 1, len(confirm.Trades))
   222  	assert.Equal(t, 1, book.getNumberOfBuyLevels())
   223  	assert.Equal(t, uint64(99), book.getTotalBuyVolume())
   224  
   225  	// now submit another order that *will* remove the rest of the peak
   226  	_, confirm = submitCrossedOrder(t, book, 3)
   227  	assert.Equal(t, 1, len(confirm.Trades))
   228  
   229  	assert.Equal(t, uint64(4), iceberg.Remaining)
   230  	assert.Equal(t, uint64(92), iceberg.IcebergOrder.ReservedRemaining)
   231  	assert.Equal(t, uint64(96), book.getTotalBuyVolume())
   232  }
   233  
   234  func TestIcebergAggressiveTakesAll(t *testing.T) {
   235  	market := "testMarket"
   236  	book := getTestOrderBook(t, market)
   237  	defer book.Finish()
   238  
   239  	_, confirm := submitCrossedOrder(t, book, 10)
   240  	assert.Equal(t, 0, len(confirm.Trades))
   241  
   242  	// submit the iceberg as an aggressive order and more than its peak is consumed
   243  	o, confirm := submitIcebergOrder(t, book, 50, 4, 2, false)
   244  	assert.Equal(t, 1, len(confirm.Trades))
   245  	assert.Equal(t, uint64(10), confirm.Trades[0].Size)
   246  
   247  	// now check iceberg sits on the book with the correct peaks
   248  	assert.Equal(t, uint64(4), o.Remaining)
   249  	assert.Equal(t, uint64(36), o.IcebergOrder.ReservedRemaining)
   250  	assert.Equal(t, uint64(40), book.getTotalBuyVolume())
   251  }
   252  
   253  func TestAggressiveIcebergFullyFilled(t *testing.T) {
   254  	market := "testMarket"
   255  	book := getTestOrderBook(t, market)
   256  	defer book.Finish()
   257  
   258  	_, confirm := submitCrossedOrder(t, book, 1000)
   259  	assert.Equal(t, 0, len(confirm.Trades))
   260  
   261  	// submit the aggressice iceberg that will be fully filled
   262  	iceberg, confirm := submitIcebergOrder(t, book, 100, 4, 2, false)
   263  	assert.Equal(t, 1, len(confirm.Trades))
   264  
   265  	// check that
   266  	assert.Equal(t, uint64(0), iceberg.Remaining)
   267  	assert.Equal(t, uint64(0), iceberg.IcebergOrder.ReservedRemaining)
   268  	assert.Equal(t, types.OrderStatusFilled, iceberg.Status)
   269  	assert.Equal(t, uint64(0), book.getTotalBuyVolume())
   270  	assert.Equal(t, uint64(900), book.getTotalSellVolume())
   271  }
   272  
   273  func TestIcebergPeakBelowMinimumNotZero(t *testing.T) {
   274  	market := "testMarket"
   275  	book := getTestOrderBook(t, market)
   276  	defer book.Finish()
   277  
   278  	iceberg, confirm := submitIcebergOrder(t, book, 100, 4, 2, true)
   279  	assert.Equal(t, 0, len(confirm.Trades))
   280  
   281  	// submit an order that takes the berg below its minimum visible size, but is not zero
   282  	_, confirm = submitCrossedOrder(t, book, 3)
   283  	assert.Equal(t, 1, len(confirm.Trades))
   284  
   285  	// check it refreshes properly
   286  	assert.Equal(t, types.OrderStatusActive, iceberg.Status)
   287  	assert.Equal(t, uint64(4), iceberg.Remaining)
   288  	assert.Equal(t, uint64(93), iceberg.IcebergOrder.ReservedRemaining)
   289  	assert.Equal(t, uint64(97), book.getTotalBuyVolume())
   290  
   291  	// put in another order which will eat into the remaining
   292  	submitCrossedOrder(t, book, 10)
   293  	assert.Equal(t, uint64(4), iceberg.Remaining)
   294  	assert.Equal(t, uint64(83), iceberg.IcebergOrder.ReservedRemaining)
   295  	assert.Equal(t, uint64(87), book.getTotalBuyVolume())
   296  }
   297  
   298  func TestIcebergRefreshToPartialPeak(t *testing.T) {
   299  	market := "testMarket"
   300  	book := getTestOrderBook(t, market)
   301  	defer book.Finish()
   302  
   303  	// submit an iceberg order that sits on the book with a big peak
   304  	iceberg, confirm := submitIcebergOrder(t, book, 100, 90, 2, true)
   305  	assert.Equal(t, 0, len(confirm.Trades))
   306  
   307  	// expect the volume to be the peak size
   308  	assert.Equal(t, uint64(100), book.getTotalBuyVolume())
   309  
   310  	// submit an order that takes almost the full peak
   311  	_, confirm = submitCrossedOrder(t, book, 89)
   312  	assert.Equal(t, 1, len(confirm.Trades))
   313  
   314  	// remaining + reserved < peak size
   315  	assert.Equal(t, uint64(11), iceberg.Remaining)
   316  	assert.Equal(t, uint64(0), iceberg.IcebergOrder.ReservedRemaining)
   317  	assert.Equal(t, uint64(11), book.getTotalBuyVolume())
   318  
   319  	// check we can now fill it and the iceberg is removed
   320  	_, confirm = submitCrossedOrder(t, book, 100)
   321  	assert.Equal(t, 1, len(confirm.Trades))
   322  	assert.Equal(t, uint64(0), iceberg.Remaining)
   323  	assert.Equal(t, uint64(0), iceberg.IcebergOrder.ReservedRemaining)
   324  	assert.Equal(t, types.OrderStatusFilled, iceberg.Status)
   325  	assert.Equal(t, uint64(0), book.getTotalBuyVolume())
   326  }
   327  
   328  func TestIcebergHiddenDistribution(t *testing.T) {
   329  	market := "testMarket"
   330  	book := getTestOrderBook(t, market)
   331  	defer book.Finish()
   332  
   333  	// submit 3 iceberg orders
   334  	iceberg1, confirm := submitIcebergOrder(t, book, 300, 100, 2, false)
   335  	assert.Equal(t, 0, len(confirm.Trades))
   336  
   337  	iceberg2, confirm := submitIcebergOrder(t, book, 300, 200, 2, false)
   338  	assert.Equal(t, 0, len(confirm.Trades))
   339  
   340  	iceberg3, _ := submitIcebergOrder(t, book, 200, 100, 2, false)
   341  
   342  	// submit a big order such that all three peaks are consumed (100 + 200 + 100 = 400)
   343  	// and the left over is 300
   344  	trades := getTradesCrossedOrder(t, book, 700)
   345  	assert.Equal(t, 3, len(trades))
   346  	assert.Equal(t, uint64(250), trades[0].Size)
   347  	assert.Equal(t, uint64(275), trades[1].Size)
   348  	assert.Equal(t, uint64(175), trades[2].Size)
   349  
   350  	// now submit it for real
   351  	o, confirm := submitCrossedOrder(t, book, 700)
   352  	assert.Equal(t, 3, len(confirm.Trades))
   353  	assert.Equal(t, types.OrderStatusFilled, o.Status)
   354  
   355  	// check iceberg one has been refresh properly
   356  	assert.Equal(t, uint64(50), iceberg1.Remaining)
   357  	assert.Equal(t, uint64(0), iceberg1.IcebergOrder.ReservedRemaining)
   358  
   359  	assert.Equal(t, uint64(25), iceberg2.Remaining)
   360  	assert.Equal(t, uint64(0), iceberg2.IcebergOrder.ReservedRemaining)
   361  
   362  	assert.Equal(t, uint64(25), iceberg3.Remaining)
   363  	assert.Equal(t, uint64(0), iceberg3.IcebergOrder.ReservedRemaining)
   364  }
   365  
   366  func TestIcebergHiddenDistributionCrumbs(t *testing.T) {
   367  	market := "testMarket"
   368  	book := getTestOrderBook(t, market)
   369  	defer book.Finish()
   370  
   371  	// submit 3 iceberg orders of equal sizes
   372  	iceberg1, _ := submitIcebergOrder(t, book, 500, 100, 2, false)
   373  	iceberg2, _ := submitIcebergOrder(t, book, 500, 100, 2, false)
   374  	iceberg3, _ := submitIcebergOrder(t, book, 500, 100, 100, false)
   375  	assert.Equal(t, uint64(1500), book.getTotalBuyVolume())
   376  
   377  	// submit a big order such that all three peaks are consumed (100 + 100 + 100 = 300)
   378  	// and the left over is 100 to be divided between three
   379  	trades := getTradesCrossedOrder(t, book, 400)
   380  	assert.Equal(t, 3, len(trades))
   381  	assert.Equal(t, uint64(134), trades[0].Size)
   382  	assert.Equal(t, uint64(133), trades[1].Size)
   383  	assert.Equal(t, uint64(133), trades[2].Size)
   384  
   385  	// now submit it for real
   386  	o, confirm := submitCrossedOrder(t, book, 400)
   387  	assert.Equal(t, 3, len(confirm.Trades))
   388  	assert.Equal(t, types.OrderStatusFilled, o.Status)
   389  
   390  	// check iceberg one has been refresh properly
   391  	assert.Equal(t, uint64(100), iceberg1.Remaining)
   392  	assert.Equal(t, uint64(266), iceberg1.IcebergOrder.ReservedRemaining)
   393  
   394  	assert.Equal(t, uint64(100), iceberg2.Remaining)
   395  	assert.Equal(t, uint64(267), iceberg2.IcebergOrder.ReservedRemaining)
   396  
   397  	assert.Equal(t, uint64(100), iceberg3.Remaining)
   398  	assert.Equal(t, uint64(267), iceberg3.IcebergOrder.ReservedRemaining)
   399  }
   400  
   401  func TestIcebergHiddenDistributionFullyConsumed(t *testing.T) {
   402  	market := "testMarket"
   403  	book := getTestOrderBook(t, market)
   404  	defer book.Finish()
   405  
   406  	// submit 3 iceberg orders
   407  	iceberg1, confirm := submitIcebergOrder(t, book, 300, 100, 2, false)
   408  	assert.Equal(t, 0, len(confirm.Trades))
   409  
   410  	iceberg2, confirm := submitIcebergOrder(t, book, 300, 200, 2, false)
   411  	assert.Equal(t, 0, len(confirm.Trades))
   412  
   413  	iceberg3, _ := submitIcebergOrder(t, book, 200, 100, 2, false)
   414  
   415  	// submit a big order such that all three peaks are consumed (100 + 200 + 100 = 400)
   416  	// and all of the hidden volume (200 + 100 + 100 = 400)
   417  	trades := getTradesCrossedOrder(t, book, 1000)
   418  	assert.Equal(t, 3, len(trades))
   419  	assert.Equal(t, uint64(300), trades[0].Size)
   420  	assert.Equal(t, uint64(300), trades[1].Size)
   421  	assert.Equal(t, uint64(200), trades[2].Size)
   422  
   423  	// now submit it for real
   424  	o, confirm := submitCrossedOrder(t, book, 1000)
   425  	assert.Equal(t, 3, len(confirm.Trades))
   426  	assert.Equal(t, types.OrderStatusActive, o.Status)
   427  	assert.Equal(t, uint64(200), o.Remaining)
   428  
   429  	// check iceberg one has been refresh properly
   430  	assert.Equal(t, uint64(0), iceberg1.Remaining)
   431  	assert.Equal(t, uint64(0), iceberg1.IcebergOrder.ReservedRemaining)
   432  	assert.Equal(t, types.OrderStatusFilled, iceberg1.Status)
   433  
   434  	assert.Equal(t, uint64(0), iceberg2.Remaining)
   435  	assert.Equal(t, uint64(0), iceberg2.IcebergOrder.ReservedRemaining)
   436  	assert.Equal(t, types.OrderStatusFilled, iceberg2.Status)
   437  
   438  	assert.Equal(t, uint64(0), iceberg3.Remaining)
   439  	assert.Equal(t, uint64(0), iceberg3.IcebergOrder.ReservedRemaining)
   440  	assert.Equal(t, types.OrderStatusFilled, iceberg2.Status)
   441  }
   442  
   443  func TestIcebergHiddenDistributionPrimeHidden(t *testing.T) {
   444  	market := "testMarket"
   445  	book := getTestOrderBook(t, market)
   446  	defer book.Finish()
   447  
   448  	// submit 3 iceberg orders
   449  	iceberg1, confirm := submitIcebergOrder(t, book, 300, 43, 2, false)
   450  	assert.Equal(t, 0, len(confirm.Trades))
   451  
   452  	iceberg2, confirm := submitIcebergOrder(t, book, 300, 67, 2, false)
   453  	assert.Equal(t, 0, len(confirm.Trades))
   454  
   455  	iceberg3, _ := submitIcebergOrder(t, book, 200, 9, 2, false)
   456  
   457  	// submit a big order such that all three peaks are consumed (43 + 67 + 9 = 119)
   458  	// and the individual remaining hidden volumes are prime (257, 233, 191) such
   459  	// that the distributed amount will make nasty precision crumbs
   460  	trades := getTradesCrossedOrder(t, book, 250)
   461  	assert.Equal(t, 3, len(trades))
   462  	assert.Equal(t, uint64(94), trades[0].Size)
   463  	assert.Equal(t, uint64(111), trades[1].Size)
   464  	assert.Equal(t, uint64(45), trades[2].Size)
   465  
   466  	// now submit it for real
   467  	o, confirm := submitCrossedOrder(t, book, 250)
   468  	assert.Equal(t, 3, len(confirm.Trades))
   469  	assert.Equal(t, types.OrderStatusFilled, o.Status)
   470  	assert.Equal(t, uint64(0), o.Remaining)
   471  
   472  	// check iceberg one has been refresh properly
   473  	assert.Equal(t, uint64(43), iceberg1.Remaining)
   474  	assert.Equal(t, uint64(163), iceberg1.IcebergOrder.ReservedRemaining)
   475  	assert.Equal(t, types.OrderStatusActive, iceberg1.Status)
   476  
   477  	assert.Equal(t, uint64(67), iceberg2.Remaining)
   478  	assert.Equal(t, uint64(122), iceberg2.IcebergOrder.ReservedRemaining)
   479  	assert.Equal(t, types.OrderStatusActive, iceberg2.Status)
   480  
   481  	assert.Equal(t, uint64(9), iceberg3.Remaining)
   482  	assert.Equal(t, uint64(146), iceberg3.IcebergOrder.ReservedRemaining)
   483  	assert.Equal(t, types.OrderStatusActive, iceberg2.Status)
   484  
   485  	assert.Equal(t, uint64(550), book.getTotalBuyVolume())
   486  }
   487  
   488  func TestIcebergTimePriorityLostOnRefresh(t *testing.T) {
   489  	market := "testMarket"
   490  	book := getTestOrderBook(t, market)
   491  	defer book.Finish()
   492  
   493  	// submit an iceberg order that sits on the book with a big peak
   494  	iceberg, confirm := submitIcebergOrder(t, book, 100, 10, 8, true)
   495  	assert.Equal(t, 0, len(confirm.Trades))
   496  
   497  	// now submit a second order that will be next in line
   498  	iceberg2, confirm := submitIcebergOrder(t, book, 100, 100, 1, true)
   499  	assert.Equal(t, 0, len(confirm.Trades))
   500  
   501  	// expect the volume to be the peak size
   502  	assert.Equal(t, uint64(200), book.getTotalBuyVolume())
   503  
   504  	// submit a order that will take out some of the peak of the first iceberg, check its refreshed
   505  	_, confirm = submitCrossedOrder(t, book, 5)
   506  	assert.Equal(t, 1, len(confirm.Trades))
   507  	assert.Equal(t, uint64(10), iceberg.Remaining)
   508  	assert.Equal(t, uint64(85), iceberg.IcebergOrder.ReservedRemaining)
   509  
   510  	// a new small order will match with the second iceberg
   511  	_, confirm = submitCrossedOrder(t, book, 1)
   512  	assert.Equal(t, 1, len(confirm.Trades))
   513  	assert.Equal(t, iceberg2.ID, confirm.PassiveOrdersAffected[0].ID)
   514  }
   515  
   516  func TestAmendIceberg(t *testing.T) {
   517  	market := "testMarket"
   518  	book := getTestOrderBook(t, market)
   519  	defer book.Finish()
   520  
   521  	// submit an iceberg order that sits on the book with a big peak
   522  	iceberg, confirm := submitIcebergOrder(t, book, 100, 10, 8, true)
   523  	assert.Equal(t, 0, len(confirm.Trades))
   524  	assert.Equal(t, uint64(100), book.getTotalBuyVolume())
   525  
   526  	// amend iceberg such that the size is increased and the reserve is increased
   527  	amend := iceberg.Clone()
   528  	amend.Size = 150
   529  	amend.IcebergOrder.ReservedRemaining = 140
   530  	err := book.AmendOrder(iceberg, amend)
   531  	require.NoError(t, err)
   532  	assert.Equal(t, uint64(150), book.getTotalBuyVolume())
   533  
   534  	// amend iceberg such that the volume is decreased but not enough to eat into the peak
   535  	amend = iceberg.Clone()
   536  	amend.Size = 140
   537  	amend.IcebergOrder.ReservedRemaining = 130
   538  	err = book.AmendOrder(iceberg, amend)
   539  	require.NoError(t, err)
   540  	assert.Equal(t, uint64(140), book.getTotalBuyVolume())
   541  
   542  	// decrease again such that reserved is 0 and peak is reduced
   543  	amend = iceberg.Clone()
   544  	amend.Size = 5
   545  	amend.Remaining = 5
   546  	amend.IcebergOrder.ReservedRemaining = 0
   547  	err = book.AmendOrder(iceberg, amend)
   548  	require.NoError(t, err)
   549  	assert.Equal(t, uint64(5), book.getTotalBuyVolume())
   550  }
   551  
   552  func TestIcebergWashTradePassiveIceberg(t *testing.T) {
   553  	market := "testMarket"
   554  	book := getTestOrderBook(t, market)
   555  	defer book.Finish()
   556  
   557  	// submit an iceberg order that sits on the book
   558  	_, confirm := submitIcebergOrder(t, book, 100, 10, 8, true)
   559  	assert.Equal(t, 0, len(confirm.Trades))
   560  	assert.Equal(t, uint64(100), book.getTotalBuyVolume())
   561  
   562  	// same party submits and order which trades with themselves
   563  	o, confirm := submitCrossedWashOrder(t, book, 10)
   564  	assert.Equal(t, types.OrderStatusStopped, o.Status)
   565  	assert.Equal(t, 0, len(confirm.Trades))
   566  }
   567  
   568  func TestIcebergWashTradeAggressiveIceberg(t *testing.T) {
   569  	market := "testMarket"
   570  	book := getTestOrderBook(t, market)
   571  	defer book.Finish()
   572  
   573  	// party submits an order that sits on the book
   574  	_, confirm := submitCrossedWashOrder(t, book, 10)
   575  	assert.Equal(t, 0, len(confirm.Trades))
   576  	assert.Equal(t, uint64(10), book.getTotalSellVolume())
   577  
   578  	// same party submit an aggressive iceberg order that trades with themselves
   579  	iceberg, confirm := submitIcebergOrder(t, book, 100, 10, 8, true)
   580  	assert.Equal(t, types.OrderStatusStopped, iceberg.Status)
   581  	assert.Equal(t, 0, len(confirm.Trades))
   582  }
   583  
   584  func TestIcebergWashTradeAggressiveIcebergPartialFill(t *testing.T) {
   585  	market := "testMarket"
   586  	book := getTestOrderBook(t, market)
   587  	defer book.Finish()
   588  
   589  	_, confirm := submitCrossedOrder(t, book, 5)
   590  	assert.Equal(t, 0, len(confirm.Trades))
   591  	assert.Equal(t, uint64(5), book.getTotalSellVolume())
   592  
   593  	_, confirm = submitCrossedWashOrder(t, book, 10)
   594  	assert.Equal(t, 0, len(confirm.Trades))
   595  	assert.Equal(t, uint64(15), book.getTotalSellVolume())
   596  
   597  	// submit an iceberg order that partially trades, then causes a wash trade and so
   598  	// is not put on the book
   599  	iceberg, confirm := submitIcebergOrder(t, book, 100, 10, 8, true)
   600  	assert.Equal(t, types.OrderStatusPartiallyFilled, iceberg.Status)
   601  	assert.Equal(t, 1, len(confirm.Trades))
   602  	assert.Equal(t, uint64(10), book.getTotalSellVolume())
   603  }
   604  
   605  func TestRefreshedPeggedIcebergStillPegged(t *testing.T) {
   606  	market := "testMarket"
   607  	book := getTestOrderBook(t, market)
   608  	defer book.Finish()
   609  
   610  	// create a pegged iceberg order
   611  	iceberg, _ := submitPeggedIcebergOrder(t, book, 100, 4, 2)
   612  
   613  	// check it is returned as pegged
   614  	pegged := book.GetActivePeggedOrderIDs()
   615  	assert.Equal(t, iceberg.ID, pegged[0])
   616  
   617  	// submit an order that will trade a cause a refresh
   618  	_, confirm := submitCrossedOrder(t, book, 3)
   619  	assert.Equal(t, 1, len(confirm.Trades))
   620  
   621  	// check it is still returned as pegged
   622  	pegged = book.GetActivePeggedOrderIDs()
   623  	assert.Equal(t, iceberg.ID, pegged[0])
   624  }