code.vegaprotocol.io/vega@v0.79.0/core/monitor/price/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 price_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/monitor/price"
    24  	"code.vegaprotocol.io/vega/core/monitor/price/mocks"
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	"code.vegaprotocol.io/vega/libs/crypto"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  	"code.vegaprotocol.io/vega/libs/proto"
    29  	"code.vegaprotocol.io/vega/logging"
    30  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    31  
    32  	"github.com/golang/mock/gomock"
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/require"
    35  )
    36  
    37  func createPriceMonitor(t *testing.T, ctrl *gomock.Controller) *price.Engine {
    38  	t.Helper()
    39  
    40  	riskModel, auctionState, settings := createPriceMonitorDeps(t, ctrl)
    41  	statevar := mocks.NewMockStateVarEngine(ctrl)
    42  	statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
    43  	auctionState.EXPECT().IsPriceAuction().Times(1).Return(false)
    44  
    45  	pm, err := price.NewMonitor("asset", "market", riskModel, auctionState, settings, statevar, logging.NewTestLogger())
    46  	require.NoError(t, err)
    47  	require.NotNil(t, pm)
    48  
    49  	return pm
    50  }
    51  
    52  func createPriceMonitorDeps(t *testing.T, ctrl *gomock.Controller) (*mocks.MockRangeProvider, *mocks.MockAuctionState, *types.PriceMonitoringSettings) {
    53  	t.Helper()
    54  	riskModel := mocks.NewMockRangeProvider(ctrl)
    55  	auctionStateMock := mocks.NewMockAuctionState(ctrl)
    56  
    57  	settings := &types.PriceMonitoringSettings{
    58  		Parameters: &types.PriceMonitoringParameters{
    59  			Triggers: []*types.PriceMonitoringTrigger{},
    60  		},
    61  	}
    62  
    63  	auctionStateMock.EXPECT().IsFBA().Return(false).AnyTimes()
    64  	auctionStateMock.EXPECT().InAuction().Return(false).AnyTimes()
    65  
    66  	return riskModel, auctionStateMock, settings
    67  }
    68  
    69  func getHash(pe *price.Engine) []byte {
    70  	state := pe.GetState()
    71  	pmproto := state.IntoProto()
    72  	bytes, _ := proto.Marshal(pmproto)
    73  	return crypto.Hash(bytes)
    74  }
    75  
    76  func TestEmpty(t *testing.T) {
    77  	ctrl := gomock.NewController(t)
    78  	defer ctrl.Finish()
    79  
    80  	pm1 := createPriceMonitor(t, ctrl)
    81  	assert.NotNil(t, pm1)
    82  
    83  	// Get the initial state
    84  	hash1 := getHash(pm1)
    85  	state1 := pm1.GetState()
    86  
    87  	// Create a new market and restore into it
    88  	riskModel, auctionState, settings := createPriceMonitorDeps(t, ctrl)
    89  
    90  	statevar := mocks.NewMockStateVarEngine(ctrl)
    91  	statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
    92  	pm2, err := price.NewMonitorFromSnapshot("marketID", "assetID", state1, settings, riskModel, auctionState, statevar, logging.NewTestLogger())
    93  	require.NoError(t, err)
    94  	require.NotNil(t, pm2)
    95  
    96  	// Now get the state again and check it against the original
    97  	hash2 := getHash(pm2)
    98  
    99  	assert.Equal(t, hash1, hash2)
   100  }
   101  
   102  func TestChangedState(t *testing.T) {
   103  	ctrl := gomock.NewController(t)
   104  	defer ctrl.Finish()
   105  
   106  	pm1 := createPriceMonitor(t, ctrl)
   107  	assert.NotNil(t, pm1)
   108  
   109  	// Get the initial state
   110  	hash1 := getHash(pm1)
   111  
   112  	// Perform some actions on the object
   113  	as := mocks.NewMockAuctionState(ctrl)
   114  	as.EXPECT().IsFBA().Return(false).Times(10)
   115  	as.EXPECT().InAuction().Return(false).Times(10)
   116  
   117  	now := time.Now()
   118  
   119  	for i := 0; i < 10; i++ {
   120  		pm1.OnTimeUpdate(now)
   121  		p := num.NewUint(uint64(100 + i))
   122  		b := pm1.CheckPrice(context.Background(), as, p, true, true)
   123  		now = now.Add(time.Minute * 1)
   124  		require.False(t, b)
   125  	}
   126  
   127  	// Check something has changed
   128  	assert.True(t, pm1.Changed())
   129  
   130  	// Get the new hash after the change
   131  	hash2 := getHash(pm1)
   132  	assert.NotEqual(t, hash1, hash2)
   133  
   134  	// Now try reloading the state
   135  	state := pm1.GetState()
   136  	assert.Len(t, state.PricesNow, 1)
   137  	assert.Len(t, state.PricesPast, 9)
   138  
   139  	riskModel, auctionState, settings := createPriceMonitorDeps(t, ctrl)
   140  	statevar := mocks.NewMockStateVarEngine(ctrl)
   141  	statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
   142  	pm2, err := price.NewMonitorFromSnapshot("marketID", "assetID", state, settings, riskModel, auctionState, statevar, logging.NewTestLogger())
   143  	require.NoError(t, err)
   144  	require.NotNil(t, pm2)
   145  
   146  	hash3 := getHash(pm2)
   147  	assert.Equal(t, hash2, hash3)
   148  
   149  	state2 := pm1.GetState()
   150  	assert.Len(t, state2.PricesNow, 1)
   151  	assert.Len(t, state2.PricesPast, 9)
   152  
   153  	asProto := state2.IntoProto()
   154  	state3 := types.PriceMonitorFromProto(asProto)
   155  	assert.Len(t, state3.PricesNow, 1)
   156  	assert.Len(t, state3.PricesPast, 9)
   157  	assert.Equal(t, state2.Now.UnixNano(), state3.Now.UnixNano())
   158  	assert.Equal(t, state2.Update.UnixNano(), state3.Update.UnixNano())
   159  	assert.Equal(t, state2.PricesPast[0].Time.UnixNano(), state3.PricesPast[0].Time.UnixNano())
   160  }
   161  
   162  func TestRestorePriceBoundRepresentation(t *testing.T) {
   163  	ctrl := gomock.NewController(t)
   164  	defer ctrl.Finish()
   165  	riskModel := mocks.NewMockRangeProvider(ctrl)
   166  	auctionStateMock := mocks.NewMockAuctionState(ctrl)
   167  	currentPrice := num.NewUint(123)
   168  	now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC)
   169  
   170  	settings := types.PriceMonitoringSettingsFromProto(&vegapb.PriceMonitoringSettings{
   171  		Parameters: &vegapb.PriceMonitoringParameters{
   172  			Triggers: []*vegapb.PriceMonitoringTrigger{
   173  				{Horizon: 3600, Probability: "0.99", AuctionExtension: 60},
   174  				{Horizon: 7200, Probability: "0.95", AuctionExtension: 300},
   175  			},
   176  		},
   177  	})
   178  
   179  	_, pMin1, pMax1, _, _ := getPriceBounds(currentPrice, 1, 2)
   180  	_, pMin2, pMax2, _, _ := getPriceBounds(currentPrice, 3, 4)
   181  	currentPriceD := currentPrice.ToDecimal()
   182  	auctionStateMock.EXPECT().IsFBA().Return(false).AnyTimes()
   183  	auctionStateMock.EXPECT().InAuction().Return(false).AnyTimes()
   184  	auctionStateMock.EXPECT().IsPriceAuction().Return(false).AnyTimes()
   185  	statevar := mocks.NewMockStateVarEngine(ctrl)
   186  	statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   187  
   188  	pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger())
   189  	require.NoError(t, err)
   190  	require.NotNil(t, pm)
   191  	downFactors := []num.Decimal{pMin1.Div(currentPriceD), pMin2.Div(currentPriceD)}
   192  	upFactors := []num.Decimal{pMax1.Div(currentPriceD), pMax2.Div(currentPriceD)}
   193  
   194  	pm.UpdateTestFactors(downFactors, upFactors)
   195  
   196  	pm.OnTimeUpdate(now)
   197  	b := pm.CheckPrice(context.Background(), auctionStateMock, currentPrice, true, true)
   198  	require.False(t, b)
   199  
   200  	state := pm.GetState()
   201  	snap, err := price.NewMonitorFromSnapshot("market", "asset", state, settings, riskModel, auctionStateMock, statevar, logging.NewTestLogger())
   202  	require.NoError(t, err)
   203  
   204  	min, max := pm.GetValidPriceRange()
   205  	sMin, sMax := snap.GetValidPriceRange()
   206  	// check the values of the wrapped decimal are the same
   207  	require.Equal(t, min, sMin)
   208  	require.Equal(t, max, sMax)
   209  }