code.vegaprotocol.io/vega@v0.79.0/core/monitor/price/snapshot.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 17 18 import ( 19 "sort" 20 21 "code.vegaprotocol.io/vega/core/types" 22 "code.vegaprotocol.io/vega/core/types/statevar" 23 "code.vegaprotocol.io/vega/libs/num" 24 "code.vegaprotocol.io/vega/logging" 25 ) 26 27 func NewMonitorFromSnapshot( 28 marketID string, 29 asset string, 30 pm *types.PriceMonitor, 31 settings *types.PriceMonitoringSettings, 32 riskModel RangeProvider, 33 auctionState AuctionState, 34 stateVarEngine StateVarEngine, 35 log *logging.Logger, 36 ) (*Engine, error) { 37 if riskModel == nil { 38 return nil, ErrNilRangeProvider 39 } 40 if settings == nil { 41 return nil, ErrNilPriceMonitoringSettings 42 } 43 44 priceRangesCache, needRecalc := newPriceRangeCacheFromSlice(pm.PriceRangeCache) 45 46 e := &Engine{ 47 market: marketID, 48 log: log, 49 riskModel: riskModel, 50 auctionState: auctionState, 51 initialised: pm.Initialised, 52 fpHorizons: keyDecimalPairToMap(pm.FPHorizons), 53 now: pm.Now, 54 update: pm.Update, 55 priceRangeCacheTime: pm.PriceRangeCacheTime, 56 refPriceCache: keyDecimalPairToMap(pm.RefPriceCache), 57 refPriceCacheTime: pm.RefPriceCacheTime, 58 bounds: priceBoundsToBounds(pm.Bounds), 59 priceRangesCache: priceRangesCache, 60 pricesNow: pricesNowToInternal(pm.PricesNow), 61 pricesPast: pricesPastToInternal(pm.PricesPast), 62 stateChanged: true, 63 asset: asset, 64 } 65 e.boundFactorsInitialised = pm.PriceBoundsConsensusReached 66 stateVarEngine.RegisterStateVariable(asset, marketID, "bound-factors", boundFactorsConverter{}, e.startCalcPriceRanges, []statevar.EventType{statevar.EventTypeTimeTrigger, statevar.EventTypeAuctionEnded, statevar.EventTypeOpeningAuctionFirstUncrossingPrice}, e.updatePriceBounds) 67 68 if needRecalc { 69 e.getCurrentPriceRanges(true) 70 } 71 return e, nil 72 } 73 74 func pricesNowToInternal(cps []*types.CurrentPrice) []*num.Uint { 75 cpsi := make([]*num.Uint, 0, len(cps)) 76 for _, cp := range cps { 77 cpsi = append(cpsi, cp.Price.Clone()) 78 } 79 return cpsi 80 } 81 82 func pricesPastToInternal(pps []*types.PastPrice) []pastPrice { 83 ppsi := make([]pastPrice, 0, len(pps)) 84 for _, pp := range pps { 85 ppsi = append(ppsi, pastPrice{ 86 Time: pp.Time, 87 AveragePrice: pp.VolumeWeightedPrice, 88 }) 89 } 90 return ppsi 91 } 92 93 func internalBoundToPriceBoundType(b *bound) *types.PriceBound { 94 return &types.PriceBound{ 95 Active: b.Active, 96 UpFactor: b.UpFactor, 97 DownFactor: b.DownFactor, 98 Trigger: b.Trigger.DeepClone(), 99 } 100 } 101 102 func priceBoundTypeToInternal(pb *types.PriceBound) *bound { 103 return &bound{ 104 Active: pb.Active, 105 UpFactor: pb.UpFactor, 106 DownFactor: pb.DownFactor, 107 Trigger: pb.Trigger.DeepClone(), 108 } 109 } 110 111 func mapToKeyDecimalPair(m map[int64]num.Decimal) []*types.KeyDecimalPair { 112 dm := make([]*types.KeyDecimalPair, 0, len(m)) 113 114 for k, v := range m { 115 dm = append(dm, &types.KeyDecimalPair{ 116 Key: k, 117 Val: v, 118 }) 119 } 120 121 sort.Slice(dm, func(i, j int) bool { 122 return dm[i].Key < dm[j].Key 123 }) 124 125 return dm 126 } 127 128 func keyDecimalPairToMap(dms []*types.KeyDecimalPair) map[int64]num.Decimal { 129 m := make(map[int64]num.Decimal, len(dms)) 130 131 for _, dm := range dms { 132 m[dm.Key] = dm.Val 133 } 134 135 return m 136 } 137 138 func priceBoundsToBounds(pbs []*types.PriceBound) []*bound { 139 bounds := make([]*bound, 0, len(pbs)) 140 for _, pb := range pbs { 141 bounds = append(bounds, priceBoundTypeToInternal(pb)) 142 } 143 return bounds 144 } 145 146 func (e *Engine) serialiseBounds() []*types.PriceBound { 147 bounds := make([]*types.PriceBound, 0, len(e.bounds)) 148 for _, b := range e.bounds { 149 bounds = append(bounds, internalBoundToPriceBoundType(b)) 150 } 151 152 return bounds 153 } 154 155 func newPriceRangeCacheFromSlice(prs []*types.PriceRangeCache) (map[int]priceRange, bool) { 156 priceRangesCache := map[int]priceRange{} 157 needsRecalc := false 158 for _, pr := range prs { 159 if pr.BoundIndex < 0 { 160 needsRecalc = true 161 break 162 } 163 priceRangesCache[pr.BoundIndex] = priceRange{ 164 MinPrice: wrapPriceRange(pr.Range.Min, true), 165 MaxPrice: wrapPriceRange(pr.Range.Max, false), 166 ReferencePrice: pr.Range.Ref, 167 } 168 } 169 170 return priceRangesCache, needsRecalc 171 } 172 173 func (e *Engine) serialisePriceRanges() []*types.PriceRangeCache { 174 prc := make([]*types.PriceRangeCache, 0, len(e.priceRangesCache)) 175 for ind, priceRange := range e.priceRangesCache { 176 prc = append(prc, &types.PriceRangeCache{ 177 BoundIndex: ind, 178 Range: &types.PriceRange{ 179 Min: priceRange.MinPrice.Original(), 180 Max: priceRange.MaxPrice.Original(), 181 Ref: priceRange.ReferencePrice, 182 }, 183 }) 184 } 185 186 sort.Slice(prc, func(i, j int) bool { return prc[i].BoundIndex < prc[j].BoundIndex }) 187 return prc 188 } 189 190 func (e *Engine) Changed() bool { 191 return e.stateChanged 192 } 193 194 func (e *Engine) serialisePricesNow() []*types.CurrentPrice { 195 psn := make([]*types.CurrentPrice, 0, len(e.pricesNow)) 196 for _, pn := range e.pricesNow { 197 psn = append(psn, &types.CurrentPrice{ 198 Price: pn.Clone(), 199 Volume: 1, 200 }) 201 } 202 203 sort.Slice(psn, func(i, j int) bool { 204 if psn[i].Price.EQ(psn[j].Price) { 205 return psn[i].Volume < psn[j].Volume 206 } 207 208 return psn[i].Price.LT(psn[j].Price) 209 }) 210 211 return psn 212 } 213 214 func (e *Engine) serialisePricesPast() []*types.PastPrice { 215 pps := make([]*types.PastPrice, 0, len(e.pricesPast)) 216 for _, pp := range e.pricesPast { 217 pps = append(pps, &types.PastPrice{ 218 Time: pp.Time, 219 VolumeWeightedPrice: pp.AveragePrice, 220 }) 221 } 222 223 sort.Slice(pps, func(i, j int) bool { 224 if pps[i].Time.Equal(pps[j].Time) { 225 return pps[j].VolumeWeightedPrice.GreaterThan(pps[i].VolumeWeightedPrice) 226 } 227 228 return pps[i].Time.Before(pps[j].Time) 229 }) 230 231 return pps 232 } 233 234 func (e *Engine) GetState() *types.PriceMonitor { 235 pm := &types.PriceMonitor{ 236 Initialised: e.initialised, 237 FPHorizons: mapToKeyDecimalPair(e.fpHorizons), 238 Now: e.now, 239 Update: e.update, 240 Bounds: e.serialiseBounds(), 241 PriceRangeCache: e.serialisePriceRanges(), 242 PricesNow: e.serialisePricesNow(), 243 PricesPast: e.serialisePricesPast(), 244 PriceRangeCacheTime: e.priceRangeCacheTime, 245 RefPriceCache: mapToKeyDecimalPair(e.refPriceCache), 246 RefPriceCacheTime: e.refPriceCacheTime, 247 PriceBoundsConsensusReached: e.boundFactorsInitialised, 248 } 249 250 e.stateChanged = false 251 252 return pm 253 }