code.vegaprotocol.io/vega@v0.79.0/core/execution/future/market_state_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/execution/common"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	vegacontext "code.vegaprotocol.io/vega/libs/context"
    26  	vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestMarketStates(t *testing.T) {
    35  	t.Run("test initial state is PROPOSED", testInitialStateIsProposed)
    36  	t.Run("cannot do order stuff in PROPOSED state", testCannotDoOrderStuffInProposedState)
    37  	t.Run("can move from PROPOSED to REJECTED state", testCanMoveFromProposedToRejectedState)
    38  	t.Run("can move from PROPOSED to PENDING state", testCanMoveFromProposedToPendingState)
    39  	t.Run("can move from PENDING to ACTIVE state", testCanMoveFromPendingToActiveState)
    40  	t.Run("can place order in PENDING state", testCanPlaceOrderInActiveState)
    41  }
    42  
    43  func testInitialStateIsProposed(t *testing.T) {
    44  	now := time.Unix(10, 0)
    45  	auctionDuration := &types.AuctionDuration{
    46  		Duration: 30, // seconds
    47  	}
    48  	tm := getTestMarket2(t, now, nil, auctionDuration, false, 0.99)
    49  	defer tm.ctrl.Finish()
    50  
    51  	assert.Equal(t, types.MarketStateProposed, tm.market.State())
    52  }
    53  
    54  func testCannotDoOrderStuffInProposedState(t *testing.T) {
    55  	now := time.Unix(10, 0)
    56  	auctionDuration := &types.AuctionDuration{
    57  		Duration: 30, // seconds
    58  	}
    59  	ctx := context.Background()
    60  
    61  	tm := getTestMarket2(t, now, nil, auctionDuration, false, 0.99)
    62  	defer tm.ctrl.Finish()
    63  	assert.Equal(t, types.MarketStateProposed, tm.market.State())
    64  
    65  	addAccountWithAmount(tm, "someparty", 100000000)
    66  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
    67  
    68  	// expect error
    69  	o1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order01", types.SideBuy, "party-A", 5, 5000)
    70  	o1conf, err := tm.market.SubmitOrder(ctx, o1)
    71  	assert.Nil(t, o1conf)
    72  	assert.EqualError(t, err, common.ErrTradingNotAllowed.Error())
    73  
    74  	o2conf, err := tm.market.CancelAllOrders(ctx, "someparty")
    75  	assert.Nil(t, o2conf)
    76  	assert.EqualError(t, err, common.ErrTradingNotAllowed.Error())
    77  
    78  	o3conf, err := tm.market.CancelOrder(ctx, "someparty", "someorder", vgcrypto.RandomHash())
    79  	assert.Nil(t, o3conf)
    80  	assert.EqualError(t, err, common.ErrTradingNotAllowed.Error())
    81  
    82  	amendment := &types.OrderAmendment{
    83  		OrderID:   o1.ID,
    84  		Price:     num.NewUint(4000),
    85  		SizeDelta: 10,
    86  	}
    87  
    88  	amendConf, err := tm.market.AmendOrder(ctx, amendment, "party-A", vgcrypto.RandomHash())
    89  	assert.Nil(t, amendConf)
    90  	assert.EqualError(t, err, common.ErrTradingNotAllowed.Error())
    91  
    92  	// but can place liquidity submission
    93  	lpsub := &types.LiquidityProvisionSubmission{
    94  		MarketID:         tm.market.GetID(),
    95  		CommitmentAmount: num.NewUint(1),
    96  		Fee:              num.DecimalFromFloat(0.1),
    97  	}
    98  
    99  	err = tm.market.SubmitLiquidityProvision(ctx, lpsub, "someparty", vgcrypto.RandomHash())
   100  
   101  	// we expect an error as this lp may be stupid
   102  	// but not equal to the trading not allowed one
   103  	assert.NoError(t, err)
   104  }
   105  
   106  func testCanMoveFromProposedToRejectedState(t *testing.T) {
   107  	now := time.Unix(10, 0)
   108  	auctionDuration := &types.AuctionDuration{
   109  		Duration: 30, // seconds
   110  	}
   111  	tm := getTestMarket2(t, now, nil, auctionDuration, false, 0.99)
   112  	defer tm.ctrl.Finish()
   113  
   114  	assert.Equal(t, types.MarketStateProposed, tm.market.State())
   115  
   116  	err := tm.market.Reject(context.Background())
   117  	assert.NoError(t, err)
   118  	assert.Equal(t, types.MarketStateRejected, tm.market.State())
   119  }
   120  
   121  func testCanMoveFromProposedToPendingState(t *testing.T) {
   122  	now := time.Unix(10, 0)
   123  	auctionDuration := &types.AuctionDuration{
   124  		Duration: 30, // seconds
   125  	}
   126  	tm := getTestMarket2(t, now, nil, auctionDuration, false, 0.99)
   127  	defer tm.ctrl.Finish()
   128  
   129  	assert.Equal(t, types.MarketStateProposed, tm.market.State())
   130  
   131  	err := tm.market.StartOpeningAuction(context.Background())
   132  	assert.NoError(t, err)
   133  	assert.Equal(t, types.MarketStatePending, tm.market.State())
   134  }
   135  
   136  func testCanMoveFromPendingToActiveState(t *testing.T) {
   137  	now := time.Unix(10, 0)
   138  	auctionDuration := &types.AuctionDuration{
   139  		Duration: 30, // seconds
   140  	}
   141  	tm := getTestMarket2(t, now, nil, auctionDuration, false, 0.99)
   142  	defer tm.ctrl.Finish()
   143  
   144  	assert.Equal(t, types.MarketStateProposed, tm.market.State())
   145  
   146  	err := tm.market.StartOpeningAuction(context.Background())
   147  	assert.NoError(t, err)
   148  	assert.Equal(t, types.MarketStatePending, tm.market.State())
   149  
   150  	addAccountWithAmount(tm, "party1", 100000000)
   151  	addAccountWithAmount(tm, "party2", 100000000)
   152  	addAccountWithAmount(tm, "party3", 100000000)
   153  	addAccountWithAmount(tm, "party4", 100000000)
   154  	addAccountWithAmount(tm, "lpprov", 100000000)
   155  	orders := []*types.Order{
   156  		getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "order1", types.SideBuy, "party1", 1, 5000),
   157  		getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "order2", types.SideSell, "party2", 1, 5000),
   158  		getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "order3", types.SideBuy, "party3", 1, 4500),  // buy too low
   159  		getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "order4", types.SideSell, "party4", 1, 5500), // sell too expensive
   160  	}
   161  	for _, o := range orders {
   162  		conf, err := tm.market.SubmitOrder(context.Background(), o)
   163  		assert.NotNil(t, conf)
   164  		assert.NoError(t, err)
   165  	}
   166  	lp := &types.LiquidityProvisionSubmission{
   167  		MarketID:         tm.market.GetID(),
   168  		CommitmentAmount: num.NewUint(15000),
   169  		Fee:              num.DecimalFromFloat(0.01),
   170  	}
   171  	require.NoError(t, tm.market.SubmitLiquidityProvision(context.Background(), lp, "lpprov", vgcrypto.RandomHash()))
   172  	// now move to after the opening auction time
   173  	now = now.Add(40 * time.Second)
   174  	tm.now = now
   175  	tm.market.OnTick(vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()), now)
   176  	assert.Equal(t, types.MarketStateActive, tm.market.State())
   177  }
   178  
   179  func testCanPlaceOrderInActiveState(t *testing.T) {
   180  	now := time.Unix(10, 0)
   181  	auctionDuration := &types.AuctionDuration{
   182  		Duration: 30, // seconds
   183  	}
   184  	tm := getTestMarket2(t, now, nil, auctionDuration, false, 0.99)
   185  	defer tm.ctrl.Finish()
   186  
   187  	assert.Equal(t, types.MarketStateProposed, tm.market.State())
   188  
   189  	err := tm.market.StartOpeningAuction(context.Background())
   190  	assert.NoError(t, err)
   191  	assert.Equal(t, types.MarketStatePending, tm.market.State())
   192  
   193  	addAccountWithAmount(tm, "party1", 100000000)
   194  	addAccountWithAmount(tm, "party2", 100000000)
   195  	addAccountWithAmount(tm, "party3", 100000000)
   196  	addAccountWithAmount(tm, "party4", 100000000)
   197  	addAccountWithAmount(tm, "lpprov", 100000000)
   198  	orders := []*types.Order{
   199  		getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "order1", types.SideBuy, "party1", 1, 5000),
   200  		getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "order2", types.SideSell, "party2", 1, 5000),
   201  		getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "order3", types.SideBuy, "party3", 1, 4500),  // buy too low
   202  		getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "order4", types.SideSell, "party4", 1, 5500), // sell too expensive
   203  	}
   204  	for _, o := range orders {
   205  		conf, err := tm.market.SubmitOrder(context.Background(), o)
   206  		assert.NotNil(t, conf)
   207  		assert.NoError(t, err)
   208  	}
   209  	lp := &types.LiquidityProvisionSubmission{
   210  		MarketID:         tm.market.GetID(),
   211  		CommitmentAmount: num.NewUint(15000),
   212  		Fee:              num.DecimalFromFloat(0.01),
   213  	}
   214  	require.NoError(t, tm.market.SubmitLiquidityProvision(context.Background(), lp, "lpprov", vgcrypto.RandomHash()))
   215  	// now move to after the opening auction time
   216  	now = now.Add(40 * time.Second)
   217  	tm.now = now
   218  	tm.market.OnTick(vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()), now)
   219  	assert.Equal(t, types.MarketStateActive, tm.market.State())
   220  
   221  	addAccountWithAmount(tm, "someparty", 100000000)
   222  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   223  
   224  	// expect error
   225  	o1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order01", types.SideBuy, "someparty", 5, 5000)
   226  	o1conf, err := tm.market.SubmitOrder(context.Background(), o1)
   227  	assert.NotNil(t, o1conf)
   228  	assert.NoError(t, err)
   229  }