code.vegaprotocol.io/vega@v0.79.0/core/execution/spot/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 spot_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  	"code.vegaprotocol.io/vega/libs/crypto"
    28  	vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
    29  	"code.vegaprotocol.io/vega/libs/num"
    30  
    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 := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now)
    39  	defer tm.ctrl.Finish()
    40  	tm.market.StartOpeningAuction(ctx)
    41  
    42  	addAccountWithAmount(tm, party1, 10000000, tm.quoteAsset)
    43  	iceberg := &types.Order{
    44  		Type:        types.OrderTypeLimit,
    45  		TimeInForce: types.OrderTimeInForceGTC,
    46  		Status:      types.OrderStatusActive,
    47  		ID:          "someid",
    48  		Side:        types.SideBuy,
    49  		Party:       party1,
    50  		MarketID:    tm.market.GetID(),
    51  		Size:        100,
    52  		Price:       num.NewUint(10),
    53  		Remaining:   100,
    54  		CreatedAt:   now.UnixNano(),
    55  		Reference:   "party1-buy-order",
    56  		Version:     common.InitialOrderVersion,
    57  		IcebergOrder: &types.IcebergOrder{
    58  			PeakSize:           10,
    59  			MinimumVisibleSize: 5,
    60  		},
    61  	}
    62  
    63  	// submit order
    64  	oid := crypto.RandomHash()
    65  	_, err := tm.market.SubmitOrder(context.Background(), iceberg.IntoSubmission(), party1, oid)
    66  	require.NoError(t, err)
    67  
    68  	tm.now = tm.now.Add(time.Second)
    69  	tm.market.OnTick(ctx, tm.now)
    70  	require.Equal(t, types.MarketStatePending, tm.market.GetMarketState())
    71  	haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset)
    72  	require.NoError(t, err)
    73  	require.Equal(t, "1000", haBalance1.Balance.String())
    74  
    75  	// now cancel the order and check potential buy returns to 0
    76  	tm.events = tm.events[:0]
    77  	_, err = tm.market.CancelOrder(context.Background(), iceberg.Party, oid, oid)
    78  	require.NoError(t, err)
    79  	tm.market.BlockEnd(vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()))
    80  
    81  	haBalance2, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset)
    82  	require.NoError(t, err)
    83  	require.Equal(t, "0", haBalance2.Balance.String())
    84  }
    85  
    86  func TestMarketAmendIceberg(t *testing.T) {
    87  	party1 := "party1"
    88  	now := time.Unix(100000, 0)
    89  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
    90  	tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now)
    91  	defer tm.ctrl.Finish()
    92  	tm.market.StartOpeningAuction(ctx)
    93  
    94  	addAccountWithAmount(tm, party1, 10000000, tm.quoteAsset)
    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  	oid := crypto.RandomHash()
   117  	_, err := tm.market.SubmitOrder(context.Background(), iceberg.IntoSubmission(), party1, oid)
   118  	require.NoError(t, err)
   119  
   120  	tm.now = tm.now.Add(time.Second)
   121  	tm.market.OnTick(ctx, tm.now)
   122  	require.Equal(t, types.MarketStatePending, tm.market.GetMarketState())
   123  
   124  	haBalance1, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset)
   125  	require.NoError(t, err)
   126  	require.Equal(t, "10000", haBalance1.Balance.String())
   127  
   128  	gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset)
   129  	require.NoError(t, err)
   130  	require.Equal(t, "9990000", gaBalance1.Balance.String())
   131  
   132  	// now reduce the size of the iceberg so that only the reserved amount is reduced
   133  	amendedOrder := &types.OrderAmendment{
   134  		OrderID:     oid,
   135  		Price:       nil,
   136  		SizeDelta:   -50,
   137  		TimeInForce: types.OrderTimeInForceGTC,
   138  	}
   139  
   140  	tm.eventCount = 0
   141  	tm.events = tm.events[:0]
   142  	_, err = tm.market.AmendOrder(context.Background(), amendedOrder, party1, vgcrypto.RandomHash())
   143  	require.NoError(t, err)
   144  
   145  	haBalance2, err := tm.collateralEngine.GetPartyHoldingAccount("party1", tm.quoteAsset)
   146  	require.NoError(t, err)
   147  	require.Equal(t, "5000", haBalance2.Balance.String())
   148  
   149  	gaBalance2, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset)
   150  	require.NoError(t, err)
   151  	require.Equal(t, "9995000", gaBalance2.Balance.String())
   152  
   153  	amended := requireOrderEvent(t, tm.events)
   154  	require.Equal(t, uint64(50), amended.Size)
   155  	require.Equal(t, uint64(10), amended.Remaining)
   156  	require.Equal(t, uint64(40), amended.IcebergOrder.ReservedRemaining)
   157  
   158  	// now increase the size delta and check that reserved remaining is increased, but remaining is the same
   159  	amendedOrder.SizeDelta = 70
   160  	tm.eventCount = 0
   161  	tm.events = tm.events[:0]
   162  	_, err = tm.market.AmendOrder(context.Background(), amendedOrder, party1, vgcrypto.RandomHash())
   163  	require.NoError(t, err)
   164  	amended = requireOrderEvent(t, tm.events)
   165  	require.Equal(t, uint64(120), amended.Size)
   166  	require.Equal(t, uint64(10), amended.Remaining)
   167  	require.Equal(t, uint64(110), amended.IcebergOrder.ReservedRemaining)
   168  
   169  	// now reduce the size such that reserved is reduce to 0 and some remaining is removed too
   170  	amendedOrder.SizeDelta = -115
   171  	tm.eventCount = 0
   172  	tm.events = tm.events[:0]
   173  	_, err = tm.market.AmendOrder(context.Background(), amendedOrder, party1, vgcrypto.RandomHash())
   174  	require.NoError(t, err)
   175  	amended = requireOrderEvent(t, tm.events)
   176  	require.Equal(t, uint64(5), amended.Size)
   177  	require.Equal(t, uint64(5), amended.Remaining)
   178  	require.Equal(t, uint64(0), amended.IcebergOrder.ReservedRemaining)
   179  }
   180  
   181  func TestMarketAmendIcebergToNoReserve(t *testing.T) {
   182  	party1 := "party1"
   183  	now := time.Unix(100000, 0)
   184  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
   185  	tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now)
   186  	defer tm.ctrl.Finish()
   187  	tm.market.StartOpeningAuction(ctx)
   188  
   189  	addAccountWithAmount(tm, party1, 10000000, tm.quoteAsset)
   190  
   191  	iceberg := &types.Order{
   192  		Type:        types.OrderTypeLimit,
   193  		TimeInForce: types.OrderTimeInForceGTC,
   194  		Status:      types.OrderStatusActive,
   195  		ID:          "someid",
   196  		Side:        types.SideBuy,
   197  		Party:       party1,
   198  		MarketID:    tm.market.GetID(),
   199  		Size:        100,
   200  		Price:       num.NewUint(100),
   201  		Remaining:   100,
   202  		CreatedAt:   now.UnixNano(),
   203  		Reference:   "party1-buy-order",
   204  		Version:     common.InitialOrderVersion,
   205  		IcebergOrder: &types.IcebergOrder{
   206  			PeakSize:           100,
   207  			MinimumVisibleSize: 5,
   208  		},
   209  	}
   210  
   211  	// submit order
   212  	oid := crypto.RandomHash()
   213  	_, err := tm.market.SubmitOrder(context.Background(), iceberg.IntoSubmission(), "party1", oid)
   214  	require.NoError(t, err)
   215  
   216  	tm.now = tm.now.Add(time.Second)
   217  	tm.market.OnTick(ctx, tm.now)
   218  	require.Equal(t, types.MarketStatePending, tm.market.GetMarketState())
   219  
   220  	// now reduce the size of the iceberg so that only the reserved amount is reduced
   221  	amendedOrder := &types.OrderAmendment{
   222  		OrderID:     oid,
   223  		Price:       nil,
   224  		SizeDelta:   -75,
   225  		TimeInForce: types.OrderTimeInForceGTC,
   226  	}
   227  
   228  	tm.eventCount = 0
   229  	tm.events = tm.events[:0]
   230  	_, err = tm.market.AmendOrder(context.Background(), amendedOrder, party1, vgcrypto.RandomHash())
   231  	require.NoError(t, err)
   232  	amended := requireOrderEvent(t, tm.events)
   233  	require.Equal(t, uint64(25), amended.Size)
   234  	require.Equal(t, uint64(25), amended.Remaining)
   235  	require.Equal(t, uint64(0), amended.IcebergOrder.ReservedRemaining)
   236  }
   237  
   238  func requireOrderEvent(t *testing.T, evts []events.Event) *types.Order {
   239  	t.Helper()
   240  	for _, e := range evts {
   241  		switch evt := e.(type) {
   242  		case *events.Order:
   243  			o, err := types.OrderFromProto(evt.Order())
   244  			require.NoError(t, err)
   245  			return o
   246  		}
   247  	}
   248  	require.Fail(t, "did not find order event")
   249  	return nil
   250  }