code.vegaprotocol.io/vega@v0.79.0/core/risk/models/lognormal.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 models 17 18 import ( 19 "errors" 20 "math" 21 22 "code.vegaprotocol.io/vega/core/types" 23 "code.vegaprotocol.io/vega/libs/num" 24 25 "code.vegaprotocol.io/quant/interfaces" 26 pd "code.vegaprotocol.io/quant/pricedistribution" 27 "code.vegaprotocol.io/quant/riskmodelbs" 28 ) 29 30 var ErrMissingLogNormalParameter = errors.New("missing log normal parameters") 31 32 // LogNormal represent a future risk model. 33 type LogNormal struct { 34 riskAversionParameter, tau num.Decimal 35 params riskmodelbs.ModelParamsBS 36 asset string 37 RiskFactorOverride *types.RiskFactorOverride 38 39 distCache interfaces.AnalyticalDistribution 40 cachePrice num.Decimal 41 cacheHorizon num.Decimal 42 } 43 44 // NewBuiltinFutures instantiate a new builtin future. 45 func NewBuiltinFutures(pf *types.LogNormalRiskModel, asset string) (*LogNormal, error) { 46 if pf.Params == nil { 47 return nil, ErrMissingLogNormalParameter 48 } 49 // the quant stuff really needs to be updated to use the same num types... 50 mu, _ := pf.Params.Mu.Float64() 51 r, _ := pf.Params.R.Float64() 52 sigma, _ := pf.Params.Sigma.Float64() 53 54 var override *types.RiskFactorOverride 55 if pf.RiskFactorOverride != nil { 56 override = &types.RiskFactorOverride{ 57 Short: pf.RiskFactorOverride.Short, 58 Long: pf.RiskFactorOverride.Long, 59 } 60 } 61 62 return &LogNormal{ 63 riskAversionParameter: pf.RiskAversionParameter, 64 tau: pf.Tau, 65 cachePrice: num.DecimalZero(), 66 params: riskmodelbs.ModelParamsBS{ 67 Mu: mu, 68 R: r, 69 Sigma: sigma, 70 }, 71 RiskFactorOverride: override, 72 asset: asset, 73 }, nil 74 } 75 76 // CalculateRiskFactors calls the risk model in order to get 77 // the new risk models. 78 func (f *LogNormal) CalculateRiskFactors() *types.RiskFactor { 79 if f.RiskFactorOverride != nil { 80 return &types.RiskFactor{ 81 Long: f.RiskFactorOverride.Long, 82 Short: f.RiskFactorOverride.Short, 83 } 84 } 85 86 rav, _ := f.riskAversionParameter.Float64() 87 tau, _ := f.tau.Float64() 88 rawrf := riskmodelbs.RiskFactorsForward(rav, tau, f.params) 89 return &types.RiskFactor{ 90 Long: num.DecimalFromFloat(rawrf.Long), 91 Short: num.DecimalFromFloat(rawrf.Short), 92 } 93 } 94 95 // PriceRange returns the minimum and maximum price as implied by the model's probability distribution with horizon given by yearFraction (e.g. 0.5 for half a year) and probability level (e.g. 0.95 for 95%). 96 func (f *LogNormal) PriceRange(currentP, yFrac, probabilityLevel num.Decimal) (num.Decimal, num.Decimal) { 97 dist := f.getDistribution(currentP, yFrac) 98 // damn you quant! 99 pl, _ := probabilityLevel.Float64() 100 min, max := pd.PriceRange(dist, pl) 101 return num.DecimalFromFloat(min), num.DecimalFromFloat(max) 102 } 103 104 // ProbabilityOfTrading of trading returns the probability of trading given current mark price, projection horizon expressed as year fraction, order price and side (isBid). 105 // Additional arguments control optional truncation of probability density outside the [minPrice,maxPrice] range. 106 func (f *LogNormal) ProbabilityOfTrading(currentP, orderP num.Decimal, minP, maxP num.Decimal, yFrac num.Decimal, isBid, applyMinMax bool) num.Decimal { 107 dist := f.getDistribution(currentP, yFrac) 108 min := math.Max(minP.InexactFloat64(), 0) 109 // still, quant uses floats 110 prob := pd.ProbabilityOfTrading(dist, orderP.InexactFloat64(), isBid, applyMinMax, min, maxP.InexactFloat64()) 111 if math.IsNaN(prob) { 112 return num.DecimalZero() 113 } 114 115 return num.DecimalFromFloat(prob) 116 } 117 118 func (f *LogNormal) getDistribution(currentP num.Decimal, yFrac num.Decimal) interfaces.AnalyticalDistribution { 119 if f.distCache == nil || !f.cachePrice.Equal(currentP) || !f.cacheHorizon.Equal(yFrac) { 120 // quant still uses floats... sad 121 yf, _ := yFrac.Float64() 122 f.distCache = f.params.GetProbabilityDistribution(currentP.InexactFloat64(), yf) 123 } 124 return f.distCache 125 } 126 127 // GetProjectionHorizon returns the projection horizon used by the model for margin calculation pruposes. 128 func (f *LogNormal) GetProjectionHorizon() num.Decimal { 129 return f.tau 130 } 131 132 func (f *LogNormal) DefaultRiskFactors() *types.RiskFactor { 133 if f.RiskFactorOverride != nil { 134 return &types.RiskFactor{ 135 Long: f.RiskFactorOverride.Long, 136 Short: f.RiskFactorOverride.Short, 137 } 138 } 139 140 return &types.RiskFactor{ 141 Short: num.DecimalFromFloat(1), 142 Long: num.DecimalFromFloat(1), 143 } 144 }