code.vegaprotocol.io/vega@v0.79.0/core/liquidity/supplied/engine_test.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_test 17 18 import ( 19 "testing" 20 21 "code.vegaprotocol.io/vega/core/integration/stubs" 22 "code.vegaprotocol.io/vega/core/liquidity/supplied" 23 "code.vegaprotocol.io/vega/core/liquidity/supplied/mocks" 24 "code.vegaprotocol.io/vega/core/types" 25 "code.vegaprotocol.io/vega/core/types/statevar" 26 "code.vegaprotocol.io/vega/libs/num" 27 "code.vegaprotocol.io/vega/logging" 28 29 "github.com/golang/mock/gomock" 30 "github.com/stretchr/testify/require" 31 ) 32 33 var ( 34 MarkPrice = num.NewUint(103) 35 MarkPriceD = MarkPrice.ToDecimal() 36 DefaultInRangeProbabilityOfTrading = num.DecimalFromFloat(.5) 37 Horizon = num.DecimalFromFloat(0.001) 38 TickSize = num.NewUint(1) 39 ) 40 41 func TestLiquidityScore(t *testing.T) { 42 minLpPrice, maxLpPrice := num.UintOne(), num.MaxUint() 43 ctrl := gomock.NewController(t) 44 defer ctrl.Finish() 45 riskModel := mocks.NewMockRiskModel(ctrl) 46 priceMonitor := mocks.NewMockPriceMonitor(ctrl) 47 riskModel.EXPECT().GetProjectionHorizon().Return(Horizon).Times(1) 48 49 minPMPrice := num.NewWrappedDecimal(num.NewUint(89), num.DecimalFromInt64(89)) 50 maxPMPrice := num.NewWrappedDecimal(num.NewUint(111), num.DecimalFromInt64(111)) 51 52 // No orders 53 priceMonitor.EXPECT().GetValidPriceRange().Return(minPMPrice, maxPMPrice).AnyTimes() 54 statevarEngine := stubs.NewStateVar() 55 engine := supplied.NewEngine(riskModel, priceMonitor, "asset1", "market1", statevarEngine, logging.NewTestLogger(), num.DecimalFromInt64(1)) 56 require.NotNil(t, engine) 57 58 f := func() (num.Decimal, num.Decimal, error) { return MarkPriceD, MarkPriceD, nil } 59 engine.SetGetStaticPricesFunc(f) 60 61 liquidity := engine.CalculateLiquidityScore([]*types.Order{}, MarkPriceD, MarkPriceD, minLpPrice, maxLpPrice) 62 require.True(t, liquidity.IsZero()) 63 64 // 1 buy, no sells 65 buyOrder1 := &types.Order{ 66 Price: num.NewUint(102), 67 Size: 30, 68 Remaining: 25, 69 Side: types.SideBuy, 70 } 71 72 buyOrder1Prob := num.DecimalFromFloat(0.256) 73 sellOrder1Prob := num.DecimalFromFloat(0.33) 74 sellOrder2Prob := num.DecimalFromFloat(0.17) 75 76 sellOrder1 := &types.Order{ 77 Price: num.NewUint(105), 78 Size: 15, 79 Remaining: 11, 80 Side: types.SideSell, 81 } 82 sellOrder2 := &types.Order{ 83 Price: num.NewUint(104), 84 Size: 60, 85 Remaining: 60, 86 Side: types.SideSell, 87 } 88 89 riskModel.EXPECT().ProbabilityOfTrading(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(func(best, order, min num.Decimal, max num.Decimal, yFrac num.Decimal, isBid bool, applyMinMax bool) num.Decimal { 90 if best.Equal(MarkPriceD) && order.Sub(buyOrder1.Price.ToDecimal()).Abs().LessThanOrEqual(num.DecimalFromFloat(0.1)) && isBid { 91 return buyOrder1Prob 92 } 93 if best.Equal(MarkPriceD) && order.Sub(sellOrder1.Price.ToDecimal()).Abs().LessThanOrEqual(num.DecimalFromFloat(0.1)) && !isBid { 94 return sellOrder1Prob 95 } 96 if best.Equal(MarkPriceD) && order.Sub(sellOrder2.Price.ToDecimal()).Abs().LessThanOrEqual(num.DecimalFromFloat(0.1)) && !isBid { 97 return sellOrder2Prob 98 } 99 if order.LessThanOrEqual(num.DecimalZero()) { 100 return num.DecimalZero() 101 } 102 if order.GreaterThanOrEqual(num.DecimalFromInt64(2).Mul(best)) { 103 return num.DecimalZero() 104 } 105 return num.DecimalFromFloat(0.5) 106 }) 107 108 statevarEngine.NewEvent("asset1", "market1", statevar.EventTypeAuctionEnded) 109 liquidity2 := engine.CalculateLiquidityScore([]*types.Order{}, MarkPriceD, MarkPriceD, minLpPrice, maxLpPrice) 110 require.True(t, liquidity2.IsZero()) 111 112 buyOrder1Size := num.DecimalFromInt64(int64(buyOrder1.Remaining)) 113 buyLiquidityScore := buyOrder1Prob.Mul(DefaultInRangeProbabilityOfTrading).Mul(buyOrder1Size) 114 buySideTotalSize := num.DecimalZero().Add(buyOrder1Size) 115 116 buyLiquidityScore = buyLiquidityScore.Div(buySideTotalSize) 117 118 sellOrder1Size := num.DecimalFromInt64(int64(sellOrder1.Remaining)) 119 sellLiquidityScore := sellOrder1Prob.Mul(DefaultInRangeProbabilityOfTrading).Mul(sellOrder1Size) 120 sellSideTotalSize := num.DecimalZero().Add(sellOrder1Size) 121 122 sellOrder2Size := num.DecimalFromInt64(int64(sellOrder2.Remaining)) 123 sellLiquidityScore = sellLiquidityScore.Add(sellOrder2Prob.Mul(DefaultInRangeProbabilityOfTrading).Mul(sellOrder2Size)) 124 sellSideTotalSize = sellSideTotalSize.Add(sellOrder2Size) 125 126 sellLiquidityScore = sellLiquidityScore.Div(sellSideTotalSize) 127 128 expectedScore := min(buyLiquidityScore, sellLiquidityScore) 129 liquidity3 := engine.CalculateLiquidityScore([]*types.Order{buyOrder1, sellOrder1, sellOrder2}, MarkPriceD, MarkPriceD, minLpPrice, maxLpPrice) 130 require.True(t, expectedScore.Equal(liquidity3)) 131 132 // 2 buys, 2 sells 133 buyOrder2 := &types.Order{ 134 Price: num.NewUint(102), 135 Size: 600, 136 Remaining: 599, 137 Side: types.SideBuy, 138 } 139 buyOrder2Prob := num.DecimalFromFloat(0.256) 140 141 // buyLiquidity += buyOrder2.Price.Float64() * float64(buyOrder2.Remaining) * buyOrder2Prob 142 buyOrder2Size := num.DecimalFromInt64(int64(buyOrder2.Remaining)) 143 buyLiquidityScore = buyOrder1Prob.Mul(DefaultInRangeProbabilityOfTrading).Mul(buyOrder1Size).Add(buyOrder2Prob.Mul(DefaultInRangeProbabilityOfTrading).Mul(buyOrder2Size)) 144 buySideTotalSize = num.DecimalZero().Add(buyOrder1Size).Add(buyOrder2Size) 145 146 buyLiquidityScore = buyLiquidityScore.Div(buySideTotalSize) 147 148 expectedScore = min(buyLiquidityScore, sellLiquidityScore) 149 liquidity4 := engine.CalculateLiquidityScore([]*types.Order{buyOrder1, sellOrder1, sellOrder2, buyOrder2}, MarkPriceD, MarkPriceD, minLpPrice, maxLpPrice) 150 require.True(t, expectedScore.Equal(liquidity4)) 151 152 // Orders outside PM range (but within LP range) 153 154 // add orders outwith the PM bounds 155 buyOrder3 := &types.Order{ 156 Price: num.UintZero().Sub(minPMPrice.Representation(), num.UintOne()), 157 Size: 123, 158 Remaining: 45, 159 Side: types.SideBuy, 160 } 161 sellOrder3 := &types.Order{ 162 Price: num.UintZero().Add(maxPMPrice.Representation(), num.UintOne()), 163 Size: 345, 164 Remaining: 67, 165 Side: types.SideSell, 166 } 167 168 // liquidity should drop as the volume-weighted PoT of trading within the LP range drops (some orders included in the score now have PoT==0) 169 liquidity5 := engine.CalculateLiquidityScore([]*types.Order{buyOrder1, sellOrder1, sellOrder2, buyOrder2, sellOrder3, buyOrder3}, MarkPriceD, MarkPriceD, minLpPrice, maxLpPrice) 170 require.True(t, liquidity5.LessThan(liquidity4)) 171 172 // Orders outside LP range (but within PM range) 173 174 // set bounds at prices of orders furtherst away form the mid 175 minLpPrice = buyOrder2.Price 176 maxLpPrice = sellOrder1.Price 177 178 // add orders outwith the LP bounds 179 buyOrder3 = &types.Order{ 180 Price: num.UintZero().Sub(minLpPrice, num.UintOne()), 181 Size: 123, 182 Remaining: 45, 183 Side: types.SideBuy, 184 } 185 sellOrder3 = &types.Order{ 186 Price: num.UintZero().Add(maxLpPrice, num.UintOne()), 187 Size: 345, 188 Remaining: 67, 189 Side: types.SideSell, 190 } 191 192 // liquidity shouldn't change 193 liquidity6 := engine.CalculateLiquidityScore([]*types.Order{buyOrder1, sellOrder1, sellOrder2, buyOrder2, sellOrder3, buyOrder3}, MarkPriceD, MarkPriceD, minLpPrice, maxLpPrice) 194 require.Equal(t, liquidity4, liquidity6) 195 } 196 197 func min(d1, d2 num.Decimal) num.Decimal { 198 if d1.LessThan(d2) { 199 return d1 200 } 201 return d2 202 }