code.vegaprotocol.io/vega@v0.79.0/core/monitor/price/pricemonitoring_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/num" 27 "code.vegaprotocol.io/vega/logging" 28 proto "code.vegaprotocol.io/vega/protos/vega" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/require" 32 ) 33 34 func TestEmptyParametersList(t *testing.T) { 35 ctrl := gomock.NewController(t) 36 defer ctrl.Finish() 37 riskModel := mocks.NewMockRangeProvider(ctrl) 38 auctionStateMock := mocks.NewMockAuctionState(ctrl) 39 currentPrice := num.NewUint(123) 40 41 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 42 43 settings := &types.PriceMonitoringSettings{ 44 Parameters: &types.PriceMonitoringParameters{ 45 Triggers: []*types.PriceMonitoringTrigger{}, 46 }, 47 } 48 49 auctionStateMock.EXPECT().IsFBA().Return(false).Times(4) 50 auctionStateMock.EXPECT().InAuction().Return(false).Times(4) 51 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 52 statevar := mocks.NewMockStateVarEngine(ctrl) 53 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 54 55 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 56 require.NoError(t, err) 57 require.NotNil(t, pm) 58 59 pm.OnTimeUpdate(now) 60 b := pm.CheckPrice(context.TODO(), auctionStateMock, currentPrice, true, true) 61 require.False(t, b) 62 63 pm.OnTimeUpdate(now.Add(time.Second)) 64 b = pm.CheckPrice(context.TODO(), auctionStateMock, currentPrice, true, true) 65 require.False(t, b) 66 67 pm.OnTimeUpdate(now.Add(time.Minute)) 68 b = pm.CheckPrice(context.TODO(), auctionStateMock, currentPrice, true, true) 69 require.False(t, b) 70 71 pm.OnTimeUpdate(now.Add(time.Hour)) 72 b = pm.CheckPrice(context.TODO(), auctionStateMock, currentPrice, true, true) 73 require.False(t, b) 74 } 75 76 func TestErrorWithNilRiskModel(t *testing.T) { 77 ctrl := gomock.NewController(t) 78 defer ctrl.Finish() 79 t1 := proto.PriceMonitoringTrigger{Horizon: 7200, Probability: "0.95", AuctionExtension: 300} 80 t2 := proto.PriceMonitoringTrigger{Horizon: 3600, Probability: "0.99", AuctionExtension: 60} 81 82 pSet := &proto.PriceMonitoringSettings{ 83 Parameters: &proto.PriceMonitoringParameters{ 84 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 85 }, 86 } 87 auctionStateMock := mocks.NewMockAuctionState(ctrl) 88 settings := types.PriceMonitoringSettingsFromProto(pSet) 89 statevar := mocks.NewMockStateVarEngine(ctrl) 90 // statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 91 // auctionStateMock.EXPECT().IsPriceAuction().Times(1).Return(false) 92 pm, err := price.NewMonitor("asset", "market", nil, auctionStateMock, settings, statevar, logging.NewTestLogger()) 93 require.Error(t, err) 94 require.Nil(t, pm) 95 } 96 97 func TestGetHorizonYearFractions(t *testing.T) { 98 ctrl := gomock.NewController(t) 99 riskModel := mocks.NewMockRangeProvider(ctrl) 100 auctionStateMock := mocks.NewMockAuctionState(ctrl) 101 t1 := proto.PriceMonitoringTrigger{Horizon: 7200, Probability: "0.95", AuctionExtension: 300} 102 t2 := proto.PriceMonitoringTrigger{Horizon: 3600, Probability: "0.99", AuctionExtension: 60} 103 104 pSet := &proto.PriceMonitoringSettings{ 105 Parameters: &proto.PriceMonitoringParameters{ 106 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 107 }, 108 } 109 settings := types.PriceMonitoringSettingsFromProto(pSet) 110 statevar := mocks.NewMockStateVarEngine(ctrl) 111 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 112 auctionStateMock.EXPECT().IsPriceAuction().Times(1).Return(false) 113 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 114 require.NoError(t, err) 115 require.NotNil(t, pm) 116 117 yearFractions := pm.GetHorizonYearFractions() 118 require.Equal(t, 2, len(yearFractions)) 119 require.Equal(t, horizonToYearFraction(t2.Horizon), yearFractions[0]) 120 require.Equal(t, horizonToYearFraction(t1.Horizon), yearFractions[1]) 121 } 122 123 func TestRecordPriceChange(t *testing.T) { 124 ctrl := gomock.NewController(t) 125 defer ctrl.Finish() 126 riskModel := mocks.NewMockRangeProvider(ctrl) 127 auctionStateMock := mocks.NewMockAuctionState(ctrl) 128 cp := num.NewUint(123) 129 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 130 t1 := proto.PriceMonitoringTrigger{Horizon: 7200, Probability: "0.95", AuctionExtension: 300} 131 t2 := proto.PriceMonitoringTrigger{Horizon: 3600, Probability: "0.99", AuctionExtension: 60} 132 133 pSet := &proto.PriceMonitoringSettings{ 134 Parameters: &proto.PriceMonitoringParameters{ 135 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 136 }, 137 } 138 settings := types.PriceMonitoringSettingsFromProto(pSet) 139 140 auctionStateMock.EXPECT().IsFBA().Return(false).Times(4) 141 auctionStateMock.EXPECT().InAuction().Return(false).Times(4) 142 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 143 statevar := mocks.NewMockStateVarEngine(ctrl) 144 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 145 146 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 147 require.NoError(t, err) 148 require.NotNil(t, pm) 149 150 pm.OnTimeUpdate(now) 151 b := pm.CheckPrice(context.TODO(), auctionStateMock, cp, true, true) 152 require.False(t, b) 153 one := num.NewUint(1) 154 cp1 := num.Sum(cp, one) // plus 1 155 cp2 := num.Sum(cp, one, one) // plus 2 156 b = pm.CheckPrice(context.TODO(), auctionStateMock, cp2, true, true) 157 require.False(t, b) 158 b = pm.CheckPrice(context.TODO(), auctionStateMock, cp1, true, true) 159 require.False(t, b) 160 b = pm.CheckPrice(context.TODO(), auctionStateMock, cp, true, true) 161 require.False(t, b) 162 } 163 164 func TestCheckBoundViolationsWithinCurrentTimeWith2HorizonProbabilityPairs(t *testing.T) { 165 ctrl := gomock.NewController(t) 166 defer ctrl.Finish() 167 riskModel := mocks.NewMockRangeProvider(ctrl) 168 auctionStateMock := mocks.NewMockAuctionState(ctrl) 169 cp := num.NewUint(123) 170 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 171 t1Time, t2Time := int64(60), int64(300) 172 t1 := proto.PriceMonitoringTrigger{Horizon: 3600, Probability: "0.99", AuctionExtension: t1Time} 173 t2 := proto.PriceMonitoringTrigger{Horizon: 7200, Probability: "0.95", AuctionExtension: t2Time} 174 pSet := &proto.PriceMonitoringSettings{ 175 Parameters: &proto.PriceMonitoringParameters{ 176 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 177 }, 178 } 179 settings := types.PriceMonitoringSettingsFromProto(pSet) 180 181 maxDown1, maxUp1, maxDown2, maxUp2 := num.NewUint(1), num.NewUint(2), num.NewUint(3), num.NewUint(4) 182 183 cpDec := num.DecimalFromUint(cp) 184 // get the price bounds 185 pMin1 := cpDec.Sub(num.DecimalFromUint(maxDown1)) 186 pMin2 := cpDec.Sub(num.DecimalFromUint(maxDown2)) 187 pMax1 := cpDec.Add(num.DecimalFromUint(maxUp1)) 188 pMax2 := cpDec.Add(num.DecimalFromUint(maxUp2)) 189 one := num.NewUint(1) // 1, just to tweak prices when calling CheckPrice 190 require.True(t, maxDown2.GT(maxDown1)) 191 require.True(t, maxUp2.GT(maxUp1)) 192 193 downFactors := []num.Decimal{pMin1.Div(cpDec), pMin2.Div(cpDec)} 194 upFactors := []num.Decimal{pMax1.Div(cpDec), pMax2.Div(cpDec)} 195 196 auctionStateMock.EXPECT().IsFBA().Return(false).AnyTimes() 197 auctionStateMock.EXPECT().IsPriceAuction().Return(false).AnyTimes() 198 auctionStateMock.EXPECT().InAuction().Return(false).Times(14) 199 statevar := mocks.NewMockStateVarEngine(ctrl) 200 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() 201 202 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 203 require.NoError(t, err) 204 require.NotNil(t, pm) 205 pm.UpdateTestFactors(downFactors, upFactors) 206 207 pm.OnTimeUpdate(now) 208 b := pm.CheckPrice(context.TODO(), auctionStateMock, cp, true, true) 209 require.False(t, b) 210 211 cPrice := num.Sum(cp, maxUp1) 212 cPrice = cPrice.Sub(cPrice, one) 213 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 214 require.False(t, b) 215 216 cPrice = num.Sum(cp, one) 217 cPrice = cPrice.Sub(cPrice, maxDown1) 218 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 219 require.False(t, b) 220 221 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 222 require.False(t, b) 223 224 cPrice = num.Sum(one, cPrice.Sub(cp, maxDown1)) // add one bc price bounds are now using Ceil for min price 225 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 226 require.False(t, b) 227 228 // set the min duration to equal auction extension 1 229 pm.SetMinDuration(time.Duration(t1.AuctionExtension) * time.Second) 230 end := types.AuctionDuration{Duration: t1.AuctionExtension} 231 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 232 233 delta := num.Sum(maxUp1, maxUp2) 234 cPrice = num.Sum(cp, delta.Div(delta, num.Sum(one, one))) 235 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 236 require.False(t, b) 237 238 // Reinstantiate price monitoring after auction to reset internal state 239 pm, err = price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 240 require.NoError(t, err) 241 require.NotNil(t, pm) 242 pm.UpdateTestFactors(downFactors, upFactors) 243 244 pm.OnTimeUpdate(now) 245 b = pm.CheckPrice(context.TODO(), auctionStateMock, cp, true, true) 246 require.False(t, b) 247 248 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 249 delta = num.Sum(maxDown1, maxDown2) 250 cPrice = cPrice.Sub(cp, delta.Div(delta, num.Sum(one, one))) 251 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 252 require.False(t, b) 253 254 // Reinstantiate price monitoring after auction to reset internal state 255 pm, err = price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 256 require.NoError(t, err) 257 require.NotNil(t, pm) 258 pm.UpdateTestFactors(downFactors, upFactors) 259 260 pm.OnTimeUpdate(now) 261 b = pm.CheckPrice(context.TODO(), auctionStateMock, cp, true, true) 262 require.False(t, b) 263 264 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 265 cPrice = num.Sum(cp, num.UintZero().Sub(maxUp2, one)) // max price bound is now floored, so sub 1 to stay below second price bound 266 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 267 require.False(t, b) 268 269 // Reinstantiate price monitoring after auction to reset internal state 270 pm, err = price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 271 require.NoError(t, err) 272 require.NotNil(t, pm) 273 pm.UpdateTestFactors(downFactors, upFactors) 274 275 pm.OnTimeUpdate(now) 276 b = pm.CheckPrice(context.TODO(), auctionStateMock, cp, true, true) 277 require.False(t, b) 278 279 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 280 cPrice = num.Sum(cPrice.Sub(cp, maxDown2), one) // add 1 back, avoid breaching both down limits 281 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 282 require.False(t, b) 283 284 // Reinstantiate price monitoring after auction to reset internal state 285 pm, err = price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 286 require.NoError(t, err) 287 require.NotNil(t, pm) 288 pm.UpdateTestFactors(downFactors, upFactors) 289 pm.OnTimeUpdate(now) 290 b = pm.CheckPrice(context.TODO(), auctionStateMock, cp, true, true) 291 require.False(t, b) 292 293 end = types.AuctionDuration{Duration: t1.AuctionExtension} 294 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 295 cPrice = num.Sum(cp, maxUp2, maxUp2) 296 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 297 require.False(t, b) 298 // recheck with same price, 2nd trigger should get breached now 299 end2 := types.AuctionDuration{Duration: t2.AuctionExtension} 300 auctionStateMock.EXPECT().InAuction().Return(true).Times(1) 301 auctionStateMock.EXPECT().ExtendAuctionPrice(end2).Times(1) 302 303 auctionEnd := now.Add(time.Duration(end.Duration) * time.Second) 304 now = auctionEnd.Add(time.Second) 305 pm.OnTimeUpdate(now) 306 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 307 require.False(t, b) 308 309 // recheck with same price, auction should end now 310 auctionStateMock.EXPECT().InAuction().Return(true).Times(1) 311 auctionStateMock.EXPECT().SetReadyToLeave().Times(1) 312 auctionEnd = auctionEnd.Add(time.Duration(end2.Duration) * time.Second) 313 auctionStateMock.EXPECT().ExpiresAt().Return(&auctionEnd) 314 now = auctionEnd.Add(time.Second) 315 pm.OnTimeUpdate(now) 316 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 317 require.False(t, b) 318 319 // Check with same price again after exiting, should not start auction now 320 auctionStateMock.EXPECT().InAuction().Return(false).Times(3) 321 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 322 require.False(t, b) 323 324 // Update factors and check again, should still be fine 325 pm.UpdateTestFactors(downFactors, upFactors) 326 pm.OnTimeUpdate(now) 327 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 328 require.False(t, b) 329 330 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 331 delta = num.Sum(maxDown2, maxDown2) 332 cPrice = cPrice.Sub(cp, delta) 333 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) 334 require.False(t, b) 335 } 336 337 func TestAuctionStartedAndEndendBy1Trigger(t *testing.T) { 338 ctrl := gomock.NewController(t) 339 defer ctrl.Finish() 340 riskModel := mocks.NewMockRangeProvider(ctrl) 341 auctionStateMock := mocks.NewMockAuctionState(ctrl) 342 price1 := num.NewUint(123) 343 ctx := context.Background() 344 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 345 t1 := proto.PriceMonitoringTrigger{Horizon: 600, Probability: "0.95", AuctionExtension: 60} 346 t2 := proto.PriceMonitoringTrigger{Horizon: 600, Probability: "0.99", AuctionExtension: 120} 347 pSet := &proto.PriceMonitoringSettings{ 348 Parameters: &proto.PriceMonitoringParameters{ 349 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 350 }, 351 } 352 settings := types.PriceMonitoringSettingsFromProto(pSet) 353 354 maxDown1, maxUp1 := num.NewUint(1), num.NewUint(2) 355 maxDown2 := num.Sum(maxUp1, maxUp1) // yes, maxUp -> maxUp == maxDown*2, down2 == down1*4 356 maxUp2 := num.Sum(maxDown2, maxDown2) // double 357 decPrice := price1.ToDecimal() 358 p1Min1 := decPrice.Sub(num.DecimalFromUint(maxDown1)) 359 p1Min2 := decPrice.Sub(num.DecimalFromUint(maxDown2)) 360 p1Max1 := decPrice.Add(num.DecimalFromUint(maxUp1)) 361 p1Max2 := decPrice.Add(num.DecimalFromUint(maxUp2)) 362 downFactorsP1 := []num.Decimal{p1Min1.Div(decPrice), p1Min2.Div(decPrice)} 363 upFactorsP1 := []num.Decimal{p1Max1.Div(decPrice), p1Max2.Div(decPrice)} 364 365 auctionStateMock.EXPECT().IsFBA().Return(false).AnyTimes() 366 auctionStateMock.EXPECT().InAuction().Return(false).Times(2) 367 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 368 statevar := mocks.NewMockStateVarEngine(ctrl) 369 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 370 371 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 372 require.NoError(t, err) 373 require.NotNil(t, pm) 374 pm.UpdateTestFactors(downFactorsP1, upFactorsP1) 375 pm.OnTimeUpdate(now) 376 b := pm.CheckPrice(ctx, auctionStateMock, price1, true, true) 377 require.False(t, b) 378 379 end := types.AuctionDuration{Duration: t1.AuctionExtension} 380 pm.SetMinDuration(time.Duration(t1.AuctionExtension) * time.Second) 381 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 382 383 delta := num.Sum().Sub(maxUp2, maxUp1) 384 cPrice := num.Sum(price1, delta) 385 pm.OnTimeUpdate(now) 386 b = pm.CheckPrice(context.TODO(), auctionStateMock, cPrice, true, true) // t1 violated only 387 require.False(t, b) 388 389 initialAuctionEnd := now.Add(time.Duration(t1.AuctionExtension) * time.Second) 390 391 auctionStateMock.EXPECT().InAuction().Return(true).Times(1) 392 auctionStateMock.EXPECT().ExpiresAt().Return(&initialAuctionEnd).Times(1) 393 auctionStateMock.EXPECT().SetReadyToLeave().Times(1) 394 395 afterInitialAuction := initialAuctionEnd.Add(time.Nanosecond) 396 pm.OnTimeUpdate(afterInitialAuction) 397 b = pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) // price should be accepted now 398 require.False(t, b) 399 } 400 401 func TestAuctionStartedAndEndendBy2Triggers(t *testing.T) { 402 ctrl := gomock.NewController(t) 403 riskModel := mocks.NewMockRangeProvider(ctrl) 404 auctionStateMock := mocks.NewMockAuctionState(ctrl) 405 price1 := num.NewUint(123) 406 ctx := context.Background() 407 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 408 t1 := proto.PriceMonitoringTrigger{Horizon: 600, Probability: "0.95", AuctionExtension: 60} 409 t2 := proto.PriceMonitoringTrigger{Horizon: 600, Probability: "0.99", AuctionExtension: 120} 410 pSet := &proto.PriceMonitoringSettings{ 411 Parameters: &proto.PriceMonitoringParameters{ 412 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 413 }, 414 } 415 settings := types.PriceMonitoringSettingsFromProto(pSet) 416 417 _, _, _, _, maxUp1 := getPriceBounds(price1, 1, 2) 418 _, _, _, _, maxUp2 := getPriceBounds(price1, 1*4, 2*4) 419 420 auctionStateMock.EXPECT().IsFBA().Return(false).Times(2) 421 auctionStateMock.EXPECT().InAuction().Return(false).Times(2) 422 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 423 statevar := mocks.NewMockStateVarEngine(ctrl) 424 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 425 426 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 427 require.NoError(t, err) 428 require.NotNil(t, pm) 429 430 pm.OnTimeUpdate(now) 431 b := pm.CheckPrice(ctx, auctionStateMock, price1, true, true) 432 require.False(t, b) 433 434 end := types.AuctionDuration{Duration: t1.AuctionExtension + t2.AuctionExtension} 435 pm.SetMinDuration(time.Duration(end.Duration) * time.Second) 436 // auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 437 438 cPrice := num.Sum(price1, maxUp2, maxUp1) 439 b = pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) // t1 violated only 440 require.False(t, b) 441 442 initialAuctionEnd := now.Add(time.Duration(t1.AuctionExtension+t2.AuctionExtension) * time.Second) 443 444 auctionStateMock.EXPECT().IsFBA().Return(false).Times(1) 445 auctionStateMock.EXPECT().InAuction().Return(true).Times(1) 446 auctionStateMock.EXPECT().ExpiresAt().Return(&initialAuctionEnd).Times(1) 447 auctionStateMock.EXPECT().SetReadyToLeave().Times(1) 448 449 afterInitialAuction := initialAuctionEnd.Add(time.Nanosecond) 450 pm.OnTimeUpdate(afterInitialAuction) 451 b = pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) // price should be accepted now 452 require.False(t, b) 453 } 454 455 func TestAuctionStartedAndEndendBy1TriggerAndExtendedBy2nd(t *testing.T) { 456 // Also verifies that GetCurrentBounds() works as expected 457 ctrl := gomock.NewController(t) 458 defer ctrl.Finish() 459 riskModel := mocks.NewMockRangeProvider(ctrl) 460 auctionStateMock := mocks.NewMockAuctionState(ctrl) 461 price1 := num.NewUint(123) 462 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 463 t1 := proto.PriceMonitoringTrigger{Horizon: 600, Probability: "0.95", AuctionExtension: 60} 464 t2 := proto.PriceMonitoringTrigger{Horizon: 600, Probability: "0.99", AuctionExtension: 120} 465 pSet := &proto.PriceMonitoringSettings{ 466 Parameters: &proto.PriceMonitoringParameters{ 467 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 468 }, 469 } 470 settings := types.PriceMonitoringSettingsFromProto(pSet) 471 ctx := context.Background() 472 decPrice, pMin1, pMax1, _, maxUp1 := getPriceBounds(price1, 1, 2) 473 _, pMin2, pMax2, _, maxUp2 := getPriceBounds(price1, 1*4, 2*4) 474 475 one := num.NewUint(1) 476 t1lb1, _ := num.UintFromDecimal(pMin1) 477 t1lb1.AddSum(one) // account for value being ceil'ed 478 t1ub1, _ := num.UintFromDecimal(pMax1) 479 t1ub1.Sub(t1ub1, one) // floor 480 t2lb1, _ := num.UintFromDecimal(pMin2) 481 t2lb1.AddSum(one) // again: ceil 482 t2ub1, _ := num.UintFromDecimal(pMax2) 483 484 auctionStateMock.EXPECT().IsFBA().Return(false).Times(2) 485 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 486 auctionStateMock.EXPECT().InAuction().Return(false).Times(2) 487 statevar := mocks.NewMockStateVarEngine(ctrl) 488 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 489 490 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 491 downFactors := []num.Decimal{pMin1.Div(decPrice), pMin2.Div(decPrice)} 492 upFactors := []num.Decimal{pMax1.Div(decPrice), pMax2.Div(decPrice)} 493 pm.UpdateTestFactors(downFactors, upFactors) 494 495 require.NoError(t, err) 496 require.NotNil(t, pm) 497 498 pm.OnTimeUpdate(now) 499 b := pm.CheckPrice(ctx, auctionStateMock, price1, true, true) 500 require.False(t, b) 501 502 bounds := pm.GetCurrentBounds() 503 require.Len(t, bounds, 2) 504 require.Equal(t, bounds[0].Trigger.IntoProto(), &t1) 505 require.True(t, bounds[0].MinValidPrice.EQ(t1lb1)) 506 require.True(t, bounds[0].MaxValidPrice.EQ(t1ub1)) 507 require.Equal(t, bounds[0].ReferencePrice, decPrice) 508 require.Equal(t, bounds[1].Trigger.IntoProto(), &t2) 509 require.True(t, bounds[1].MinValidPrice.EQ(t2lb1)) 510 require.True(t, bounds[1].MaxValidPrice.EQ(t2ub1)) 511 require.Equal(t, bounds[1].ReferencePrice, decPrice) 512 513 end := types.AuctionDuration{Duration: t1.AuctionExtension} 514 pm.SetMinDuration(time.Duration(end.Duration) * time.Second) 515 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 516 517 cPrice := num.Sum(price1, maxUp2) 518 cPrice.Sub(cPrice, maxUp1) 519 cp2 := cPrice 520 b = pm.CheckPrice(ctx, auctionStateMock, cp2, true, true) // t1 violated only 521 require.False(t, b) 522 523 initialAuctionEnd := now.Add(time.Duration(t1.AuctionExtension) * time.Second) 524 525 auctionStateMock.EXPECT().IsFBA().Return(false).Times(1) 526 auctionStateMock.EXPECT().InAuction().Return(true).Times(1) 527 auctionStateMock.EXPECT().ExpiresAt().Return(&initialAuctionEnd).Times(1) 528 529 bounds = pm.GetCurrentBounds() 530 require.Len(t, bounds, 1) 531 require.Equal(t, bounds[0].Trigger.IntoProto(), &t2) 532 require.True(t, bounds[0].MinValidPrice.EQ(t2lb1)) 533 require.True(t, bounds[0].MaxValidPrice.EQ(t2ub1)) 534 require.Equal(t, bounds[0].ReferencePrice, decPrice) 535 536 afterInitialAuction := initialAuctionEnd.Add(time.Nanosecond) 537 now = afterInitialAuction 538 539 cPrice = num.Sum(price1, maxUp2, maxUp1) 540 end2 := types.AuctionDuration{Duration: t2.AuctionExtension} 541 auctionStateMock.EXPECT().ExtendAuctionPrice(end2).Times(1) 542 pm.OnTimeUpdate(afterInitialAuction) 543 cp3 := cPrice 544 b = pm.CheckPrice(ctx, auctionStateMock, cp3, true, true) // price should violated 2nd trigger and result in auction extension 545 require.False(t, b) 546 547 bounds = pm.GetCurrentBounds() 548 require.Len(t, bounds, 0) 549 550 extendedAuctionEnd := now.Add(time.Duration(t1.AuctionExtension+t2.AuctionExtension) * time.Second) 551 552 // get new bounds 553 _, pMin1, pMax1, _, _ = getPriceBounds(cPrice, 1, 2) 554 _, pMin2, pMax2, _, _ = getPriceBounds(cPrice, 1*4, 2*4) 555 556 t1lb1, _ = num.UintFromDecimal(pMin1) 557 t1lb1.AddSum(one) // again ceil 558 t1ub1, _ = num.UintFromDecimal(pMax1) 559 t1ub1.Sub(t1ub1, one) // floor... 560 t2lb1, _ = num.UintFromDecimal(pMin2) 561 t2lb1.AddSum(one) // ceil... 562 t2ub1, _ = num.UintFromDecimal(pMax2) 563 t2ub1.Sub(t2ub1, one) // floor... 564 565 auctionStateMock.EXPECT().IsFBA().Return(false).Times(1) 566 auctionStateMock.EXPECT().InAuction().Return(true).Times(1) 567 auctionStateMock.EXPECT().SetReadyToLeave().Times(1) 568 569 afterExtendedAuction := extendedAuctionEnd.Add(time.Nanosecond) 570 pm.OnTimeUpdate(afterExtendedAuction) 571 b = pm.CheckPrice(ctx, auctionStateMock, cp3, true, true) // price should be accepted now 572 require.False(t, b) 573 } 574 575 func TestAuctionStartedBy1TriggerAndNotExtendedBy2ndStaleTrigger(t *testing.T) { 576 // Also verifies that GetCurrentBounds() works as expected 577 ctrl := gomock.NewController(t) 578 defer ctrl.Finish() 579 riskModel := mocks.NewMockRangeProvider(ctrl) 580 auctionStateMock := mocks.NewMockAuctionState(ctrl) 581 price1 := num.NewUint(123) 582 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 583 t1 := proto.PriceMonitoringTrigger{Horizon: 6, Probability: "0.95", AuctionExtension: 60} 584 t2 := proto.PriceMonitoringTrigger{Horizon: 6, Probability: "0.99", AuctionExtension: 120} 585 pSet := &proto.PriceMonitoringSettings{ 586 Parameters: &proto.PriceMonitoringParameters{ 587 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 588 }, 589 } 590 settings := types.PriceMonitoringSettingsFromProto(pSet) 591 ctx := context.Background() 592 decPrice, pMin1, pMax1, _, maxUp1 := getPriceBounds(price1, 1, 2) 593 _, pMin2, pMax2, _, maxUp2 := getPriceBounds(price1, 1*4, 2*4) 594 595 one := num.NewUint(1) 596 t1lb1, _ := num.UintFromDecimal(pMin1) 597 t1lb1.AddSum(one) // account for value being ceil'ed 598 t1ub1, _ := num.UintFromDecimal(pMax1) 599 t1ub1.Sub(t1ub1, one) // floor 600 t2lb1, _ := num.UintFromDecimal(pMin2) 601 t2lb1.AddSum(one) // again: ceil 602 t2ub1, _ := num.UintFromDecimal(pMax2) 603 604 auctionStateMock.EXPECT().IsFBA().Return(false).Times(2) 605 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 606 auctionStateMock.EXPECT().InAuction().Return(false).Times(2) 607 statevar := mocks.NewMockStateVarEngine(ctrl) 608 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 609 610 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 611 downFactors := []num.Decimal{pMin1.Div(decPrice), pMin2.Div(decPrice)} 612 upFactors := []num.Decimal{pMax1.Div(decPrice), pMax2.Div(decPrice)} 613 pm.UpdateTestFactors(downFactors, upFactors) 614 615 require.NoError(t, err) 616 require.NotNil(t, pm) 617 618 pm.OnTimeUpdate(now) 619 b := pm.CheckPrice(ctx, auctionStateMock, price1, true, true) 620 require.False(t, b) 621 622 bounds := pm.GetCurrentBounds() 623 require.Len(t, bounds, 2) 624 require.Equal(t, bounds[0].Trigger.IntoProto(), &t1) 625 require.True(t, bounds[0].MinValidPrice.EQ(t1lb1)) 626 require.True(t, bounds[0].MaxValidPrice.EQ(t1ub1)) 627 require.Equal(t, bounds[0].ReferencePrice, decPrice) 628 require.Equal(t, bounds[1].Trigger.IntoProto(), &t2) 629 require.True(t, bounds[1].MinValidPrice.EQ(t2lb1)) 630 require.True(t, bounds[1].MaxValidPrice.EQ(t2ub1)) 631 require.Equal(t, bounds[1].ReferencePrice, decPrice) 632 633 end := types.AuctionDuration{Duration: t1.AuctionExtension} 634 pm.SetMinDuration(time.Duration(end.Duration) * time.Second) 635 auctionStateMock.EXPECT().StartPriceAuction(now, &end).Times(1) 636 637 cPrice := num.Sum(price1, maxUp2) 638 cPrice.Sub(cPrice, maxUp1) 639 cp2 := cPrice 640 b = pm.CheckPrice(ctx, auctionStateMock, cp2, true, true) // t1 violated only 641 require.False(t, b) 642 643 initialAuctionEnd := now.Add(time.Duration(t1.AuctionExtension) * time.Second) 644 645 auctionStateMock.EXPECT().IsFBA().Return(false).Times(1) 646 auctionStateMock.EXPECT().InAuction().Return(true).Times(1) 647 648 bounds = pm.GetCurrentBounds() 649 require.Len(t, bounds, 1) 650 require.Equal(t, bounds[0].Trigger.IntoProto(), &t2) 651 require.True(t, bounds[0].MinValidPrice.EQ(t2lb1)) 652 require.True(t, bounds[0].MaxValidPrice.EQ(t2ub1)) 653 require.Equal(t, bounds[0].ReferencePrice, decPrice) 654 655 afterInitialAuction := initialAuctionEnd.Add(time.Nanosecond) 656 now = afterInitialAuction 657 658 auctionStateMock.EXPECT().ExtendAuctionPrice(gomock.Any()).Times(1) 659 660 cPrice = num.Sum(price1, maxUp2, maxUp1) 661 pm.OnTimeUpdate(afterInitialAuction) 662 cp3 := cPrice 663 b = pm.CheckPrice(ctx, auctionStateMock, cp3, true, true) // price should violated 2nd trigger and result in auction extension 664 require.False(t, b) 665 } 666 667 func TestMarketInOpeningAuction(t *testing.T) { 668 ctrl := gomock.NewController(t) 669 defer ctrl.Finish() 670 riskModel := mocks.NewMockRangeProvider(ctrl) 671 auctionStateMock := mocks.NewMockAuctionState(ctrl) 672 currentPrice := num.NewUint(123) 673 t1 := proto.PriceMonitoringTrigger{Horizon: 7200, Probability: "0.95", AuctionExtension: 300} 674 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 675 pSet := &proto.PriceMonitoringSettings{ 676 Parameters: &proto.PriceMonitoringParameters{ 677 Triggers: []*proto.PriceMonitoringTrigger{&t1}, 678 }, 679 } 680 settings := types.PriceMonitoringSettingsFromProto(pSet) 681 682 ctx := context.Background() 683 684 auctionStateMock.EXPECT().IsFBA().Return(false).Times(1) 685 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 686 auctionStateMock.EXPECT().InAuction().Return(true).Times(1) 687 end := now.Add(time.Second) 688 auctionStateMock.EXPECT().ExpiresAt().Return(&end).Times(1) 689 statevar := mocks.NewMockStateVarEngine(ctrl) 690 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 691 692 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 693 require.NoError(t, err) 694 require.NotNil(t, pm) 695 696 pm.OnTimeUpdate(now) 697 b := pm.CheckPrice(ctx, auctionStateMock, currentPrice, true, true) 698 require.False(t, b) 699 } 700 701 func TestMarketInGenericAuction(t *testing.T) { 702 ctrl := gomock.NewController(t) 703 defer ctrl.Finish() 704 riskModel := mocks.NewMockRangeProvider(ctrl) 705 auctionStateMock := mocks.NewMockAuctionState(ctrl) 706 currentPrice := num.NewUint(123) 707 t1 := proto.PriceMonitoringTrigger{Horizon: 7200, Probability: "0.95", AuctionExtension: 300} 708 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 709 710 pSet := &proto.PriceMonitoringSettings{ 711 Parameters: &proto.PriceMonitoringParameters{ 712 Triggers: []*proto.PriceMonitoringTrigger{&t1}, 713 }, 714 } 715 settings := types.PriceMonitoringSettingsFromProto(pSet) 716 717 _, _, _, maxDown, maxUp := getPriceBounds(currentPrice, 5, 10) 718 one := num.NewUint(1) 719 ctx := context.Background() 720 721 // price monitoring starts with auction, not initialised, so there's no fixed price level it'll check 722 auctionStateMock.EXPECT().IsFBA().Return(false).AnyTimes() 723 auctionStateMock.EXPECT().InAuction().Return(true).Times(4) 724 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 725 auctionStateMock.EXPECT().CanLeave().Return(false).AnyTimes() 726 statevar := mocks.NewMockStateVarEngine(ctrl) 727 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 728 end := now.Add(time.Second) 729 auctionStateMock.EXPECT().ExpiresAt().Return(&end).AnyTimes() 730 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 731 require.NoError(t, err) 732 require.NotNil(t, pm) 733 734 pm.OnTimeUpdate(now) 735 pm.ResetPriceHistory(currentPrice) 736 737 cPrice := num.Sum(currentPrice, maxUp) 738 cPrice.Sub(cPrice, one) 739 b := pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) 740 require.False(t, b) 741 742 cPrice.Sub(num.Sum(currentPrice, one), maxDown) 743 cp3 := cPrice 744 b = pm.CheckPrice(ctx, auctionStateMock, cp3, true, true) 745 require.False(t, b) 746 747 extension := types.AuctionDuration{Duration: t1.AuctionExtension} 748 auctionStateMock.EXPECT().ExtendAuctionPrice(extension).Times(1) 749 cPrice = num.Sum(currentPrice, maxUp, maxUp) 750 cp4 := cPrice 751 b = pm.CheckPrice(ctx, auctionStateMock, cp4, true, true) 752 require.False(t, b) 753 754 cPrice = num.Sum(maxDown, maxDown) 755 cPrice.Sub(currentPrice, cPrice) 756 cp5 := cPrice 757 b = pm.CheckPrice(ctx, auctionStateMock, cp5, true, true) 758 require.False(t, b) 759 } 760 761 func TestGetValidPriceRange_NoTriggers(t *testing.T) { 762 ctrl := gomock.NewController(t) 763 defer ctrl.Finish() 764 riskModel := mocks.NewMockRangeProvider(ctrl) 765 auctionStateMock := mocks.NewMockAuctionState(ctrl) 766 currentPrice := num.NewUint(123) 767 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 768 ctx := context.Background() 769 770 settings := &types.PriceMonitoringSettings{ 771 Parameters: &types.PriceMonitoringParameters{ 772 Triggers: []*types.PriceMonitoringTrigger{}, 773 }, 774 } 775 776 auctionStateMock.EXPECT().IsFBA().Return(false).Times(1) 777 auctionStateMock.EXPECT().InAuction().Return(false).Times(1) 778 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 779 statevar := mocks.NewMockStateVarEngine(ctrl) 780 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 781 782 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 783 require.NoError(t, err) 784 require.NotNil(t, pm) 785 786 expMax := num.MaxUint() 787 min, max := pm.GetValidPriceRange() 788 require.True(t, min.Representation().IsZero()) 789 require.Equal(t, expMax.String(), max.Representation().String()) 790 791 pm.OnTimeUpdate(now) 792 b := pm.CheckPrice(ctx, auctionStateMock, currentPrice, true, true) 793 require.False(t, b) 794 795 min, max = pm.GetValidPriceRange() 796 require.True(t, min.Representation().IsZero()) 797 require.Equal(t, expMax.String(), max.Representation().String()) 798 } 799 800 func TestGetValidPriceRange_2triggers(t *testing.T) { 801 ctrl := gomock.NewController(t) 802 defer ctrl.Finish() 803 riskModel := mocks.NewMockRangeProvider(ctrl) 804 auctionStateMock := mocks.NewMockAuctionState(ctrl) 805 currentPrice := num.NewUint(123) 806 now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC) 807 var t1Time int64 = 60 808 var t2Time int64 = 300 809 t1 := proto.PriceMonitoringTrigger{Horizon: 3600, Probability: "0.99", AuctionExtension: t1Time} 810 t2 := proto.PriceMonitoringTrigger{Horizon: 7200, Probability: "0.95", AuctionExtension: t2Time} 811 pSet := &proto.PriceMonitoringSettings{ 812 Parameters: &proto.PriceMonitoringParameters{ 813 Triggers: []*proto.PriceMonitoringTrigger{&t1, &t2}, 814 }, 815 } 816 settings := types.PriceMonitoringSettingsFromProto(pSet) 817 818 ctx := context.Background() 819 _, pMin1, pMax1, maxDown1, maxUp1 := getPriceBounds(currentPrice, 1, 2) 820 _, pMin2, pMax2, _, _ := getPriceBounds(currentPrice, 3, 4) 821 one := num.NewUint(1) 822 currentPriceD := currentPrice.ToDecimal() 823 auctionStateMock.EXPECT().IsFBA().Return(false).Times(12) 824 auctionStateMock.EXPECT().InAuction().Return(false).Times(12) 825 auctionStateMock.EXPECT().IsPriceAuction().Return(false).Times(1) 826 statevar := mocks.NewMockStateVarEngine(ctrl) 827 statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) 828 829 pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger()) 830 require.NoError(t, err) 831 require.NotNil(t, pm) 832 downFactors := []num.Decimal{pMin1.Div(currentPriceD), pMin2.Div(currentPriceD)} 833 upFactors := []num.Decimal{pMax1.Div(currentPriceD), pMax2.Div(currentPriceD)} 834 835 pm.UpdateTestFactors(downFactors, upFactors) 836 837 pm.OnTimeUpdate(now) 838 b := pm.CheckPrice(ctx, auctionStateMock, currentPrice, true, true) 839 require.False(t, b) 840 841 _, _ = pm.GetValidPriceRange() 842 now = now.Add(time.Second) 843 cPrice := num.Sum(currentPrice, maxUp1) 844 cPrice.Sub(cPrice, one) 845 pm.OnTimeUpdate(now) 846 b = pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) 847 require.False(t, b) 848 849 _, _ = pm.GetValidPriceRange() 850 now = now.Add(time.Minute) 851 cPrice = num.Sum(currentPrice, one) 852 cPrice.Sub(cPrice, maxDown1) 853 pm.OnTimeUpdate(now) 854 b = pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) 855 require.False(t, b) 856 857 _, _ = pm.GetValidPriceRange() 858 now = now.Add(time.Hour) 859 cPrice = num.Sum(currentPrice, maxUp1) 860 cPrice.Sub(cPrice, one) 861 pm.OnTimeUpdate(now) 862 b = pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) 863 require.False(t, b) 864 865 _, _ = pm.GetValidPriceRange() 866 now = now.Add(time.Minute) 867 cPrice.Sub(currentPrice, maxDown1) 868 cPrice.AddSum(one) 869 pm.OnTimeUpdate(now) 870 b = pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) 871 require.False(t, b) 872 873 min, max := pm.GetValidPriceRange() 874 b = pm.CheckPrice(ctx, auctionStateMock, min.Representation(), true, true) 875 require.False(t, b) 876 877 b = pm.CheckPrice(ctx, auctionStateMock, max.Representation(), true, true) 878 require.False(t, b) 879 880 // Should trigger an auction 881 auctionStateMock.EXPECT().StartPriceAuction(now, gomock.Any()).Times(1) 882 883 cPrice.Sub(min.Representation(), one) 884 b = pm.CheckPrice(ctx, auctionStateMock, cPrice, true, true) 885 require.False(t, b) 886 887 now = now.Add(time.Second) 888 pm.OnTimeUpdate(now) 889 b = pm.CheckPrice(ctx, auctionStateMock, currentPrice, true, true) 890 require.False(t, b) 891 892 min, max = pm.GetValidPriceRange() 893 894 b = pm.CheckPrice(ctx, auctionStateMock, min.Representation(), true, true) 895 require.False(t, b) 896 897 b = pm.CheckPrice(ctx, auctionStateMock, max.Representation(), true, true) 898 require.False(t, b) 899 900 // Should trigger an auction 901 auctionStateMock.EXPECT().StartPriceAuction(now, gomock.Any()).Times(1) 902 cPrice.Add(max.Representation(), one) 903 cp11 := cPrice 904 b = pm.CheckPrice(ctx, auctionStateMock, cp11, true, true) 905 require.False(t, b) 906 } 907 908 var secondsPerYear = num.DecimalFromFloat(365.25 * 24 * 60 * 60) 909 910 func getPriceBounds(price *num.Uint, min, max uint64) (decPr, minPr, maxPr num.Decimal, mn, mx *num.Uint) { 911 decPr = price.ToDecimal() 912 mn = num.NewUint(min) 913 mx = num.NewUint(max) 914 minPr = decPr.Sub(mn.ToDecimal()) 915 maxPr = decPr.Add(mx.ToDecimal()) 916 return 917 } 918 919 func horizonToYearFraction(horizon int64) num.Decimal { 920 hdec := num.DecimalFromFloat(float64(horizon)) 921 return hdec.Div(secondsPerYear) 922 }