code.vegaprotocol.io/vega@v0.79.0/core/execution/common/mark_price_utils_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 common_test 17 18 import ( 19 "testing" 20 "time" 21 22 "code.vegaprotocol.io/vega/core/execution/common" 23 "code.vegaprotocol.io/vega/core/matching" 24 "code.vegaprotocol.io/vega/core/types" 25 vgcrypto "code.vegaprotocol.io/vega/libs/crypto" 26 "code.vegaprotocol.io/vega/libs/num" 27 "code.vegaprotocol.io/vega/logging" 28 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestMarkPriceByWeights(t *testing.T) { 33 // no mark prices 34 require.Nil(t, common.CompositePriceByWeight(nil, []num.Decimal{num.NewDecimalFromFloat(0.2), num.NewDecimalFromFloat(0.5)}, []int64{3, 2}, []time.Duration{0, 0}, 10)) 35 36 // no non-stale mark prices 37 // time now is 10, both timestamps of prices are 2,3 with delta = 0 for both 38 require.Nil(t, common.CompositePriceByWeight([]*num.Uint{num.NewUint(100), num.NewUint(80)}, []num.Decimal{num.NewDecimalFromFloat(0.2), num.NewDecimalFromFloat(0.5)}, []int64{3, 2}, []time.Duration{0, 0}, 10)) 39 40 // only the first price is non stale 41 // 10-8<=2 42 require.Equal(t, num.NewUint(100), common.CompositePriceByWeight([]*num.Uint{num.NewUint(100), num.NewUint(80)}, []num.Decimal{num.NewDecimalFromFloat(0.2), num.NewDecimalFromFloat(0.5)}, []int64{8, 2}, []time.Duration{2, 0}, 10)) 43 44 // only the second price is non stale 45 // 10-7<=3 46 require.Equal(t, num.NewUint(80), common.CompositePriceByWeight([]*num.Uint{num.NewUint(100), num.NewUint(80)}, []num.Decimal{num.NewDecimalFromFloat(0.2), num.NewDecimalFromFloat(0.5)}, []int64{8, 7}, []time.Duration{1, 3}, 10)) 47 48 // both prices are eligible use weights: 49 // 0.4*100+0.6*80 = 88 50 require.Equal(t, num.NewUint(88), common.CompositePriceByWeight([]*num.Uint{num.NewUint(100), num.NewUint(80)}, []num.Decimal{num.NewDecimalFromFloat(0.2), num.NewDecimalFromFloat(0.3)}, []int64{8, 7}, []time.Duration{2, 3}, 10)) 51 } 52 53 func TestMarkPriceByMedian(t *testing.T) { 54 // no prices 55 require.Nil(t, common.CompositePriceByMedian(nil, []int64{3, 2}, []time.Duration{0, 0}, 10)) 56 57 // no non-stale mark prices 58 // time now is 10, both timestamps of prices are 2,3 with delta = 0 for both 59 require.Nil(t, common.CompositePriceByMedian([]*num.Uint{num.NewUint(100), num.NewUint(80)}, []int64{3, 2}, []time.Duration{0, 0}, 10)) 60 61 // only the first price is non stale 62 // 10-8<=2 63 require.Equal(t, num.NewUint(100), common.CompositePriceByMedian([]*num.Uint{num.NewUint(100), num.NewUint(80)}, []int64{8, 2}, []time.Duration{2, 0}, 10)) 64 65 // only the second price is non stale 66 // 10-7<=3 67 require.Equal(t, num.NewUint(80), common.CompositePriceByMedian([]*num.Uint{num.NewUint(100), num.NewUint(80)}, []int64{8, 7}, []time.Duration{1, 3}, 10)) 68 69 // both prices are non stale, median is calculated (average in this even case) 70 require.Equal(t, num.NewUint(90), common.CompositePriceByMedian([]*num.Uint{num.NewUint(100), num.NewUint(80)}, []int64{8, 7}, []time.Duration{2, 3}, 10)) 71 72 // all prices are non stale, median is calculated 73 require.Equal(t, num.NewUint(99), common.CompositePriceByMedian([]*num.Uint{num.NewUint(99), num.NewUint(100), num.NewUint(80)}, []int64{8, 8, 7}, []time.Duration{2, 2, 3}, 10)) 74 } 75 76 func TestMedianMarkPrice(t *testing.T) { 77 require.Nil(t, common.MedianPrice(nil)) 78 require.Equal(t, "100", common.MedianPrice([]*num.Uint{num.NewUint(110), num.NewUint(99), num.NewUint(100)}).String()) 79 require.Equal(t, "100", common.MedianPrice([]*num.Uint{num.NewUint(110), num.NewUint(101), num.NewUint(99), num.NewUint(100)}).String()) 80 } 81 82 func TestMarkPriceFromTrades(t *testing.T) { 83 alpha := num.DecimalZero() 84 decayPower := num.DecimalZero() 85 lambda := num.NewDecimalFromFloat(100) 86 87 trade1 := &types.Trade{ 88 Price: num.NewUint(129), 89 Size: 10, 90 Timestamp: 120, 91 } 92 93 trade2 := &types.Trade{ 94 Price: num.NewUint(124), 95 Size: 40, 96 Timestamp: 150, 97 } 98 trade3 := &types.Trade{ 99 Price: num.NewUint(133), 100 Size: 50, 101 Timestamp: 200, 102 } 103 104 // given alpha is 0, the time_weight is 1 105 // the total size is 60, so trade weights are: 106 // 1/10, 4/10, 5/10 107 // so the markPrice = 0.1*129 + 0.4*124 + 0.5 * 133 = 129 108 mp := common.PriceFromTrades([]*types.Trade{trade1, trade2, trade3}, alpha, lambda, decayPower, 200) 109 require.Equal(t, "129", mp.String()) 110 111 // now lets repeat with non zero alpha 112 alpha = num.DecimalFromFloat(0.2) 113 decayPower = num.DecimalOne() 114 115 // given alpha is 0, the time_weight is 1 116 // the total size is 60, so trade weights are: 117 // 1/10 * (1 - 0.2 * (200-120)/100) = 0.084 118 // 4/10 * (1 - 0.2 * (200-150)/100) = 0.36 119 // 5/10 * (1 - 0.2 * (200-200)/100) = 0.5 120 // total weight = 0.944 121 // mp = (0.084 * 129 + 0.36 * 124 + 0.5 * 133)/0.944 = 172.1276595745 122 mp = common.PriceFromTrades([]*types.Trade{trade1, trade2, trade3}, alpha, lambda, decayPower, 200) 123 require.Equal(t, "129", mp.String()) 124 } 125 126 func TestPBookAtTimeT(t *testing.T) { 127 book := matching.NewCachedOrderBook(logging.NewTestLogger(), matching.NewDefaultConfig(), "market1", false, func(int64) {}) 128 C := num.NewUint(1000) 129 initialScaling := num.DecimalFromFloat(0.2) 130 slippage := num.DecimalFromFloat(0.1) 131 shortRisk := num.DecimalFromFloat(0.3) 132 longRisk := num.DecimalFromFloat(0.4) 133 134 // empty book 135 require.Nil(t, common.PriceFromBookAtTime(C, initialScaling, slippage, shortRisk, longRisk, book)) 136 137 // no bids 138 _, err := book.SubmitOrder(newOrder(num.NewUint(120), 10, types.SideSell)) 139 require.NoError(t, err) 140 require.Nil(t, common.PriceFromBookAtTime(C, initialScaling, slippage, shortRisk, longRisk, book)) 141 book.CancelAllOrders("party1") 142 143 // no asks 144 _, err = book.SubmitOrder(newOrder(num.NewUint(125), 10, types.SideBuy)) 145 require.NoError(t, err) 146 require.Nil(t, common.PriceFromBookAtTime(C, initialScaling, slippage, shortRisk, longRisk, book)) 147 148 // orders on both sides 149 _, err = book.SubmitOrder(newOrder(num.NewUint(200), 10, types.SideSell)) 150 require.NoError(t, err) 151 152 // N_buy = 1000 / ((0.2) * (0.1+0.3)) = 12500 153 // N_sell = 1000 / ((0.2) * (0.1+0.4)) = 10000 154 // V_buy = N_buy/best_bid = 12500/125 = 100 155 // V_sell = N_sell/best_ask = 10000/200 = 50 156 // insufficient volume in the book for both sides 157 158 require.Nil(t, common.PriceFromBookAtTime(C, initialScaling, slippage, shortRisk, longRisk, book)) 159 160 // add orders on both sides 161 _, err = book.SubmitOrder(newOrder(num.NewUint(200), 40, types.SideSell)) 162 require.NoError(t, err) 163 _, err = book.SubmitOrder(newOrder(num.NewUint(125), 90, types.SideBuy)) 164 require.NoError(t, err) 165 166 // (125+200)/2 = 162 167 require.Equal(t, "162", common.PriceFromBookAtTime(C, initialScaling, slippage, shortRisk, longRisk, book).String()) 168 } 169 170 func TestCalculateTimeWeightedAverageBookMarkPrice(t *testing.T) { 171 timeToPrice := map[int64]*num.Uint{0: num.NewUint(100), 30: num.NewUint(120), 45: num.NewUint(150)} 172 173 // 100 * 30/60 + 120 * 15/60 + 150 * 15/60 = 117.5 => 117 174 require.Equal(t, "117", common.CalculateTimeWeightedAverageBookPrice(timeToPrice, 60, 60).String()) 175 176 // 120 * 15/30 + 150 * 15/30 = 97.5 => 135 177 require.Equal(t, "135", common.CalculateTimeWeightedAverageBookPrice(timeToPrice, 60, 30).String()) 178 179 // 100 * 30/120 + 120 * 15/120 + 150 * 75/120 = 133.75 => 133 180 require.Equal(t, "133", common.CalculateTimeWeightedAverageBookPrice(timeToPrice, 120, 120).String()) 181 182 // only the price from 45 is considered as the price from 30 is starting before the mark price period 183 require.Equal(t, "150", common.CalculateTimeWeightedAverageBookPrice(timeToPrice, 120, 80).String()) 184 } 185 186 func newOrder(price *num.Uint, size uint64, side types.Side) *types.Order { 187 return &types.Order{ 188 ID: vgcrypto.RandomHash(), 189 Status: types.OrderStatusActive, 190 Type: types.OrderTypeLimit, 191 MarketID: "market1", 192 Party: "party1", 193 Side: side, 194 Price: price, 195 OriginalPrice: price, 196 Size: size, 197 Remaining: size, 198 TimeInForce: types.OrderTimeInForceGTC, 199 } 200 }