Skip to content

Commit

Permalink
Merge pull request #11040 from vegaprotocol/11038
Browse files Browse the repository at this point in the history
fix: simplify price range internal and serialised state
  • Loading branch information
ze97286 authored and jeremyletang committed May 24, 2024
1 parent 7ecac47 commit ca34543
Show file tree
Hide file tree
Showing 7 changed files with 1,699 additions and 1,785 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# Changelog

## 0.75.8

### 🐛 Fixes

- [11038](https://github.com/vegaprotocol/vega/issues/11038) - Simplify price ranges state.


## 0.75.7

### 🐛 Fixes

- [11042](https://github.com/vegaprotocol/vega/issues/11042) - Add missing value to enum.
- [10928](https://github.com/vegaprotocol/vega/issues/10928) - Fix `collateralIncreaseEstimate` for limit orders in isolated margin mode


## 0.75.4

### 🐛 Fixes
Expand All @@ -15,12 +23,14 @@
- [10997](https://github.com/vegaprotocol/vega/issues/10997) - Handle cases where leaving opening auction triggers monitoring auction.
- [10958](https://github.com/vegaprotocol/vega/issues/10958) - Fix incorrect documentation.


## 0.75.3

### 🐛 Fixes

- [10969](https://github.com/vegaprotocol/vega/issues/10969) - Ensure teams statistics are computed from team rewards.
- [10973](https://github.com/vegaprotocol/vega/issues/10973) - Avoid entering an auction or doing mark-to-market before market entered opening auction or after it is in a terminal state.
- [10973](https://github.com/vegaprotocol/vega/issues/10973) - Avoid entering an auction or doing mark-to-market before market entered opening auction or after it is in a terminal state.


## 0.75.2

Expand All @@ -29,6 +39,7 @@
- [10960](https://github.com/vegaprotocol/vega/issues/10960) - Only the owner of a referral set can update it.
- [10950](https://github.com/vegaprotocol/vega/issues/10950) - Fix bug that caused cancelled liquidity provisions to stick around after opening auction.


## 0.75.1

### 🐛 Fixes
Expand Down
19 changes: 10 additions & 9 deletions core/monitor/price/pricemonitoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ type Engine struct {
bounds []*bound

priceRangeCacheTime time.Time
priceRangesCache map[*bound]priceRange
priceRangesCache map[int]priceRange

refPriceCacheTime time.Time
refPriceCache map[int64]num.Decimal
Expand All @@ -152,7 +152,7 @@ func (e *Engine) UpdateSettings(riskModel risk.Model, settings *types.PriceMonit
e.fpHorizons, e.bounds = computeBoundsAndHorizons(settings)
e.initialised = false
e.boundFactorsInitialised = false
e.priceRangesCache = make(map[*bound]priceRange, len(e.bounds)) // clear the cache
e.priceRangesCache = make(map[int]priceRange, len(e.bounds)) // clear the cache
// reset reference cache
e.refPriceCacheTime = time.Time{}
e.refPriceCache = map[int64]num.Decimal{}
Expand Down Expand Up @@ -232,7 +232,8 @@ func (e *Engine) GetValidPriceRange() (num.WrappedDecimal, num.WrappedDecimal) {
func (e *Engine) GetCurrentBounds() []*types.PriceMonitoringBounds {
priceRanges := e.getCurrentPriceRanges(false)
ret := make([]*types.PriceMonitoringBounds, 0, len(priceRanges))
for b, pr := range priceRanges {
for ind, pr := range priceRanges {
b := e.bounds[ind]
if b.Active {
ret = append(ret,
&types.PriceMonitoringBounds{
Expand Down Expand Up @@ -439,12 +440,12 @@ func (e *Engine) checkBounds(trades []*types.Trade) []*types.PriceMonitoringTrig
if t.Size == 0 {
continue
}
for _, b := range e.bounds {
for i, b := range e.bounds {
if !b.Active {
continue
}
p := t.Price
priceRange := priceRanges[b]
priceRange := priceRanges[i]
if p.LT(priceRange.MinPrice.Representation()) || p.GT(priceRange.MaxPrice.Representation()) {
ret = append(ret, b.Trigger)
// deactivate the bound that just got violated so it doesn't prevent auction from terminating
Expand All @@ -458,15 +459,15 @@ func (e *Engine) checkBounds(trades []*types.Trade) []*types.PriceMonitoringTrig
}

// getCurrentPriceRanges calculates price ranges from current reference prices and bound down/up factors.
func (e *Engine) getCurrentPriceRanges(force bool) map[*bound]priceRange {
func (e *Engine) getCurrentPriceRanges(force bool) map[int]priceRange {
if !force && e.priceRangeCacheTime == e.now && len(e.priceRangesCache) > 0 {
return e.priceRangesCache
}
ranges := make(map[*bound]priceRange, len(e.priceRangesCache))
ranges := make(map[int]priceRange, len(e.priceRangesCache))
if e.noHistory() {
return ranges
}
for _, b := range e.bounds {
for i, b := range e.bounds {
if !b.Active {
continue
}
Expand Down Expand Up @@ -496,7 +497,7 @@ func (e *Engine) getCurrentPriceRanges(force bool) map[*bound]priceRange {
max = ref.Mul(defaultUpFactor)
}

ranges[b] = priceRange{
ranges[i] = priceRange{
MinPrice: wrapPriceRange(min, true),
MaxPrice: wrapPriceRange(max, false),
ReferencePrice: ref,
Expand Down
45 changes: 22 additions & 23 deletions core/monitor/price/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func NewMonitorFromSnapshot(
return nil, ErrNilPriceMonitoringSettings
}

priceRangesCache, needRecalc := newPriceRangeCacheFromSlice(pm.PriceRangeCache)

e := &Engine{
market: marketID,
log: log,
Expand All @@ -54,14 +56,18 @@ func NewMonitorFromSnapshot(
refPriceCache: keyDecimalPairToMap(pm.RefPriceCache),
refPriceCacheTime: pm.RefPriceCacheTime,
bounds: priceBoundsToBounds(pm.Bounds),
priceRangesCache: newPriceRangeCacheFromSlice(pm.PriceRangeCache),
priceRangesCache: priceRangesCache,
pricesNow: pricesNowToInternal(pm.PricesNow),
pricesPast: pricesPastToInternal(pm.PricesPast),
stateChanged: true,
asset: asset,
}
e.boundFactorsInitialised = pm.PriceBoundsConsensusReached
stateVarEngine.RegisterStateVariable(asset, marketID, "bound-factors", boundFactorsConverter{}, e.startCalcPriceRanges, []statevar.EventType{statevar.EventTypeTimeTrigger, statevar.EventTypeAuctionEnded, statevar.EventTypeOpeningAuctionFirstUncrossingPrice}, e.updatePriceBounds)

if needRecalc {
e.getCurrentPriceRanges(true)
}
return e, nil
}

Expand Down Expand Up @@ -149,24 +155,29 @@ func (e *Engine) serialiseBounds() []*types.PriceBound {
return bounds
}

func newPriceRangeCacheFromSlice(prs []*types.PriceRangeCache) map[*bound]priceRange {
priceRangesCache := map[*bound]priceRange{}
func newPriceRangeCacheFromSlice(prs []*types.PriceRangeCache) (map[int]priceRange, bool) {
priceRangesCache := map[int]priceRange{}
needsRecalc := false
for _, pr := range prs {
priceRangesCache[priceBoundTypeToInternal(pr.Bound)] = priceRange{
if pr.BoundIndex < 0 {
needsRecalc = true
break
}
priceRangesCache[pr.BoundIndex] = priceRange{
MinPrice: wrapPriceRange(pr.Range.Min, true),
MaxPrice: wrapPriceRange(pr.Range.Max, false),
ReferencePrice: pr.Range.Ref,
}
}
return priceRangesCache

return priceRangesCache, needsRecalc
}

// SerialisePriceranges expored for testing.
func (e *Engine) SerialisePriceRanges() []*types.PriceRangeCache {
func (e *Engine) serialisePriceRanges() []*types.PriceRangeCache {
prc := make([]*types.PriceRangeCache, 0, len(e.priceRangesCache))
for bound, priceRange := range e.priceRangesCache {
for ind, priceRange := range e.priceRangesCache {
prc = append(prc, &types.PriceRangeCache{
Bound: internalBoundToPriceBoundType(bound),
BoundIndex: ind,
Range: &types.PriceRange{
Min: priceRange.MinPrice.Original(),
Max: priceRange.MaxPrice.Original(),
Expand All @@ -175,19 +186,7 @@ func (e *Engine) SerialisePriceRanges() []*types.PriceRangeCache {
})
}

sort.SliceStable(prc, func(i, j int) bool {
if prc[i].Bound.Active != prc[j].Bound.Active {
return prc[i].Bound.Active
}
if prc[i].Bound.UpFactor.Equal(prc[j].Bound.UpFactor) {
if prc[i].Bound.DownFactor.Equal(prc[j].Bound.DownFactor) {
return prc[i].Bound.Trigger.Horizon < prc[j].Bound.Trigger.Horizon
}
return prc[j].Bound.DownFactor.LessThan(prc[i].Bound.DownFactor)
}

return prc[j].Bound.UpFactor.GreaterThan(prc[i].Bound.UpFactor)
})
sort.Slice(prc, func(i, j int) bool { return prc[i].BoundIndex < prc[j].BoundIndex })

return prc
}
Expand Down Expand Up @@ -243,7 +242,7 @@ func (e *Engine) GetState() *types.PriceMonitor {
Now: e.now,
Update: e.update,
Bounds: e.serialiseBounds(),
PriceRangeCache: e.SerialisePriceRanges(),
PriceRangeCache: e.serialisePriceRanges(),
PricesNow: e.serialisePricesNow(),
PricesPast: e.serialisePricesPast(),
PriceRangeCacheTime: e.priceRangeCacheTime,
Expand Down
113 changes: 0 additions & 113 deletions core/monitor/price/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,116 +206,3 @@ func TestRestorePriceBoundRepresentation(t *testing.T) {
require.Equal(t, min, sMin)
require.Equal(t, max, sMax)
}

func TestSerialiseBoundsDeterministically(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
riskModel := mocks.NewMockRangeProvider(ctrl)
auctionStateMock := mocks.NewMockAuctionState(ctrl)
currentPrice := num.NewUint(123)
now := time.Date(1993, 2, 2, 6, 0, 0, 1, time.UTC)

settings := types.PriceMonitoringSettingsFromProto(&vegapb.PriceMonitoringSettings{
Parameters: &vegapb.PriceMonitoringParameters{
Triggers: []*vegapb.PriceMonitoringTrigger{
{Horizon: 3600, Probability: "0.99", AuctionExtension: 60},
{Horizon: 3600, Probability: "0.99", AuctionExtension: 60},
{Horizon: 3600, Probability: "0.99", AuctionExtension: 60},
{Horizon: 3600, Probability: "0.99", AuctionExtension: 60},
{Horizon: 3600, Probability: "0.99", AuctionExtension: 60},
{Horizon: 7200, Probability: "0.95", AuctionExtension: 300},
{Horizon: 7200, Probability: "0.95", AuctionExtension: 300},
{Horizon: 7200, Probability: "0.95", AuctionExtension: 300},
{Horizon: 7200, Probability: "0.95", AuctionExtension: 300},
{Horizon: 7200, Probability: "0.95", AuctionExtension: 300},
},
},
})

_, pMin1, pMax1, _, _ := getPriceBounds(currentPrice, 1, 2)
_, pMin2, pMax2, _, _ := getPriceBounds(currentPrice, 3, 4)
currentPriceD := currentPrice.ToDecimal()
auctionStateMock.EXPECT().IsFBA().Return(false).AnyTimes()
auctionStateMock.EXPECT().InAuction().Return(false).AnyTimes()
auctionStateMock.EXPECT().IsPriceAuction().Return(false).AnyTimes()
statevar := mocks.NewMockStateVarEngine(ctrl)
statevar.EXPECT().RegisterStateVariable(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()

pm, err := price.NewMonitor("asset", "market", riskModel, auctionStateMock, settings, statevar, logging.NewTestLogger())
require.NoError(t, err)
require.NotNil(t, pm)
downFactors := []num.Decimal{
pMin1.Div(currentPriceD),
pMin1.Div(currentPriceD),
pMin1.Div(currentPriceD),
pMin1.Div(currentPriceD),
pMin1.Div(currentPriceD),
pMin2.Div(currentPriceD),
pMin2.Div(currentPriceD),
pMin2.Div(currentPriceD),
pMin2.Div(currentPriceD),
pMin2.Div(currentPriceD),
}
upFactors := []num.Decimal{
pMax1.Div(currentPriceD),
pMax1.Div(currentPriceD),
pMax1.Div(currentPriceD),
pMax1.Div(currentPriceD),
pMax1.Div(currentPriceD),
pMax2.Div(currentPriceD),
pMax2.Div(currentPriceD),
pMax2.Div(currentPriceD),
pMax2.Div(currentPriceD),
pMax2.Div(currentPriceD),
}

pm.UpdateTestFactors(downFactors, upFactors)

pm.OnTimeUpdate(now)
b := pm.CheckPrice(context.Background(), auctionStateMock, []*types.Trade{{Price: currentPrice, Size: 1}}, true, true)
require.False(t, b)

bounds := pm.GetCurrentBounds()
require.NotEmpty(t, bounds)
minP := bounds[0].MinValidPrice.Clone()
minP.Sub(minP, num.UintOne())
auctionStateMock.EXPECT().StartPriceAuction(gomock.Any(), gomock.Any()).Times(1)
b = pm.CheckPrice(context.Background(), auctionStateMock, []*types.Trade{{Price: minP, Size: 1}}, true, true)
require.False(t, b)

pBounds := pm.SerialisePriceRanges()
// now get state
state := pm.GetState()
snap, err := price.NewMonitorFromSnapshot("market", "asset", state, settings, riskModel, auctionStateMock, statevar, logging.NewTestLogger())
require.NoError(t, err)

sBounds := snap.SerialisePriceRanges()
require.Equal(t, len(pBounds), len(sBounds))
// ensure the inactive bound is at the back of the slice
require.False(t, pBounds[len(pBounds)-1].Bound.Active)
require.False(t, sBounds[len(sBounds)-1].Bound.Active)
for i := 0; i < len(sBounds); i++ {
pBound, sBound := pBounds[i], sBounds[i]
require.EqualValues(t, pBound, sBound)
}
// Now repeat the test above, but change the state to move the inactive bound back by one each time
for i := len(state.PriceRangeCache) - 1; i < 0; i-- {
// move the inactive price bound back by one
state.PriceRangeCache[i], state.PriceRangeCache[i-1] = state.PriceRangeCache[i-1], state.PriceRangeCache[i]
// sanity-check, make sure the inactive bound is now no longer the last element, and is where we expecti it to be
require.False(t, state.PriceRangeCache[i-1].Bound.Active)
require.True(t, state.PriceRangeCache[i].Bound.Active)
// always make sure the last element is active
require.True(t, state.PriceRangeCache[len(state.PriceRangeCache)-1].Bound.Active)
snap, err := price.NewMonitorFromSnapshot("market", "asset", state, settings, riskModel, auctionStateMock, statevar, logging.NewTestLogger())
require.NoError(t, err)
sBounds := snap.SerialisePriceRanges()
require.Equal(t, len(pBounds), len(sBounds))
// the inactive bound must be the last one
require.False(t, sBounds[len(sBounds)-1].Bound.Active)
for i := 0; i < len(sBounds); i++ {
pBound, sBound := pBounds[i], sBounds[i]
require.EqualValues(t, pBound, sBound)
}
}
}
17 changes: 11 additions & 6 deletions core/types/snapshot_nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,9 @@ type PriceBound struct {
}

type PriceRangeCache struct {
Bound *PriceBound
Range *PriceRange
BoundIndex int
Bound *PriceBound
Range *PriceRange
}

type PriceRange struct {
Expand Down Expand Up @@ -3430,16 +3431,20 @@ func (p PriceRange) IntoProto() *snapshot.PriceRange {
}

func PriceRangeCacheFromProto(prc *snapshot.PriceRangeCache) *PriceRangeCache {
BoundIndex := int(prc.BoundIndex)
if prc.Bound != nil {
BoundIndex = -1
}
return &PriceRangeCache{
Bound: PriceBoundFromProto(prc.Bound),
Range: PriceRangeFromProto(prc.Range),
BoundIndex: BoundIndex,
Range: PriceRangeFromProto(prc.Range),
}
}

func (p PriceRangeCache) IntoProto() *snapshot.PriceRangeCache {
return &snapshot.PriceRangeCache{
Bound: p.Bound.IntoProto(),
Range: p.Range.IntoProto(),
BoundIndex: uint64(p.BoundIndex),
Range: p.Range.IntoProto(),
}
}

Expand Down
1 change: 1 addition & 0 deletions protos/sources/vega/snapshot/v1/snapshot.proto
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ message PriceBound {
message PriceRangeCache {
PriceBound bound = 1;
PriceRange range = 2;
uint64 bound_index = 3;
}

message CurrentPrice {
Expand Down
Loading

0 comments on commit ca34543

Please sign in to comment.