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  }