code.vegaprotocol.io/vega@v0.79.0/core/execution/future/market_iceberg_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 future_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/events"
    24  	"code.vegaprotocol.io/vega/core/execution/common"
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	vegacontext "code.vegaprotocol.io/vega/libs/context"
    27  	vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
    28  	"code.vegaprotocol.io/vega/libs/num"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestMarketSubmitCancelIceberg(t *testing.T) {
    35  	party1 := "party1"
    36  	now := time.Unix(100000, 0)
    37  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
    38  	tm := getTestMarket(t, now, nil, nil)
    39  	defer tm.ctrl.Finish()
    40  
    41  	addAccount(t, tm, party1)
    42  	iceberg := &types.Order{
    43  		Type:        types.OrderTypeLimit,
    44  		TimeInForce: types.OrderTimeInForceGTC,
    45  		Status:      types.OrderStatusActive,
    46  		ID:          "someid",
    47  		Side:        types.SideBuy,
    48  		Party:       party1,
    49  		MarketID:    tm.market.GetID(),
    50  		Size:        100,
    51  		Price:       num.NewUint(100),
    52  		Remaining:   100,
    53  		CreatedAt:   now.UnixNano(),
    54  		Reference:   "party1-buy-order",
    55  		Version:     common.InitialOrderVersion,
    56  		IcebergOrder: &types.IcebergOrder{
    57  			PeakSize:           10,
    58  			MinimumVisibleSize: 5,
    59  		},
    60  	}
    61  
    62  	// submit order
    63  	_, err := tm.market.SubmitOrder(context.Background(), iceberg)
    64  	require.NoError(t, err)
    65  
    66  	tm.now = tm.now.Add(time.Second)
    67  	tm.market.OnTick(ctx, tm.now)
    68  	require.Equal(t, types.MarketStateActive, tm.market.State()) // enter auction
    69  
    70  	// check that its on the book and the volume is only the visible peak
    71  	assert.Equal(t, int64(100), tm.market.GetVolumeOnBook())
    72  
    73  	// and that the position represents the whole iceberg size
    74  	tm.market.BlockEnd(vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()))
    75  	pos := requirePositionUpdate(t, tm.events)
    76  	assert.Equal(t, int64(100), pos.PotentialBuys())
    77  
    78  	// now cancel the order and check potential buy returns to 0
    79  	tm.events = tm.events[:0]
    80  	_, err = tm.market.CancelOrder(context.Background(), iceberg.Party, iceberg.ID, iceberg.ID)
    81  	require.NoError(t, err)
    82  	tm.market.BlockEnd(vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()))
    83  	pos = requirePositionUpdate(t, tm.events)
    84  	assert.Equal(t, int64(0), pos.PotentialBuys())
    85  }
    86  
    87  func TestMarketAmendIceberg(t *testing.T) {
    88  	party1 := "party1"
    89  	now := time.Unix(100000, 0)
    90  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
    91  	tm := getTestMarket(t, now, nil, nil)
    92  	defer tm.ctrl.Finish()
    93  
    94  	addAccount(t, tm, party1)
    95  	iceberg := &types.Order{
    96  		Type:        types.OrderTypeLimit,
    97  		TimeInForce: types.OrderTimeInForceGTC,
    98  		Status:      types.OrderStatusActive,
    99  		ID:          "someid",
   100  		Side:        types.SideBuy,
   101  		Party:       party1,
   102  		MarketID:    tm.market.GetID(),
   103  		Size:        100,
   104  		Price:       num.NewUint(100),
   105  		Remaining:   100,
   106  		CreatedAt:   now.UnixNano(),
   107  		Reference:   "party1-buy-order",
   108  		Version:     common.InitialOrderVersion,
   109  		IcebergOrder: &types.IcebergOrder{
   110  			PeakSize:           10,
   111  			MinimumVisibleSize: 5,
   112  		},
   113  	}
   114  
   115  	// submit order
   116  	_, err := tm.market.SubmitOrder(context.Background(), iceberg)
   117  	require.NoError(t, err)
   118  
   119  	tm.now = tm.now.Add(time.Second)
   120  	tm.market.OnTick(ctx, tm.now)
   121  	require.Equal(t, types.MarketStateActive, tm.market.State()) // enter auction
   122  
   123  	// now reduce the size of the iceberg so that only the reserved amount is reduced
   124  	amendedOrder := &types.OrderAmendment{
   125  		OrderID:     iceberg.ID,
   126  		Price:       nil,
   127  		SizeDelta:   -50,
   128  		TimeInForce: types.OrderTimeInForceGTC,
   129  	}
   130  
   131  	tm.eventCount = 0
   132  	tm.events = tm.events[:0]
   133  	_, err = tm.market.AmendOrder(context.Background(), amendedOrder, party1, vgcrypto.RandomHash())
   134  	require.NoError(t, err)
   135  	amended := requireOrderEvent(t, tm.events)
   136  	assert.Equal(t, uint64(50), amended.Size)
   137  	assert.Equal(t, iceberg.Remaining, amended.Remaining)
   138  	assert.Equal(t, uint64(40), amended.IcebergOrder.ReservedRemaining)
   139  
   140  	// now increase the size delta and check that reserved remaining is increase, but remaining is the same
   141  	amendedOrder.SizeDelta = 70
   142  	tm.eventCount = 0
   143  	tm.events = tm.events[:0]
   144  	_, err = tm.market.AmendOrder(context.Background(), amendedOrder, party1, vgcrypto.RandomHash())
   145  	require.NoError(t, err)
   146  	amended = requireOrderEvent(t, tm.events)
   147  	assert.Equal(t, uint64(120), amended.Size)
   148  	assert.Equal(t, iceberg.Remaining, amended.Remaining)
   149  	assert.Equal(t, uint64(110), amended.IcebergOrder.ReservedRemaining)
   150  
   151  	// now reduce the size such that reserved is reduce to 0 and some remaining is removed too
   152  	amendedOrder.SizeDelta = -115
   153  	tm.eventCount = 0
   154  	tm.events = tm.events[:0]
   155  	_, err = tm.market.AmendOrder(context.Background(), amendedOrder, party1, vgcrypto.RandomHash())
   156  	require.NoError(t, err)
   157  	amended = requireOrderEvent(t, tm.events)
   158  	assert.Equal(t, uint64(5), amended.Size)
   159  	assert.Equal(t, uint64(5), amended.Remaining)
   160  	assert.Equal(t, uint64(0), amended.IcebergOrder.ReservedRemaining)
   161  }
   162  
   163  func TestMarketAmendIcebergToNoReserve(t *testing.T) {
   164  	party1 := "party1"
   165  	now := time.Unix(100000, 0)
   166  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
   167  	tm := getTestMarket(t, now, nil, nil)
   168  	defer tm.ctrl.Finish()
   169  
   170  	addAccount(t, tm, party1)
   171  	iceberg := &types.Order{
   172  		Type:        types.OrderTypeLimit,
   173  		TimeInForce: types.OrderTimeInForceGTC,
   174  		Status:      types.OrderStatusActive,
   175  		ID:          "someid",
   176  		Side:        types.SideBuy,
   177  		Party:       party1,
   178  		MarketID:    tm.market.GetID(),
   179  		Size:        100,
   180  		Price:       num.NewUint(100),
   181  		Remaining:   100,
   182  		CreatedAt:   now.UnixNano(),
   183  		Reference:   "party1-buy-order",
   184  		Version:     common.InitialOrderVersion,
   185  		IcebergOrder: &types.IcebergOrder{
   186  			PeakSize:           100,
   187  			MinimumVisibleSize: 5,
   188  		},
   189  	}
   190  
   191  	// submit order
   192  	_, err := tm.market.SubmitOrder(context.Background(), iceberg)
   193  	require.NoError(t, err)
   194  
   195  	tm.now = tm.now.Add(time.Second)
   196  	tm.market.OnTick(ctx, tm.now)
   197  	require.Equal(t, types.MarketStateActive, tm.market.State()) // enter auction
   198  
   199  	// now reduce the size of the iceberg so that only the reserved amount is reduced
   200  	amendedOrder := &types.OrderAmendment{
   201  		OrderID:     iceberg.ID,
   202  		Price:       nil,
   203  		SizeDelta:   -75,
   204  		TimeInForce: types.OrderTimeInForceGTC,
   205  	}
   206  
   207  	tm.eventCount = 0
   208  	tm.events = tm.events[:0]
   209  	_, err = tm.market.AmendOrder(context.Background(), amendedOrder, party1, vgcrypto.RandomHash())
   210  	require.NoError(t, err)
   211  	amended := requireOrderEvent(t, tm.events)
   212  	assert.Equal(t, uint64(25), amended.Size)
   213  	assert.Equal(t, uint64(25), amended.Remaining)
   214  	assert.Equal(t, uint64(0), amended.IcebergOrder.ReservedRemaining)
   215  }
   216  
   217  func requireOrderEvent(t *testing.T, evts []events.Event) *types.Order {
   218  	t.Helper()
   219  	for _, e := range evts {
   220  		switch evt := e.(type) {
   221  		case *events.Order:
   222  			o, err := types.OrderFromProto(evt.Order())
   223  			require.NoError(t, err)
   224  			return o
   225  		}
   226  	}
   227  	require.Fail(t, "did not find order event")
   228  	return nil
   229  }
   230  
   231  func requirePositionUpdate(t *testing.T, evts []events.Event) *events.PositionState {
   232  	t.Helper()
   233  	for _, e := range evts {
   234  		switch evt := e.(type) {
   235  		case *events.PositionState:
   236  			return evt
   237  		}
   238  	}
   239  	require.Fail(t, "did not find position update event")
   240  	return nil
   241  }