code.vegaprotocol.io/vega@v0.79.0/core/execution/future/market_snapshot_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  	"errors"
    21  	"testing"
    22  	"time"
    23  
    24  	bmocks "code.vegaprotocol.io/vega/core/broker/mocks"
    25  	"code.vegaprotocol.io/vega/core/collateral"
    26  	dstypes "code.vegaprotocol.io/vega/core/datasource/common"
    27  	"code.vegaprotocol.io/vega/core/datasource/spec"
    28  	"code.vegaprotocol.io/vega/core/execution/common"
    29  	"code.vegaprotocol.io/vega/core/execution/common/mocks"
    30  	"code.vegaprotocol.io/vega/core/execution/future"
    31  	"code.vegaprotocol.io/vega/core/fee"
    32  	fmock "code.vegaprotocol.io/vega/core/fee/mocks"
    33  	"code.vegaprotocol.io/vega/core/integration/stubs"
    34  	"code.vegaprotocol.io/vega/core/liquidity/v2"
    35  	"code.vegaprotocol.io/vega/core/matching"
    36  	"code.vegaprotocol.io/vega/core/positions"
    37  	"code.vegaprotocol.io/vega/core/products"
    38  	"code.vegaprotocol.io/vega/core/risk"
    39  	"code.vegaprotocol.io/vega/core/settlement"
    40  	"code.vegaprotocol.io/vega/core/types"
    41  	vegacontext "code.vegaprotocol.io/vega/libs/context"
    42  	vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
    43  	"code.vegaprotocol.io/vega/libs/num"
    44  	"code.vegaprotocol.io/vega/logging"
    45  
    46  	"github.com/golang/mock/gomock"
    47  	"github.com/stretchr/testify/assert"
    48  	"github.com/stretchr/testify/require"
    49  )
    50  
    51  func TestRestoreSettledMarket(t *testing.T) {
    52  	tm := getSettledMarket(t)
    53  	em := tm.market.GetState()
    54  
    55  	ctrl := gomock.NewController(t)
    56  	defer ctrl.Finish()
    57  	oracleEngine := mocks.NewMockOracleEngine(ctrl)
    58  
    59  	var unsubs uint64
    60  	unsubscribe := func(_ context.Context, id spec.SubscriptionID) { unsubs++ }
    61  	oracleEngine.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(spec.SubscriptionID(1), unsubscribe, nil)
    62  	oracleEngine.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(spec.SubscriptionID(2), unsubscribe, nil)
    63  
    64  	snap, err := newMarketFromSnapshot(t, context.Background(), ctrl, em, oracleEngine)
    65  	require.NoError(t, err)
    66  	require.NotEmpty(t, snap)
    67  
    68  	// check the market is restored settled and that we have unsubscribed the two oracles
    69  	assert.Equal(t, types.MarketStateSettled, snap.State())
    70  	assert.Equal(t, uint64(2), unsubs)
    71  	closed := snap.OnTick(vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()), time.Now())
    72  	assert.True(t, closed)
    73  }
    74  
    75  func TestRestoreClosedMarket(t *testing.T) {
    76  	tm := getActiveMarket(t)
    77  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
    78  	changes := &types.MarketStateUpdateConfiguration{
    79  		MarketID:        tm.mktCfg.ID,
    80  		SettlementPrice: num.UintOne(),
    81  		UpdateType:      types.MarketStateUpdateTypeTerminate,
    82  	}
    83  	tm.market.UpdateMarketState(ctx, changes)
    84  	em := tm.market.GetState()
    85  
    86  	ctrl := gomock.NewController(t)
    87  	defer ctrl.Finish()
    88  	oracleEngine := mocks.NewMockOracleEngine(ctrl)
    89  
    90  	var unsubs uint64
    91  	unsubscribe := func(_ context.Context, id spec.SubscriptionID) { unsubs++ }
    92  	oracleEngine.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(spec.SubscriptionID(1), unsubscribe, nil)
    93  	oracleEngine.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(spec.SubscriptionID(2), unsubscribe, nil)
    94  
    95  	snap, err := newMarketFromSnapshot(t, context.Background(), ctrl, em, oracleEngine)
    96  	require.NoError(t, err)
    97  	require.NotEmpty(t, snap)
    98  
    99  	// check the market is restored settled and that we have unsubscribed the two oracles
   100  	assert.Equal(t, types.MarketStateClosed, snap.State())
   101  	assert.Equal(t, uint64(2), unsubs)
   102  	closed := snap.OnTick(vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()), time.Now())
   103  	assert.True(t, closed)
   104  }
   105  
   106  func TestRestoreTerminatedMarket(t *testing.T) {
   107  	tm := getTerminatedMarket(t)
   108  	em := tm.market.GetState()
   109  
   110  	ctrl := gomock.NewController(t)
   111  	defer ctrl.Finish()
   112  	oracleEngine := mocks.NewMockOracleEngine(ctrl)
   113  
   114  	var termUnsub bool
   115  	unsubscribe := func(_ context.Context, id spec.SubscriptionID) {
   116  		if id == spec.SubscriptionID(2) {
   117  			termUnsub = true
   118  		}
   119  	}
   120  	oracleEngine.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(spec.SubscriptionID(1), unsubscribe, nil)
   121  	oracleEngine.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(spec.SubscriptionID(2), unsubscribe, nil)
   122  
   123  	snap, err := newMarketFromSnapshot(t, context.Background(), ctrl, em, oracleEngine)
   124  	require.NoError(t, err)
   125  	require.NotEmpty(t, snap)
   126  
   127  	// check the market is restored terminated and that we have unsubscribed one oracles
   128  	assert.Equal(t, types.MarketStateTradingTerminated, snap.State())
   129  	assert.True(t, termUnsub)
   130  	closed := snap.OnTick(vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash()), time.Now())
   131  	assert.False(t, closed)
   132  }
   133  
   134  func TestRestoreNilLastTradedPrice(t *testing.T) {
   135  	now := time.Unix(10, 0)
   136  	tm := getTestMarket(t, now, nil, nil)
   137  	defer tm.ctrl.Finish()
   138  
   139  	em := tm.market.GetState()
   140  	assert.Nil(t, em.LastTradedPrice)
   141  	assert.Nil(t, em.CurrentMarkPrice)
   142  
   143  	ctrl := gomock.NewController(t)
   144  	defer ctrl.Finish()
   145  	oracleEngine := mocks.NewMockOracleEngine(ctrl)
   146  
   147  	unsubscribe := func(_ context.Context, id spec.SubscriptionID) {
   148  	}
   149  	oracleEngine.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(spec.SubscriptionID(1), unsubscribe, nil)
   150  	oracleEngine.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(spec.SubscriptionID(2), unsubscribe, nil)
   151  
   152  	snap, err := newMarketFromSnapshot(t, context.Background(), ctrl, em, oracleEngine)
   153  	require.NoError(t, err)
   154  	require.NotEmpty(t, snap)
   155  
   156  	em2 := snap.GetState()
   157  	assert.Nil(t, em2.LastTradedPrice)
   158  	assert.Nil(t, em2.CurrentMarkPrice)
   159  }
   160  
   161  func getTerminatedMarket(t *testing.T) *testMarket {
   162  	t.Helper()
   163  	pubKeys := []*dstypes.Signer{
   164  		dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey),
   165  	}
   166  
   167  	now := time.Unix(10, 0)
   168  	tm := getTestMarket(t, now, nil, nil)
   169  	defer tm.ctrl.Finish()
   170  
   171  	// terminate the market
   172  	err := tm.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   173  		Signers: pubKeys,
   174  		Data: map[string]string{
   175  			"trading.terminated": "true",
   176  		},
   177  	})
   178  	require.NoError(t, err)
   179  	require.Equal(t, types.MarketStateTradingTerminated, tm.market.State())
   180  
   181  	return tm
   182  }
   183  
   184  func getSettledMarket(t *testing.T) *testMarket {
   185  	t.Helper()
   186  
   187  	tm := getTerminatedMarket(t)
   188  
   189  	pubKeys := []*dstypes.Signer{
   190  		dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey),
   191  	}
   192  
   193  	err := tm.oracleEngine.BroadcastData(context.Background(), dstypes.Data{
   194  		Signers: pubKeys,
   195  		Data: map[string]string{
   196  			"prices.ETH.value": "100",
   197  		},
   198  	})
   199  	require.NoError(t, err)
   200  	assert.Equal(t, types.MarketStateSettled, tm.market.State())
   201  
   202  	return tm
   203  }
   204  
   205  func getActiveMarket(t *testing.T) *testMarket {
   206  	t.Helper()
   207  
   208  	esm := newEquityShareMarket(t)
   209  	matchingPrice := uint64(900000)
   210  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
   211  	esm.WithSubmittedOrder(t, "some-id-1", "party1", types.SideSell, matchingPrice+1).
   212  		WithSubmittedOrder(t, "some-id-2", "party2", types.SideBuy, matchingPrice-1).
   213  		WithSubmittedOrder(t, "some-id-3", "party1", types.SideSell, matchingPrice).
   214  		WithSubmittedOrder(t, "some-id-4", "party2", types.SideBuy, matchingPrice).
   215  		WithSubmittedLiquidityProvision(t, "party1", "lp-id-1", 2000000, "0.5").
   216  		WithSubmittedLiquidityProvision(t, "party2", "lp-id-2", 1000000, "0.5")
   217  
   218  	// end opening auction
   219  	esm.tm.market.OnTick(ctx, esm.Now.Add(2*time.Second))
   220  	return esm.tm
   221  }
   222  
   223  // newMarketFromSnapshot is a wrapper for NewMarketFromSnapshot with a lot of defaults handled.
   224  func newMarketFromSnapshot(t *testing.T, ctx context.Context, ctrl *gomock.Controller, em *types.ExecMarket, oracleEngine products.OracleEngine) (*future.Market, error) {
   225  	t.Helper()
   226  	var (
   227  		riskConfig       = risk.NewDefaultConfig()
   228  		positionConfig   = positions.NewDefaultConfig()
   229  		settlementConfig = settlement.NewDefaultConfig()
   230  		matchingConfig   = matching.NewDefaultConfig()
   231  		feeConfig        = fee.NewDefaultConfig()
   232  		liquidityConfig  = liquidity.NewDefaultConfig()
   233  	)
   234  	log := logging.NewTestLogger()
   235  
   236  	assets, err := em.Market.GetAssets()
   237  	require.NoError(t, err)
   238  	cfgAsset := NewAssetStub(assets[0], em.Market.DecimalPlaces)
   239  
   240  	epochEngine := mocks.NewMockEpochEngine(ctrl)
   241  	epochEngine.EXPECT().NotifyOnEpoch(gomock.Any(), gomock.Any()).Times(1)
   242  	teams := mocks.NewMockTeams(ctrl)
   243  	bc := mocks.NewMockAccountBalanceChecker(ctrl)
   244  	broker := bmocks.NewMockBroker(ctrl)
   245  
   246  	broker.EXPECT().Stage(gomock.Any()).AnyTimes()
   247  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   248  	timeService := mocks.NewMockTimeService(ctrl)
   249  	timeService.EXPECT().GetTimeNow().AnyTimes()
   250  	collateralEngine := collateral.New(log, collateral.NewDefaultConfig(), timeService, broker)
   251  
   252  	marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine)
   253  	epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore)
   254  
   255  	positionConfig.StreamPositionVerbose = true
   256  	referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl)
   257  	volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl)
   258  	volumeRebate := fmock.NewMockVolumeRebateService(ctrl)
   259  	referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   260  	volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes()
   261  	referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes()
   262  	banking := mocks.NewMockBanking(ctrl)
   263  	parties := mocks.NewMockParties(ctrl)
   264  
   265  	return future.NewMarketFromSnapshot(ctx, log, em, riskConfig, positionConfig, settlementConfig, matchingConfig,
   266  		feeConfig, liquidityConfig, collateralEngine, oracleEngine, timeService, broker, stubs.NewStateVar(), cfgAsset, marketActivityTracker,
   267  		peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties)
   268  }