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 }