code.vegaprotocol.io/vega@v0.79.0/core/execution/amm/shape_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 amm
    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  	"code.vegaprotocol.io/vega/libs/ptr"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestOrderbookShape(t *testing.T) {
    31  	t.Run("test orderbook shape pending AMM", testOrderbookShapePendingAMM)
    32  	t.Run("test orderbook shape when AMM is 0", testOrderbookShapeZeroPosition)
    33  	t.Run("test orderbook shape when AMM is long", testOrderbookShapeLong)
    34  	t.Run("test orderbook shape when AMM is short", testOrderbookShapeShort)
    35  	t.Run("test orderbook shape when calculations are capped", testOrderbookShapeLimited)
    36  	t.Run("test orderbook shape step over fair price", testOrderbookShapeStepOverFairPrice)
    37  	t.Run("test orderbook shape step fair price at boundary", testOrderbookShapeNoStepOverFairPrice)
    38  	t.Run("test orderbook shape AMM reduce only", testOrderbookShapeReduceOnly)
    39  	t.Run("test orderbook shape boundary order when approx", testOrderbookShapeBoundaryOrder)
    40  	t.Run("test orderbook shape region not divisible by tick", testOrderbookSubTick)
    41  	t.Run("test orderbook shape closing pool close to base", testClosingCloseToBase)
    42  	t.Run("test orderbook shape point expansion at fair price", testPointExpansionAtFairPrice)
    43  }
    44  
    45  func testOrderbookShapePendingAMM(t *testing.T) {
    46  	p := newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), num.NewUint(13))
    47  	defer p.ctrl.Finish()
    48  
    49  	p.pool.status = types.AMMPoolStatusPending
    50  	low := p.submission.Parameters.LowerBound
    51  	high := p.submission.Parameters.UpperBound
    52  
    53  	r := p.pool.OrderbookShape(low, high, nil)
    54  	assert.Equal(t, 0, len(r.Buys))
    55  	assert.Equal(t, 0, len(r.Sells))
    56  }
    57  
    58  func testOrderbookShapeZeroPosition(t *testing.T) {
    59  	p := newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), num.NewUint(13))
    60  	defer p.ctrl.Finish()
    61  
    62  	low := p.submission.Parameters.LowerBound
    63  	base := p.submission.Parameters.Base
    64  	high := p.submission.Parameters.UpperBound
    65  
    66  	// when range [7, 10] expect orders at prices (7, 8, 9)
    67  	// there will be no order at price 10 since that is the pools fair-price and it quotes +/-1 eitherside
    68  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
    69  	r := p.pool.OrderbookShape(low, base, nil)
    70  
    71  	assertOrderPrices(t, r.Buys, types.SideBuy, 7, 9)
    72  	assert.Equal(t, 0, len(r.Sells))
    73  
    74  	// when range [7, 9] expect orders at prices (7, 8, 9)
    75  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
    76  	r = p.pool.OrderbookShape(low, num.NewUint(9), nil)
    77  	assertOrderPrices(t, r.Buys, types.SideBuy, 7, 9)
    78  	assert.Equal(t, 0, len(r.Sells))
    79  
    80  	// when range [10, 13] expect orders at prices (11, 12, 13)
    81  	// there will be no order at price 10 since that is the pools fair-price and it quotes +/-1 eitherside
    82  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
    83  	r = p.pool.OrderbookShape(base, high, nil)
    84  	assert.Equal(t, 0, len(r.Buys))
    85  	assertOrderPrices(t, r.Sells, types.SideSell, 11, 13)
    86  
    87  	// when range [11, 13] expect orders at prices (11, 12, 13)
    88  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
    89  	r = p.pool.OrderbookShape(num.NewUint(11), high, nil)
    90  	assert.Equal(t, 0, len(r.Buys))
    91  	assertOrderPrices(t, r.Sells, types.SideSell, 11, 13)
    92  
    93  	// whole range from [7, 10] will have buys (7, 8, 9) and sells (11, 12, 13)
    94  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
    95  	r = p.pool.OrderbookShape(low, high, nil)
    96  	assertOrderPrices(t, r.Buys, types.SideBuy, 7, 9)
    97  	assertOrderPrices(t, r.Sells, types.SideSell, 11, 13)
    98  
    99  	// mid both curves spanning buys and sells, range from [8, 12] will have buys (8, 9) and sells (11, 12)
   100  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   101  	r = p.pool.OrderbookShape(num.NewUint(8), num.NewUint(12), nil)
   102  	assertOrderPrices(t, r.Buys, types.SideBuy, 8, 9)
   103  	assertOrderPrices(t, r.Sells, types.SideSell, 11, 12)
   104  
   105  	// range (8, 8) should return a single buy order at price 8, which is a bit counter intuitive
   106  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   107  	r = p.pool.OrderbookShape(num.NewUint(8), num.NewUint(8), nil)
   108  	assertOrderPrices(t, r.Buys, types.SideBuy, 8, 8)
   109  	assert.Equal(t, 0, len(r.Sells))
   110  
   111  	// range (10, 10) should return only the orders at the fair-price, which is 0 orders
   112  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   113  	r = p.pool.OrderbookShape(num.NewUint(10), num.NewUint(10), nil)
   114  	assert.Equal(t, 0, len(r.Buys))
   115  	assert.Equal(t, 0, len(r.Sells))
   116  }
   117  
   118  func testOrderbookShapeLong(t *testing.T) {
   119  	p := newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), num.NewUint(13))
   120  	defer p.ctrl.Finish()
   121  
   122  	low := p.submission.Parameters.LowerBound
   123  	base := p.submission.Parameters.Base
   124  	high := p.submission.Parameters.UpperBound
   125  
   126  	// AMM is long and will have a fair-price of 8
   127  	position := int64(17980)
   128  	ensurePosition(t, p.pos, position, num.UintZero())
   129  	require.Equal(t, "8", p.pool.FairPrice().String())
   130  
   131  	// range [7, 10] with have buy order (7) and sell orders (9, 10)
   132  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   133  	r := p.pool.OrderbookShape(low, base, nil)
   134  	assertOrderPrices(t, r.Buys, types.SideBuy, 7, 7)
   135  	assertOrderPrices(t, r.Sells, types.SideSell, 9, 10)
   136  
   137  	// range [10, 13] with have sell orders (10, 11, 12, 13)
   138  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   139  	r = p.pool.OrderbookShape(base, high, nil)
   140  	assert.Equal(t, 0, len(r.Buys))
   141  	assertOrderPrices(t, r.Sells, types.SideSell, 10, 13)
   142  
   143  	// whole range will have buys at (7) and sells at (9, 10, 11, 12, 13)
   144  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   145  	r = p.pool.OrderbookShape(low, high, nil)
   146  	assertOrderPrices(t, r.Buys, types.SideBuy, 7, 7)
   147  	assertOrderPrices(t, r.Sells, types.SideSell, 9, 13)
   148  
   149  	// query at fair price returns no orders
   150  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   151  	r = p.pool.OrderbookShape(num.NewUint(8), num.NewUint(8), nil)
   152  	assert.Equal(t, 0, len(r.Buys))
   153  	assert.Equal(t, 0, len(r.Sells))
   154  }
   155  
   156  func testOrderbookShapeShort(t *testing.T) {
   157  	p := newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), num.NewUint(13))
   158  	defer p.ctrl.Finish()
   159  
   160  	low := p.submission.Parameters.LowerBound
   161  	base := p.submission.Parameters.Base
   162  	high := p.submission.Parameters.UpperBound
   163  
   164  	// AMM is short and will have a fair-price of 12
   165  	position := int64(-20000)
   166  	ensurePosition(t, p.pos, position, num.UintZero())
   167  	require.Equal(t, "12", p.pool.FairPrice().String())
   168  
   169  	// range [7, 10] with have buy order (7,8,9,10)
   170  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   171  	r := p.pool.OrderbookShape(low, base, nil)
   172  	assertOrderPrices(t, r.Buys, types.SideBuy, 7, 10)
   173  	assert.Equal(t, 0, len(r.Sells))
   174  
   175  	// range [10, 13] with have buy orders (10, 11) and sell orders (13)
   176  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   177  	r = p.pool.OrderbookShape(base, high, nil)
   178  	assertOrderPrices(t, r.Buys, types.SideBuy, 10, 11)
   179  	assertOrderPrices(t, r.Sells, types.SideSell, 13, 13)
   180  
   181  	// whole range will have buys at (7,8,9,10,11) and sells at (13)
   182  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   183  	r = p.pool.OrderbookShape(low, high, nil)
   184  	assertOrderPrices(t, r.Buys, types.SideBuy, 7, 11)
   185  	assertOrderPrices(t, r.Sells, types.SideSell, 13, 13)
   186  
   187  	// query at fair price returns no orders
   188  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   189  	r = p.pool.OrderbookShape(num.NewUint(12), num.NewUint(12), nil)
   190  	assert.Equal(t, 0, len(r.Buys))
   191  	assert.Equal(t, 0, len(r.Sells))
   192  }
   193  
   194  func testOrderbookShapeLimited(t *testing.T) {
   195  	p := newTestPoolWithRanges(t, num.NewUint(20), num.NewUint(40), num.NewUint(60))
   196  	defer p.ctrl.Finish()
   197  
   198  	low := p.submission.Parameters.LowerBound
   199  	base := p.submission.Parameters.Base
   200  	high := p.submission.Parameters.UpperBound
   201  
   202  	// position is zero but we're capping max calculations at ~10
   203  	position := int64(0)
   204  	p.pool.maxCalculationLevels = num.NewUint(10)
   205  
   206  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   207  	r := p.pool.OrderbookShape(low, base, nil)
   208  	assert.Equal(t, 11, len(r.Buys))
   209  	assert.Equal(t, 0, len(r.Sells))
   210  
   211  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   212  	r = p.pool.OrderbookShape(base, high, nil)
   213  	assert.Equal(t, 0, len(r.Buys))
   214  	assert.Equal(t, 11, len(r.Sells))
   215  
   216  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   217  	r = p.pool.OrderbookShape(low, high, nil)
   218  	assert.Equal(t, 6, len(r.Buys))
   219  	assert.Equal(t, 6, len(r.Sells))
   220  }
   221  
   222  func testOrderbookShapeStepOverFairPrice(t *testing.T) {
   223  	p := newTestPoolWithRanges(t, num.NewUint(20), num.NewUint(40), num.NewUint(60))
   224  	defer p.ctrl.Finish()
   225  
   226  	low := p.submission.Parameters.LowerBound
   227  	base := p.submission.Parameters.Base
   228  	high := p.submission.Parameters.UpperBound
   229  
   230  	// make levels of 10 makes the step price 2, and this position gives the pool a fair price of 25
   231  	// when we take the step from 24 -> 26 we want to make sure we split that order into two, so we
   232  	// will actually do maxCalculationLevels + 1 calculations but I think thats fine and keeps the calculations
   233  	// simple
   234  	position := int64(6000)
   235  	p.pool.maxCalculationLevels = num.NewUint(10)
   236  	ensurePosition(t, p.pos, position, num.UintZero())
   237  	require.Equal(t, "26", p.pool.FairPrice().String())
   238  
   239  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   240  	r := p.pool.OrderbookShape(low, base, nil)
   241  	assert.Equal(t, 4, len(r.Buys))
   242  	assert.Equal(t, 8, len(r.Sells))
   243  
   244  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   245  	r = p.pool.OrderbookShape(base, high, nil)
   246  	assert.Equal(t, 0, len(r.Buys))
   247  	assert.Equal(t, 12, len(r.Sells))
   248  
   249  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   250  	r = p.pool.OrderbookShape(low, high, nil)
   251  	assert.Equal(t, 3, len(r.Buys))
   252  	assert.Equal(t, 10, len(r.Sells))
   253  }
   254  
   255  func testOrderbookShapeNoStepOverFairPrice(t *testing.T) {
   256  	p := newTestPoolWithRanges(t, num.NewUint(20), num.NewUint(40), num.NewUint(60))
   257  	defer p.ctrl.Finish()
   258  
   259  	low := p.submission.Parameters.LowerBound
   260  	base := p.submission.Parameters.Base
   261  	high := p.submission.Parameters.UpperBound
   262  
   263  	position := int64(0)
   264  	p.pool.maxCalculationLevels = num.NewUint(6)
   265  
   266  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   267  	r := p.pool.OrderbookShape(low, base, nil)
   268  	assert.Equal(t, 7, len(r.Buys))
   269  	assert.Equal(t, 0, len(r.Sells))
   270  
   271  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   272  	r = p.pool.OrderbookShape(base, high, nil)
   273  	assert.Equal(t, 0, len(r.Buys))
   274  	assert.Equal(t, 7, len(r.Sells))
   275  
   276  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   277  	r = p.pool.OrderbookShape(low, high, nil)
   278  	assert.Equal(t, 4, len(r.Buys))
   279  	assert.Equal(t, 4, len(r.Sells))
   280  }
   281  
   282  func testOrderbookShapeReduceOnly(t *testing.T) {
   283  	p := newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), num.NewUint(13))
   284  	defer p.ctrl.Finish()
   285  
   286  	low := p.submission.Parameters.LowerBound
   287  	base := p.submission.Parameters.Base
   288  	high := p.submission.Parameters.UpperBound
   289  
   290  	// pool is reduce only so will not have any orders above/below fair price depending on position
   291  	p.pool.status = types.AMMPoolStatusReduceOnly
   292  
   293  	// AMM is position 0 it will have no orders
   294  	position := int64(0)
   295  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   296  	r := p.pool.OrderbookShape(low, base, nil)
   297  	assert.Equal(t, 0, len(r.Buys))
   298  	assert.Equal(t, 0, len(r.Sells))
   299  
   300  	// AMM is long and will have a fair-price of 8 and so will only have orders from 8 -> base
   301  	position = int64(17980)
   302  	ensurePosition(t, p.pos, position, num.UintZero())
   303  	require.Equal(t, "8", p.pool.FairPrice().String())
   304  
   305  	// range [7, 13] will have only sellf orders (9, 10)
   306  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   307  	r = p.pool.OrderbookShape(low, high, nil)
   308  	assert.Equal(t, 0, len(r.Buys))
   309  	assertOrderPrices(t, r.Sells, types.SideSell, 9, 10)
   310  
   311  	// AMM is short and will have a fair-price of 12
   312  	position = int64(-20000)
   313  	ensurePosition(t, p.pos, position, num.UintZero())
   314  	require.Equal(t, "12", p.pool.FairPrice().String())
   315  
   316  	// range [10, 13] with have buy orders (10, 11)
   317  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   318  	r = p.pool.OrderbookShape(base, high, nil)
   319  	assertOrderPrices(t, r.Buys, types.SideBuy, 10, 11)
   320  	assert.Equal(t, 0, len(r.Sells))
   321  }
   322  
   323  func testOrderbookShapeBoundaryOrder(t *testing.T) {
   324  	p := newTestPoolWithRanges(t, num.NewUint(100), num.NewUint(200), num.NewUint(300))
   325  	defer p.ctrl.Finish()
   326  
   327  	midlow := num.NewUint(150)
   328  	midhigh := num.NewUint(250)
   329  
   330  	position := int64(0)
   331  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   332  
   333  	// limit the number of orders in the expansion
   334  	p.pool.maxCalculationLevels = num.NewUint(5)
   335  
   336  	r := p.pool.OrderbookShape(midlow, midhigh, nil)
   337  	assert.Equal(t, 4, len(r.Buys))
   338  	assert.Equal(t, 4, len(r.Sells))
   339  
   340  	// we're in approximate mode but we still require an exact order at the boundaries of the shape range
   341  	// check that the price for the first by is midlow, and the last sell is midhigh
   342  	assert.Equal(t, midlow.String(), r.Buys[0].Price.String())
   343  	assert.Equal(t, midhigh.String(), r.Sells[(len(r.Sells)-1)].Price.String())
   344  }
   345  
   346  func testOrderbookSubTick(t *testing.T) {
   347  	p := newTestPoolWithSubmission(t, num.DecimalFromFloat(1), num.DecimalFromFloat(100),
   348  		&types.SubmitAMM{
   349  			CommitmentAmount: num.NewUint(10000000),
   350  			Parameters: &types.ConcentratedLiquidityParameters{
   351  				LowerBound: num.NewUint(10),
   352  				Base:       num.NewUint(15),
   353  				UpperBound: num.NewUint(20),
   354  			},
   355  		},
   356  		0,
   357  	)
   358  
   359  	defer p.ctrl.Finish()
   360  
   361  	// limit the number of orders in the expansion
   362  	p.pool.maxCalculationLevels = num.NewUint(1000)
   363  
   364  	position := int64(1000)
   365  	ensurePositionN(t, p.pos, position, num.UintZero(), 4)
   366  
   367  	// fair-price should be 1483, and so best buy should be 1383 (fair-price minus one-tick)
   368  	bp, _ := p.pool.BestPrice(types.SideBuy)
   369  	require.Equal(t, "1383", bp.String())
   370  
   371  	// now pretend we are in auction and we have a sell order at 1000, so we need to expand the crossed
   372  	// region of 1000 -> 1383
   373  	from := num.NewUint(1000)
   374  	to := num.NewUint(1383)
   375  	r := p.pool.OrderbookShape(from, to, nil)
   376  
   377  	assert.Equal(t, 4, len(r.Buys))
   378  	assert.Equal(t, bp.String(), r.Buys[3].Price.String())
   379  
   380  	assert.Equal(t, 0, len(r.Sells))
   381  }
   382  
   383  func testClosingCloseToBase(t *testing.T) {
   384  	p := newTestPoolWithSubmission(t, num.DecimalFromFloat(1), num.DecimalFromFloat(100),
   385  		&types.SubmitAMM{
   386  			CommitmentAmount: num.NewUint(10000000),
   387  			Parameters: &types.ConcentratedLiquidityParameters{
   388  				LowerBound: num.NewUint(10),
   389  				Base:       num.NewUint(15),
   390  				UpperBound: num.NewUint(20),
   391  			},
   392  		},
   393  		0,
   394  	)
   395  
   396  	defer p.ctrl.Finish()
   397  
   398  	// its reducing
   399  	p.pool.status = types.AMMPoolStatusReduceOnly
   400  
   401  	// and it is long one
   402  	position := int64(1)
   403  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   404  
   405  	// now pretend we are in auction and we have a sell order at 1000, so we need to expand the crossed
   406  	// region of 1000 -> 1383
   407  	from := num.NewUint(1000)
   408  	to := num.NewUint(2000)
   409  	r := p.pool.OrderbookShape(from, to, nil)
   410  
   411  	// should have one sell of volume 1
   412  	assert.Equal(t, 0, len(r.Buys))
   413  	assert.Equal(t, 1, len(r.Sells))
   414  	assert.Equal(t, 1, int(r.Sells[0].Size))
   415  	assert.Equal(t, "14", r.Sells[0].OriginalPrice.String())
   416  
   417  	// and it is short one
   418  	position = int64(-1)
   419  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   420  
   421  	r = p.pool.OrderbookShape(from, to, nil)
   422  
   423  	// should have one sell of volume 1
   424  	assert.Equal(t, 1, len(r.Buys))
   425  	assert.Equal(t, 0, len(r.Sells))
   426  	assert.Equal(t, 1, int(r.Buys[0].Size))
   427  	assert.Equal(t, "16", r.Buys[0].OriginalPrice.String())
   428  
   429  	// no position
   430  	position = int64(0)
   431  	ensurePositionN(t, p.pos, position, num.UintZero(), 2)
   432  
   433  	r = p.pool.OrderbookShape(from, to, nil)
   434  
   435  	// should have one sell of volume 1
   436  	assert.Equal(t, 0, len(r.Buys))
   437  	assert.Equal(t, 0, len(r.Sells))
   438  }
   439  
   440  func testPointExpansionAtFairPrice(t *testing.T) {
   441  	p := newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), num.NewUint(13))
   442  	defer p.ctrl.Finish()
   443  
   444  	base := p.submission.Parameters.Base
   445  
   446  	// range [10, 10] fair price is 10, no orders
   447  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   448  	r := p.pool.OrderbookShape(base, base, nil)
   449  	assert.Equal(t, 0, len(r.Buys))
   450  	assert.Equal(t, 0, len(r.Sells))
   451  
   452  	// now try with a one sided curve where the input range shrinks to a point-expansion
   453  	p = newTestPoolWithRanges(t, num.NewUint(7), num.NewUint(10), nil)
   454  	defer p.ctrl.Finish()
   455  
   456  	// range [10, 1000] but sell curve is empty so effective range is [10, 10] at fair-price
   457  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   458  	r = p.pool.OrderbookShape(base, num.NewUint(1000), nil)
   459  	assert.Equal(t, 0, len(r.Buys))
   460  	assert.Equal(t, 0, len(r.Sells))
   461  
   462  	// now try with a one sided curve where the input range shrinks to a point-expansion
   463  	p = newTestPoolWithRanges(t, nil, num.NewUint(10), num.NewUint(13))
   464  	defer p.ctrl.Finish()
   465  
   466  	// range [1, 10] but buy curve is empty so effective range is [10, 10] at fair-price
   467  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   468  	r = p.pool.OrderbookShape(num.NewUint(1), base, nil)
   469  	assert.Equal(t, 0, len(r.Buys))
   470  	assert.Equal(t, 0, len(r.Sells))
   471  }
   472  
   473  func TestOrderbookShapeSparseAMM(t *testing.T) {
   474  	p := newTestPoolWithOpts(t, num.DecimalOne(), num.NewUint(99), num.NewUint(198), num.NewUint(297), num.NewUint(1000), 100)
   475  
   476  	defer p.ctrl.Finish()
   477  
   478  	low := p.submission.Parameters.LowerBound
   479  	base := p.submission.Parameters.Base
   480  	high := p.submission.Parameters.UpperBound
   481  
   482  	// when range [99, 198] expect orders at ticks of 9 from 99 -> 189
   483  	// there will be no order at price 200 since that is the pools fair-price and it quotes +/-1 eitherside
   484  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   485  	r := p.pool.OrderbookShape(low, base, nil)
   486  	assert.Equal(t, 14, len(r.Buys))
   487  	assert.Equal(t, 0, len(r.Sells))
   488  
   489  	// check boundary orders
   490  	assert.Equal(t, "99", r.Buys[0].Price.String())
   491  	assert.Equal(t, "191", r.Buys[(len(r.Buys)-1)].Price.String())
   492  
   493  	// when range [99, 191] expect orders at prices 100 -> 191
   494  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   495  	r = p.pool.OrderbookShape(low, num.NewUint(189), nil)
   496  	assert.Equal(t, 14, len(r.Buys))
   497  	assert.Equal(t, 0, len(r.Sells))
   498  
   499  	// check boundary orders
   500  	assert.Equal(t, "99", r.Buys[0].Price.String())
   501  	assert.Equal(t, "189", r.Buys[(len(r.Buys)-1)].Price.String())
   502  
   503  	// when range [198, 297] expect orders at prices 207 -> 297
   504  	// there will be no order at price 198 since that is the pools fair-price and it quotes +/-1 eitherside
   505  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   506  	r = p.pool.OrderbookShape(base, high, nil)
   507  	assert.Equal(t, 0, len(r.Buys))
   508  	assert.Equal(t, 12, len(r.Sells))
   509  
   510  	// check boundary orders
   511  	assert.Equal(t, "203", r.Sells[0].Price.String())
   512  	assert.Equal(t, "297", r.Sells[(len(r.Sells)-1)].Price.String())
   513  
   514  	// when range [207, 297] expect orders at prices 207 -> 297
   515  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   516  	r = p.pool.OrderbookShape(num.NewUint(207), high, nil)
   517  	assert.Equal(t, 0, len(r.Buys))
   518  	assert.Equal(t, 11, len(r.Sells))
   519  
   520  	// check boundary orders
   521  	assert.Equal(t, "207", r.Sells[0].Price.String())
   522  	assert.Equal(t, "297", r.Sells[(len(r.Sells)-1)].Price.String())
   523  
   524  	// range (8, 8) should return a single buy order at price 8, which is a bit counter intuitive
   525  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   526  	r = p.pool.OrderbookShape(num.NewUint(117), num.NewUint(117), nil)
   527  	assertOrderPrices(t, r.Buys, types.SideBuy, 117, 117)
   528  	assert.Equal(t, 0, len(r.Sells))
   529  
   530  	// range (10, 10) should return only the orders at the fair-price, which is 0 orders
   531  	ensurePositionN(t, p.pos, 0, num.UintZero(), 2)
   532  	r = p.pool.OrderbookShape(num.NewUint(198), num.NewUint(198), nil)
   533  	assert.Equal(t, 0, len(r.Buys))
   534  	assert.Equal(t, 0, len(r.Sells))
   535  }
   536  
   537  func TestOrderbookShapeSparseAMMBoundaryOrders(t *testing.T) {
   538  	submit := &types.SubmitAMM{
   539  		AMMBaseCommand: types.AMMBaseCommand{
   540  			Party:             vgcrypto.RandomHash(),
   541  			MarketID:          vgcrypto.RandomHash(),
   542  			SlippageTolerance: num.DecimalFromFloat(0.1),
   543  		},
   544  		CommitmentAmount: num.MustUintFromString("5960289358452040000000", 10),
   545  		Parameters: &types.ConcentratedLiquidityParameters{
   546  			Base:                 num.NewUint(195836),
   547  			LowerBound:           num.NewUint(92079),
   548  			UpperBound:           num.NewUint(246109),
   549  			LeverageAtLowerBound: ptr.From(num.DecimalFromFloat(95.13295934001242)),
   550  			LeverageAtUpperBound: ptr.From(num.DecimalFromFloat(86.1204552842962)),
   551  		},
   552  	}
   553  	p := newTestPoolWithSubmission(t, num.DecimalFromInt64(1000), num.DecimalFromFloat(10000000000000000), submit, 100)
   554  	defer p.ctrl.Finish()
   555  
   556  	ensurePositionN(t, p.pos, 0, nil, -1)
   557  	r := p.pool.OrderbookShape(
   558  		num.MustUintFromString("1958381716019393098944", 10),
   559  		num.MustUintFromString("4897350000000000000000", 10),
   560  		nil,
   561  	)
   562  	assert.Equal(t, 0, len(r.Buys))
   563  	require.Equal(t, "1958381716019393098944", r.Sells[0].Price.String())
   564  	require.Equal(t, "2461090000000000000000", r.Sells[len(r.Sells)-1].Price.String())
   565  }