code.vegaprotocol.io/vega@v0.79.0/core/liquidity/supplied/engine.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 supplied 17 18 import ( 19 "context" 20 "errors" 21 22 "code.vegaprotocol.io/vega/core/types" 23 "code.vegaprotocol.io/vega/core/types/statevar" 24 "code.vegaprotocol.io/vega/libs/num" 25 "code.vegaprotocol.io/vega/logging" 26 ) 27 28 // ErrNoValidOrders informs that there weren't any valid orders to cover the liquidity obligation with. 29 // This could happen when for a given side (buy or sell) limit orders don't supply enough liquidity and there aren't any 30 // valid pegged orders (all the prives are invalid) to cover it with. 31 var ( 32 ErrNoValidOrders = errors.New("no valid orders to cover the liquidity obligation with") 33 ) 34 35 // RiskModel allows calculation of min/max price range and a probability of trading. 36 // 37 //go:generate go run github.com/golang/mock/mockgen -destination mocks/risk_model_mock.go -package mocks code.vegaprotocol.io/vega/core/liquidity/supplied RiskModel 38 type RiskModel interface { 39 ProbabilityOfTrading(currentPrice, orderPrice, minPrice, maxPrice num.Decimal, yearFraction num.Decimal, isBid, applyMinMax bool) num.Decimal 40 GetProjectionHorizon() num.Decimal 41 } 42 43 // PriceMonitor provides the range of valid prices, that is prices that wouldn't trade the current trading mode 44 // 45 //go:generate go run github.com/golang/mock/mockgen -destination mocks/price_monitor_mock.go -package mocks code.vegaprotocol.io/vega/core/liquidity/supplied PriceMonitor 46 type PriceMonitor interface { 47 GetValidPriceRange() (num.WrappedDecimal, num.WrappedDecimal) 48 } 49 50 type StateVarEngine interface { 51 RegisterStateVariable(asset, market, name string, converter statevar.Converter, startCalculation func(string, statevar.FinaliseCalculation), trigger []statevar.EventType, result func(context.Context, statevar.StateVariableResult) error) error 52 } 53 54 // Engine provides functionality related to supplied liquidity. 55 type Engine struct { 56 rm RiskModel 57 pm PriceMonitor 58 marketID string 59 horizon num.Decimal // projection horizon used in probability calculations 60 probabilityOfTradingTauScaling num.Decimal 61 minProbabilityOfTrading num.Decimal 62 pot *probabilityOfTrading 63 potInitialised bool 64 65 getBestStaticPrices func() (num.Decimal, num.Decimal, error) 66 log *logging.Logger 67 positionFactor num.Decimal 68 } 69 70 // NewEngine returns a reference to a new supplied liquidity calculation engine. 71 func NewEngine(riskModel RiskModel, priceMonitor PriceMonitor, asset, marketID string, stateVarEngine StateVarEngine, log *logging.Logger, positionFactor num.Decimal) *Engine { 72 e := &Engine{ 73 rm: riskModel, 74 pm: priceMonitor, 75 marketID: marketID, 76 horizon: riskModel.GetProjectionHorizon(), 77 probabilityOfTradingTauScaling: num.DecimalFromInt64(1), // this is the same as the default in the netparams 78 minProbabilityOfTrading: defaultMinimumProbabilityOfTrading, 79 pot: &probabilityOfTrading{}, 80 potInitialised: false, 81 log: log, 82 positionFactor: positionFactor, 83 } 84 85 stateVarEngine.RegisterStateVariable(asset, marketID, "probability_of_trading", probabilityOfTradingConverter{}, e.startCalcProbOfTrading, []statevar.EventType{statevar.EventTypeTimeTrigger, statevar.EventTypeAuctionEnded, statevar.EventTypeOpeningAuctionFirstUncrossingPrice}, e.updateProbabilities) 86 return e 87 } 88 89 func (e *Engine) UpdateMarketConfig(riskModel RiskModel, monitor PriceMonitor) { 90 e.rm = riskModel 91 e.pm = monitor 92 e.horizon = riskModel.GetProjectionHorizon() 93 e.potInitialised = false 94 } 95 96 func (e *Engine) SetGetStaticPricesFunc(f func() (num.Decimal, num.Decimal, error)) { 97 e.getBestStaticPrices = f 98 } 99 100 func (e *Engine) OnMinProbabilityOfTradingLPOrdersUpdate(v num.Decimal) { 101 e.minProbabilityOfTrading = v 102 } 103 104 func (e *Engine) OnProbabilityOfTradingTauScalingUpdate(v num.Decimal) { 105 e.probabilityOfTradingTauScaling = v 106 } 107 108 // CalculateLiquidityScore returns the current liquidity scores (volume-weighted probability of trading). 109 func (e *Engine) CalculateLiquidityScore( 110 orders []*types.Order, 111 bestBid, bestAsk num.Decimal, 112 minLpPrice, maxLpPrice *num.Uint, 113 ) num.Decimal { 114 minPMPrice, maxPMPrice := e.pm.GetValidPriceRange() 115 116 bLiq := num.DecimalZero() 117 sLiq := num.DecimalZero() 118 bSize := num.DecimalZero() 119 sSize := num.DecimalZero() 120 for _, o := range orders { 121 if o.Price.LT(minLpPrice) || o.Price.GT(maxLpPrice) { 122 continue 123 } 124 prob := num.DecimalZero() 125 // if order is outside of price monitoring bounds then probability is set to 0. 126 if o.Price.GTE(minPMPrice.Representation()) && o.Price.LTE(maxPMPrice.Representation()) { 127 prob = getProbabilityOfTrading(bestBid, bestAsk, minPMPrice.Original(), maxPMPrice.Original(), e.pot, o.Price.ToDecimal(), o.Side == types.SideBuy, e.minProbabilityOfTrading, OffsetOneDecimal) 128 if prob.LessThanOrEqual(e.minProbabilityOfTrading) { 129 prob = num.DecimalZero() 130 } 131 } 132 s := num.DecimalFromUint(num.NewUint(o.Remaining)) 133 l := prob.Mul(s) 134 if o.Side == types.SideBuy { 135 bLiq = bLiq.Add(l) 136 bSize = bSize.Add(s) 137 } 138 if o.Side == types.SideSell { 139 sLiq = sLiq.Add(l) 140 sSize = sSize.Add(s) 141 } 142 } 143 // descale by total volume per side 144 if !bSize.IsZero() { 145 bLiq = bLiq.Div(bSize) 146 } 147 if !sSize.IsZero() { 148 sLiq = sLiq.Div(sSize) 149 } 150 151 // return the minimum of the two 152 if bLiq.LessThanOrEqual(sLiq) { 153 return bLiq 154 } 155 return sLiq 156 }