code.vegaprotocol.io/vega@v0.79.0/core/liquidity/v2/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 liquidity
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sort"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/events"
    25  	"code.vegaprotocol.io/vega/core/types"
    26  	"code.vegaprotocol.io/vega/libs/num"
    27  	"code.vegaprotocol.io/vega/libs/proto"
    28  	"code.vegaprotocol.io/vega/logging"
    29  	typespb "code.vegaprotocol.io/vega/protos/vega"
    30  	snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    31  )
    32  
    33  const defaultFeeCalculationTimeStep = time.Minute
    34  
    35  type SnapshotEngine struct {
    36  	*Engine
    37  
    38  	*snapshotV2
    39  	*snapshotV1
    40  }
    41  
    42  func (e SnapshotEngine) OnEpochRestore(ep types.Epoch) {
    43  	e.slaEpochStart = ep.StartTime
    44  }
    45  
    46  func (e SnapshotEngine) V2StateProvider() types.StateProvider {
    47  	return e.snapshotV2
    48  }
    49  
    50  func (e SnapshotEngine) V1StateProvider() types.StateProvider {
    51  	return e.snapshotV1
    52  }
    53  
    54  func (e SnapshotEngine) StopSnapshots() {
    55  	e.snapshotV1.Stop()
    56  	e.snapshotV2.Stop()
    57  }
    58  
    59  type snapshotV2 struct {
    60  	*Engine
    61  
    62  	pl     types.Payload
    63  	market string
    64  
    65  	// liquidity types
    66  	stopped                     bool
    67  	serialisedProvisions        []byte
    68  	serialisedPendingProvisions []byte
    69  	serialisedPerformances      []byte
    70  	serialisedSupplied          []byte
    71  	serialisedScores            []byte
    72  	serialisedParemeters        []byte
    73  	serialisedFeeStats          []byte
    74  
    75  	// Keys need to be computed when the engine is instantiated as they are dynamic.
    76  	hashKeys             []string
    77  	provisionsKey        string
    78  	pendingProvisionsKey string
    79  	performancesKey      string
    80  	scoresKey            string
    81  	suppliedKey          string
    82  	paramsKey            string
    83  	feeStatsKey          string
    84  }
    85  
    86  func (e *snapshotV2) Namespace() types.SnapshotNamespace {
    87  	return types.LiquidityV2Snapshot
    88  }
    89  
    90  func (e *snapshotV2) Keys() []string {
    91  	return e.hashKeys
    92  }
    93  
    94  func (e *snapshotV2) GetState(k string) ([]byte, []types.StateProvider, error) {
    95  	state, err := e.serialise(k)
    96  	return state, nil, err
    97  }
    98  
    99  func (e *snapshotV2) LoadState(ctx context.Context, p *types.Payload) ([]types.StateProvider, error) {
   100  	if e.Namespace() != p.Data.Namespace() {
   101  		return nil, types.ErrInvalidSnapshotNamespace
   102  	}
   103  
   104  	switch pl := p.Data.(type) {
   105  	case *types.PayloadLiquidityV2Provisions:
   106  		return nil, e.loadProvisions(ctx, pl.Provisions.GetLiquidityProvisions(), p)
   107  	case *types.PayloadLiquidityV2PendingProvisions:
   108  		return nil, e.loadPendingProvisions(ctx, pl.PendingProvisions.GetPendingLiquidityProvisions(), p)
   109  	case *types.PayloadLiquidityV2Performances:
   110  		return nil, e.loadPerformances(pl.Performances, p)
   111  	case *types.PayloadLiquidityV2Supplied:
   112  		return nil, e.loadSupplied(pl.Supplied, p)
   113  	case *types.PayloadLiquidityV2Scores:
   114  		return nil, e.loadScores(pl.Scores, p)
   115  	case *types.PayloadLiquidityV2Parameters:
   116  		return nil, e.loadParameters(pl.Parameters, p)
   117  	case *types.PayloadPaidLiquidityV2FeeStats:
   118  		e.loadFeeStats(pl.Stats, p)
   119  		return nil, nil
   120  	default:
   121  		return nil, types.ErrUnknownSnapshotType
   122  	}
   123  }
   124  
   125  func (e *snapshotV2) Stopped() bool {
   126  	return e.stopped
   127  }
   128  
   129  func (e *snapshotV2) Stop() {
   130  	e.log.Debug("market has been cleared, stopping snapshot production", logging.MarketID(e.marketID))
   131  	e.stopped = true
   132  }
   133  
   134  func (e *snapshotV2) serialise(k string) ([]byte, error) {
   135  	var (
   136  		buf []byte
   137  		err error
   138  	)
   139  
   140  	switch k {
   141  	case e.provisionsKey:
   142  		buf, err = e.serialiseProvisions()
   143  	case e.pendingProvisionsKey:
   144  		buf, err = e.serialisePendingProvisions()
   145  	case e.performancesKey:
   146  		buf, err = e.serialisePerformances()
   147  	case e.suppliedKey:
   148  		buf, err = e.serialiseSupplied()
   149  	case e.scoresKey:
   150  		buf, err = e.serialiseScores()
   151  	case e.paramsKey:
   152  		buf, err = e.serialiseParameters()
   153  	case e.feeStatsKey:
   154  		buf, err = e.serialiseFeeStats()
   155  	default:
   156  		return nil, types.ErrSnapshotKeyDoesNotExist
   157  	}
   158  
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	if e.stopped {
   164  		return nil, nil
   165  	}
   166  
   167  	switch k {
   168  	case e.provisionsKey:
   169  		e.serialisedProvisions = buf
   170  	case e.pendingProvisionsKey:
   171  		e.serialisedPendingProvisions = buf
   172  	case e.performancesKey:
   173  		e.serialisedPerformances = buf
   174  	case e.suppliedKey:
   175  		e.serialisedSupplied = buf
   176  	case e.scoresKey:
   177  		e.serialisedScores = buf
   178  	case e.paramsKey:
   179  		e.serialisedParemeters = buf
   180  	case e.feeStatsKey:
   181  		e.serialisedFeeStats = buf
   182  	default:
   183  		return nil, types.ErrSnapshotKeyDoesNotExist
   184  	}
   185  
   186  	return buf, nil
   187  }
   188  
   189  func (e *snapshotV2) serialiseProvisions() ([]byte, error) {
   190  	// these are sorted already, only a conversion to proto is needed
   191  	lps := e.Engine.provisions.Slice()
   192  	pblps := make([]*typespb.LiquidityProvision, 0, len(lps))
   193  	for _, v := range lps {
   194  		pblps = append(pblps, v.IntoProto())
   195  	}
   196  
   197  	payload := &snapshotpb.Payload{
   198  		Data: &snapshotpb.Payload_LiquidityV2Provisions{
   199  			LiquidityV2Provisions: &snapshotpb.LiquidityV2Provisions{
   200  				MarketId:            e.market,
   201  				LiquidityProvisions: pblps,
   202  			},
   203  		},
   204  	}
   205  
   206  	return e.marshalPayload(payload)
   207  }
   208  
   209  func (e *snapshotV2) serialisePendingProvisions() ([]byte, error) {
   210  	// these are sorted already, only a conversion to proto is needed
   211  	lps := e.Engine.pendingProvisions.Slice()
   212  	pblps := make([]*typespb.LiquidityProvision, 0, len(lps))
   213  	for _, v := range lps {
   214  		pblps = append(pblps, v.IntoProto())
   215  	}
   216  
   217  	payload := &snapshotpb.Payload{
   218  		Data: &snapshotpb.Payload_LiquidityV2PendingProvisions{
   219  			LiquidityV2PendingProvisions: &snapshotpb.LiquidityV2PendingProvisions{
   220  				MarketId:                   e.market,
   221  				PendingLiquidityProvisions: pblps,
   222  			},
   223  		},
   224  	}
   225  
   226  	return e.marshalPayload(payload)
   227  }
   228  
   229  func (e *snapshotV2) serialisePerformances() ([]byte, error) {
   230  	// Extract and sort the parties to serialize a deterministic array.
   231  	parties := make([]string, 0, len(e.slaPerformance))
   232  	for party := range e.slaPerformance {
   233  		parties = append(parties, party)
   234  	}
   235  	sort.Strings(parties)
   236  
   237  	performancePerPartySnapshot := make([]*snapshotpb.LiquidityV2PerformancePerParty, 0, len(e.slaPerformance))
   238  	for _, party := range parties {
   239  		partyPerformance := e.slaPerformance[party]
   240  
   241  		trueLen := 0
   242  		registeredPenaltiesPerEpochSnapshot := make([]string, 0, partyPerformance.previousPenalties.Len())
   243  		for _, registeredPenalty := range partyPerformance.previousPenalties.Slice() {
   244  			if registeredPenalty != nil {
   245  				trueLen++
   246  				registeredPenaltiesPerEpochSnapshot = append(registeredPenaltiesPerEpochSnapshot, registeredPenalty.String())
   247  			}
   248  		}
   249  		registeredPenaltiesPerEpochSnapshot = registeredPenaltiesPerEpochSnapshot[0:trueLen]
   250  
   251  		var start int64
   252  		if partyPerformance.start != (time.Time{}) {
   253  			start = partyPerformance.start.UnixNano()
   254  		}
   255  
   256  		partyPerformanceSnapshot := &snapshotpb.LiquidityV2PerformancePerParty{
   257  			Party:                            party,
   258  			ElapsedTimeMeetingSlaDuringEpoch: int64(partyPerformance.s),
   259  			CommitmentStartTime:              start,
   260  			RegisteredPenaltiesPerEpoch:      registeredPenaltiesPerEpochSnapshot,
   261  			PositionInPenaltiesPerEpoch:      uint32(partyPerformance.previousPenalties.Position()),
   262  			LastEpochFractionOfTimeOnBook:    partyPerformance.lastEpochTimeBookFraction,
   263  			LastEpochFeePenalty:              partyPerformance.lastEpochFeePenalty,
   264  			LastEpochBondPenalty:             partyPerformance.lastEpochBondPenalty,
   265  			RequiredLiquidity:                partyPerformance.requiredLiquidity,
   266  			NotionalVolumeBuys:               partyPerformance.notionalVolumeBuys,
   267  			NotionalVolumeSells:              partyPerformance.notionalVolumeSells,
   268  		}
   269  
   270  		performancePerPartySnapshot = append(performancePerPartySnapshot, partyPerformanceSnapshot)
   271  	}
   272  
   273  	payload := &snapshotpb.Payload{
   274  		Data: &snapshotpb.Payload_LiquidityV2Performances{
   275  			LiquidityV2Performances: &snapshotpb.LiquidityV2Performances{
   276  				MarketId:            e.market,
   277  				EpochStartTime:      e.slaEpochStart.UnixNano(),
   278  				PerformancePerParty: performancePerPartySnapshot,
   279  			},
   280  		},
   281  	}
   282  
   283  	return e.marshalPayload(payload)
   284  }
   285  
   286  func (e *snapshotV2) serialiseSupplied() ([]byte, error) {
   287  	v1Payload := e.suppliedEngine.Payload()
   288  
   289  	// Dirty hack to support serialization of a mutualized supplied engine between
   290  	// liquidity engine version 1 and 2.
   291  	supplied := v1Payload.GetLiquiditySupplied()
   292  	return e.marshalPayload(&snapshotpb.Payload{
   293  		Data: &snapshotpb.Payload_LiquidityV2Supplied{
   294  			LiquidityV2Supplied: &snapshotpb.LiquidityV2Supplied{
   295  				MarketId:         supplied.MarketId,
   296  				ConsensusReached: supplied.ConsensusReached,
   297  				BidCache:         supplied.BidCache,
   298  				AskCache:         supplied.AskCache,
   299  			},
   300  		},
   301  	})
   302  }
   303  
   304  func (e *snapshotV2) serialiseScores() ([]byte, error) {
   305  	scores := make([]*snapshotpb.LiquidityScore, 0, len(e.avgScores))
   306  
   307  	keys := make([]string, 0, len(e.avgScores))
   308  	for k := range e.avgScores {
   309  		keys = append(keys, k)
   310  	}
   311  	sort.Strings(keys)
   312  
   313  	for _, k := range keys {
   314  		s := &snapshotpb.LiquidityScore{
   315  			PartyId: k,
   316  			Score:   e.avgScores[k].String(),
   317  		}
   318  		scores = append(scores, s)
   319  	}
   320  
   321  	var lastFeeDistributionTime int64
   322  	if !e.lastFeeDistribution.IsZero() {
   323  		lastFeeDistributionTime = e.lastFeeDistribution.UnixNano()
   324  	}
   325  
   326  	var feeCalculationTimeStep time.Duration
   327  	if e.feeCalculationTimeStep != 0 {
   328  		feeCalculationTimeStep = e.feeCalculationTimeStep
   329  	} else {
   330  		feeCalculationTimeStep = defaultFeeCalculationTimeStep
   331  	}
   332  
   333  	payload := &snapshotpb.Payload{
   334  		Data: &snapshotpb.Payload_LiquidityV2Scores{
   335  			LiquidityV2Scores: &snapshotpb.LiquidityV2Scores{
   336  				MarketId:                e.market,
   337  				RunningAverageCounter:   int32(e.nAvg),
   338  				Scores:                  scores,
   339  				LastFeeDistributionTime: lastFeeDistributionTime,
   340  				FeeCalculationTimeStep:  int64(feeCalculationTimeStep),
   341  			},
   342  		},
   343  	}
   344  
   345  	return e.marshalPayload(payload)
   346  }
   347  
   348  func (e *snapshotV2) serialiseParameters() ([]byte, error) {
   349  	payload := &snapshotpb.Payload{
   350  		Data: &snapshotpb.Payload_LiquidityV2Parameters{
   351  			LiquidityV2Parameters: &snapshotpb.LiquidityV2Parameters{
   352  				MarketId:            e.market,
   353  				MarketSlaParameters: e.slaParams.IntoProto(),
   354  				StakeToVolume:       e.stakeToCcyVolume.String(),
   355  				BondPenaltySlope:    e.nonPerformanceBondPenaltySlope.String(),
   356  				BondPenaltyMax:      e.nonPerformanceBondPenaltyMax.String(),
   357  			},
   358  		},
   359  	}
   360  
   361  	return e.marshalPayload(payload)
   362  }
   363  
   364  func (e *snapshotV2) serialiseFeeStats() ([]byte, error) {
   365  	payload := &snapshotpb.Payload{
   366  		Data: &snapshotpb.Payload_LiquidityV2PaidFeesStats{
   367  			LiquidityV2PaidFeesStats: &snapshotpb.LiquidityV2PaidFeesStats{
   368  				MarketId: e.market,
   369  				Stats:    e.allocatedFeesStats.ToProto(e.market, e.asset, 0), // I don't think it matters what the epoch is as this is just used for snapshots
   370  			},
   371  		},
   372  	}
   373  
   374  	return e.marshalPayload(payload)
   375  }
   376  
   377  func (e *snapshotV2) marshalPayload(payload *snapshotpb.Payload) ([]byte, error) {
   378  	buf, err := proto.Marshal(payload)
   379  	if err != nil {
   380  		return nil, err
   381  	}
   382  
   383  	return buf, nil
   384  }
   385  
   386  func (e *snapshotV2) loadProvisions(ctx context.Context, provisions []*typespb.LiquidityProvision, p *types.Payload) error {
   387  	e.Engine.provisions = newSnapshotableProvisionsPerParty()
   388  
   389  	evts := make([]events.Event, 0, len(provisions))
   390  	for _, v := range provisions {
   391  		provision, err := types.LiquidityProvisionFromProto(v)
   392  		if err != nil {
   393  			return err
   394  		}
   395  		e.Engine.provisions.Set(v.PartyId, provision)
   396  		evts = append(evts, events.NewLiquidityProvisionEvent(ctx, provision))
   397  	}
   398  
   399  	var err error
   400  	e.serialisedProvisions, err = proto.Marshal(p.IntoProto())
   401  	e.broker.SendBatch(evts)
   402  	return err
   403  }
   404  
   405  func (e *snapshotV2) loadPendingProvisions(ctx context.Context, provisions []*typespb.LiquidityProvision, p *types.Payload) error {
   406  	e.Engine.pendingProvisions = newSnapshotablePendingProvisions()
   407  
   408  	evts := make([]events.Event, 0, len(provisions))
   409  	for _, v := range provisions {
   410  		provision, err := types.LiquidityProvisionFromProto(v)
   411  		if err != nil {
   412  			return err
   413  		}
   414  		e.Engine.pendingProvisions.Set(provision)
   415  		evts = append(evts, events.NewLiquidityProvisionEvent(ctx, provision))
   416  	}
   417  
   418  	var err error
   419  	e.serialisedPendingProvisions, err = proto.Marshal(p.IntoProto())
   420  	e.broker.SendBatch(evts)
   421  	return err
   422  }
   423  
   424  func (e *snapshotV2) loadPerformances(performances *snapshotpb.LiquidityV2Performances, p *types.Payload) error {
   425  	var err error
   426  
   427  	e.Engine.slaEpochStart = time.Unix(0, performances.EpochStartTime)
   428  
   429  	e.Engine.slaPerformance = map[string]*slaPerformance{}
   430  	for _, partyPerformance := range performances.PerformancePerParty {
   431  		registeredPenaltiesPerEpochAsDecimal := make([]*num.Decimal, 0, len(partyPerformance.RegisteredPenaltiesPerEpoch))
   432  		for _, registeredPenalty := range partyPerformance.RegisteredPenaltiesPerEpoch {
   433  			registeredPenaltyAsDecimal, err := num.DecimalFromString(registeredPenalty)
   434  			if err != nil {
   435  				return fmt.Errorf("invalid penalty %q for party %q on market %q: %w", registeredPenalty, partyPerformance.Party, performances.MarketId, err)
   436  			}
   437  			registeredPenaltiesPerEpochAsDecimal = append(registeredPenaltiesPerEpochAsDecimal, &registeredPenaltyAsDecimal)
   438  		}
   439  
   440  		previousPenalties := restoreSliceRing[*num.Decimal](
   441  			registeredPenaltiesPerEpochAsDecimal,
   442  			e.Engine.slaParams.PerformanceHysteresisEpochs,
   443  			int(partyPerformance.PositionInPenaltiesPerEpoch),
   444  		)
   445  
   446  		var startTime time.Time
   447  		if partyPerformance.CommitmentStartTime > 0 {
   448  			startTime = time.Unix(0, partyPerformance.CommitmentStartTime)
   449  		}
   450  
   451  		e.Engine.slaPerformance[partyPerformance.Party] = &slaPerformance{
   452  			s:                         time.Duration(partyPerformance.ElapsedTimeMeetingSlaDuringEpoch),
   453  			start:                     startTime,
   454  			previousPenalties:         previousPenalties,
   455  			lastEpochTimeBookFraction: partyPerformance.LastEpochFractionOfTimeOnBook,
   456  			lastEpochBondPenalty:      partyPerformance.LastEpochBondPenalty,
   457  			lastEpochFeePenalty:       partyPerformance.LastEpochFeePenalty,
   458  			requiredLiquidity:         partyPerformance.RequiredLiquidity,
   459  			notionalVolumeBuys:        partyPerformance.NotionalVolumeBuys,
   460  			notionalVolumeSells:       partyPerformance.NotionalVolumeSells,
   461  		}
   462  	}
   463  
   464  	e.serialisedPerformances, err = proto.Marshal(p.IntoProto())
   465  	return err
   466  }
   467  
   468  func (e *snapshotV2) loadSupplied(ls *snapshotpb.LiquidityV2Supplied, p *types.Payload) error {
   469  	// Dirty hack so we can reuse the supplied engine from the liquidity engine v1,
   470  	// without snapshot payload namespace issue.
   471  	err := e.suppliedEngine.Reload(&snapshotpb.LiquiditySupplied{
   472  		MarketId:         ls.MarketId,
   473  		ConsensusReached: ls.ConsensusReached,
   474  		BidCache:         ls.BidCache,
   475  		AskCache:         ls.AskCache,
   476  	})
   477  	if err != nil {
   478  		return err
   479  	}
   480  	e.serialisedSupplied, err = proto.Marshal(p.IntoProto())
   481  	return err
   482  }
   483  
   484  func (e *snapshotV2) loadScores(ls *snapshotpb.LiquidityV2Scores, p *types.Payload) error {
   485  	var err error
   486  
   487  	e.nAvg = int64(ls.RunningAverageCounter)
   488  	if ls.LastFeeDistributionTime == 0 {
   489  		e.lastFeeDistribution = time.Time{}
   490  	} else {
   491  		e.lastFeeDistribution = time.Unix(0, ls.LastFeeDistributionTime)
   492  	}
   493  
   494  	if ls.FeeCalculationTimeStep != 0 {
   495  		e.feeCalculationTimeStep = time.Duration(ls.FeeCalculationTimeStep)
   496  	} else {
   497  		e.feeCalculationTimeStep = defaultFeeCalculationTimeStep
   498  	}
   499  
   500  	scores := make(map[string]num.Decimal, len(ls.Scores))
   501  	for _, p := range ls.Scores {
   502  		score, err := num.DecimalFromString(p.Score)
   503  		if err != nil {
   504  			return err
   505  		}
   506  		scores[p.PartyId] = score
   507  	}
   508  
   509  	e.avgScores = scores
   510  
   511  	e.serialisedScores, err = proto.Marshal(p.IntoProto())
   512  	return err
   513  }
   514  
   515  func (e *snapshotV2) loadParameters(ls *snapshotpb.LiquidityV2Parameters, p *types.Payload) error {
   516  	var err error
   517  
   518  	// market SLA parameters
   519  	e.slaParams = types.LiquiditySLAParamsFromProto(ls.MarketSlaParameters)
   520  
   521  	one := num.DecimalOne()
   522  	e.openPlusPriceRange = one.Add(e.slaParams.PriceRange)
   523  	e.openMinusPriceRange = one.Sub(e.slaParams.PriceRange)
   524  
   525  	// now network SLA parameters
   526  	bondMax, _ := num.DecimalFromString(ls.BondPenaltyMax)
   527  	bondSlope, _ := num.DecimalFromString(ls.BondPenaltySlope)
   528  	stakeToVolume, _ := num.DecimalFromString(ls.StakeToVolume)
   529  
   530  	e.nonPerformanceBondPenaltyMax = bondMax
   531  	e.nonPerformanceBondPenaltySlope = bondSlope
   532  	e.stakeToCcyVolume = stakeToVolume
   533  
   534  	e.serialisedParemeters, err = proto.Marshal(p.IntoProto())
   535  	return err
   536  }
   537  
   538  func (e *snapshotV2) loadFeeStats(ls *snapshotpb.LiquidityV2PaidFeesStats, _ *types.Payload) {
   539  	e.allocatedFeesStats = types.NewPaidLiquidityFeesStatsFromProto(ls.Stats)
   540  }
   541  
   542  func (e *snapshotV2) buildHashKeys(market string) {
   543  	e.provisionsKey = (&types.PayloadLiquidityV2Provisions{
   544  		Provisions: &snapshotpb.LiquidityV2Provisions{
   545  			MarketId: market,
   546  		},
   547  	}).Key()
   548  
   549  	e.pendingProvisionsKey = (&types.PayloadLiquidityV2PendingProvisions{
   550  		PendingProvisions: &snapshotpb.LiquidityV2PendingProvisions{
   551  			MarketId: market,
   552  		},
   553  	}).Key()
   554  
   555  	e.performancesKey = (&types.PayloadLiquidityV2Performances{
   556  		Performances: &snapshotpb.LiquidityV2Performances{
   557  			MarketId: market,
   558  		},
   559  	}).Key()
   560  
   561  	e.suppliedKey = (&types.PayloadLiquidityV2Supplied{
   562  		Supplied: &snapshotpb.LiquidityV2Supplied{
   563  			MarketId: market,
   564  		},
   565  	}).Key()
   566  
   567  	e.scoresKey = (&types.PayloadLiquidityV2Scores{
   568  		Scores: &snapshotpb.LiquidityV2Scores{
   569  			MarketId: market,
   570  		},
   571  	}).Key()
   572  
   573  	e.paramsKey = (&types.PayloadLiquidityV2Parameters{
   574  		Parameters: &snapshotpb.LiquidityV2Parameters{
   575  			MarketId: market,
   576  		},
   577  	}).Key()
   578  
   579  	e.feeStatsKey = (&types.PayloadPaidLiquidityV2FeeStats{
   580  		Stats: &snapshotpb.LiquidityV2PaidFeesStats{
   581  			MarketId: market,
   582  		},
   583  	}).Key()
   584  
   585  	e.hashKeys = append([]string{},
   586  		e.provisionsKey,
   587  		e.pendingProvisionsKey,
   588  		e.performancesKey,
   589  		e.suppliedKey,
   590  		e.scoresKey,
   591  		e.paramsKey,
   592  		e.feeStatsKey,
   593  	)
   594  }
   595  
   596  func defaultLiquiditySLAParams() *types.LiquiditySLAParams {
   597  	return &types.LiquiditySLAParams{
   598  		PriceRange:                  num.DecimalFromFloat(0.05),
   599  		CommitmentMinTimeFraction:   num.DecimalFromFloat(0.95),
   600  		SlaCompetitionFactor:        num.DecimalFromFloat(0.9),
   601  		PerformanceHysteresisEpochs: 1,
   602  	}
   603  }
   604  
   605  func NewSnapshotEngine(
   606  	config Config,
   607  	log *logging.Logger,
   608  	timeService TimeService,
   609  	broker Broker,
   610  	riskModel RiskModel,
   611  	priceMonitor PriceMonitor,
   612  	orderBook OrderBook,
   613  	auctionState AuctionState,
   614  	asset string,
   615  	marketID string,
   616  	stateVarEngine StateVarEngine,
   617  	positionFactor num.Decimal,
   618  	slaParams *types.LiquiditySLAParams,
   619  ) *SnapshotEngine {
   620  	if slaParams == nil {
   621  		slaParams = defaultLiquiditySLAParams()
   622  	}
   623  
   624  	e := NewEngine(
   625  		config,
   626  		log,
   627  		timeService,
   628  		broker,
   629  		riskModel,
   630  		priceMonitor,
   631  		orderBook,
   632  		auctionState,
   633  		asset,
   634  		marketID,
   635  		stateVarEngine,
   636  		positionFactor,
   637  		slaParams,
   638  	)
   639  
   640  	se := &SnapshotEngine{
   641  		Engine: e,
   642  		snapshotV2: &snapshotV2{
   643  			Engine:  e,
   644  			pl:      types.Payload{},
   645  			market:  marketID,
   646  			stopped: false,
   647  		},
   648  		snapshotV1: &snapshotV1{
   649  			Engine: e,
   650  			market: marketID,
   651  		},
   652  	}
   653  
   654  	se.buildHashKeys(marketID)
   655  
   656  	return se
   657  }