code.vegaprotocol.io/vega@v0.79.0/core/plugins/positions_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 plugins_test 17 18 // No race condition checks on these tests, the channels are buffered to avoid actual issues 19 // we are aware that the tests themselves can be written in an unsafe way, but that's the tests 20 // not the code itsel. The behaviour of the tests is 100% reliable. 21 import ( 22 "context" 23 "testing" 24 25 "code.vegaprotocol.io/vega/core/events" 26 "code.vegaprotocol.io/vega/core/plugins" 27 "code.vegaprotocol.io/vega/core/types" 28 "code.vegaprotocol.io/vega/libs/num" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/assert" 32 ) 33 34 type tradeStub struct { 35 size int64 36 price *num.Uint 37 } 38 39 type posPluginTst struct { 40 *plugins.Positions 41 ctrl *gomock.Controller 42 ctx context.Context 43 cfunc context.CancelFunc 44 } 45 46 func TestMultipleTradesOfSameSize(t *testing.T) { 47 position := getPosPlugin(t) 48 defer position.Finish() 49 market := "market-id" 50 ps := events.NewSettlePositionEvent(position.ctx, "party1", market, num.NewUint(1000), []events.TradeSettlement{ 51 tradeStub{ 52 size: -1, 53 price: num.NewUint(1000), 54 }, 55 tradeStub{ 56 size: -1, 57 price: num.NewUint(1000), 58 }, 59 }, 1, num.DecimalFromFloat(1)) 60 position.Push(ps) 61 pp, err := position.GetPositionsByMarket(market) 62 assert.NoError(t, err) 63 assert.NotZero(t, len(pp)) 64 // average entry price should be 1k 65 assert.Equal(t, ps.Price(), pp[0].AverageEntryPrice) 66 } 67 68 func TestMultipleTradesAndLossSocializationPartyNoOpenVolume(t *testing.T) { 69 position := getPosPlugin(t) 70 defer position.Finish() 71 market := "market-id" 72 ps := events.NewSettlePositionEvent(position.ctx, "party1", market, num.NewUint(1000), []events.TradeSettlement{ 73 tradeStub{ 74 size: 2, 75 price: num.NewUint(1000), 76 }, 77 tradeStub{ 78 size: -2, 79 price: num.NewUint(1500), 80 }, 81 }, 1, num.DecimalFromFloat(1)) 82 position.Push(ps) 83 pp, err := position.GetPositionsByMarket(market) 84 assert.NoError(t, err) 85 assert.NotZero(t, len(pp)) 86 // average entry price should be 1k 87 // initially calculation say the RealisedPNL should be 1000 88 assert.Equal(t, "1000", pp[0].RealisedPnl.String()) 89 90 // then we process the event for LossSocialization 91 lsevt := events.NewLossSocializationEvent(position.ctx, "party1", market, num.NewUint(300), true, 1, types.LossTypeUnspecified) 92 position.Push(lsevt) 93 pp, err = position.GetPositionsByMarket(market) 94 assert.NoError(t, err) 95 assert.NotZero(t, len(pp)) 96 // with the changes, the RealisedPNL should be 700 97 assert.Equal(t, "700", pp[0].RealisedPnl.String()) 98 assert.Equal(t, "0", pp[0].UnrealisedPnl.String()) 99 } 100 101 func TestDistressedPartyUpdate(t *testing.T) { 102 position := getPosPlugin(t) 103 defer position.Finish() 104 market := "market-id" 105 ps := events.NewSettlePositionEvent(position.ctx, "party1", market, num.NewUint(1000), []events.TradeSettlement{ 106 tradeStub{ 107 size: 2, 108 price: num.NewUint(1000), 109 }, 110 tradeStub{ 111 size: 3, 112 price: num.NewUint(1200), 113 }, 114 }, 1, num.DecimalFromFloat(1)) 115 position.Push(ps) 116 pp, err := position.GetPositionsByMarket(market) 117 assert.NoError(t, err) 118 assert.NotZero(t, len(pp)) 119 // average entry price should be 1k 120 // initially calculation say the RealisedPNL should be 1000 121 assert.Equal(t, "0", pp[0].RealisedPnl.String()) 122 assert.Equal(t, "-600", pp[0].UnrealisedPnl.String()) 123 124 // then we process the event for LossSocialization 125 lsevt := events.NewLossSocializationEvent(position.ctx, "party1", market, num.NewUint(300), true, 1, types.LossTypeUnspecified) 126 position.Push(lsevt) 127 pp, err = position.GetPositionsByMarket(market) 128 assert.NoError(t, err) 129 assert.NotZero(t, len(pp)) 130 // with the changes, the RealisedPNL should be 700 131 assert.Equal(t, "-300", pp[0].RealisedPnl.String()) 132 assert.Equal(t, "-600", pp[0].UnrealisedPnl.String()) 133 // now assume this party is distressed, and we've taken all their funds 134 sde := events.NewSettleDistressed(position.ctx, "party1", market, num.UintZero(), num.NewUint(100), 1) 135 position.Push(sde) 136 pp, err = position.GetPositionsByMarket(market) 137 assert.NoError(t, err) 138 assert.NotZero(t, len(pp)) 139 assert.Equal(t, "0", pp[0].UnrealisedPnl.String()) 140 assert.Equal(t, "-1000", pp[0].RealisedPnl.String()) 141 } 142 143 func TestMultipleTradesAndLossSocializationPartyWithOpenVolume(t *testing.T) { 144 position := getPosPlugin(t) 145 defer position.Finish() 146 market := "market-id" 147 ps := events.NewSettlePositionEvent(position.ctx, "party1", market, num.NewUint(1000), []events.TradeSettlement{ 148 tradeStub{ 149 size: 2, 150 price: num.NewUint(1000), 151 }, 152 tradeStub{ 153 size: 3, 154 price: num.NewUint(1200), 155 }, 156 }, 1, num.DecimalFromFloat(1)) 157 position.Push(ps) 158 pp, err := position.GetPositionsByMarket(market) 159 assert.NoError(t, err) 160 assert.NotZero(t, len(pp)) 161 // average entry price should be 1k 162 // initially calculation say the RealisedPNL should be 1000 163 assert.Equal(t, "0", pp[0].RealisedPnl.String()) 164 assert.Equal(t, "-600", pp[0].UnrealisedPnl.String()) 165 166 // then we process the event for LossSocialization 167 lsevt := events.NewLossSocializationEvent(position.ctx, "party1", market, num.NewUint(300), true, 1, types.LossTypeUnspecified) 168 position.Push(lsevt) 169 pp, err = position.GetPositionsByMarket(market) 170 assert.NoError(t, err) 171 assert.NotZero(t, len(pp)) 172 // with the changes, the RealisedPNL should be 700 173 assert.Equal(t, "-300", pp[0].RealisedPnl.String()) 174 assert.Equal(t, "-600", pp[0].UnrealisedPnl.String()) 175 } 176 177 func getPosPlugin(t *testing.T) *posPluginTst { 178 t.Helper() 179 ctrl := gomock.NewController(t) 180 ctx, cfunc := context.WithCancel(context.Background()) 181 p := plugins.NewPositions(ctx) 182 tst := posPluginTst{ 183 Positions: p, 184 ctrl: ctrl, 185 ctx: ctx, 186 cfunc: cfunc, 187 } 188 return &tst 189 } 190 191 func (p *posPluginTst) Finish() { 192 p.cfunc() // cancel context 193 defer p.ctrl.Finish() 194 } 195 196 func (t tradeStub) Size() int64 { 197 return t.size 198 } 199 200 func (t tradeStub) Price() *num.Uint { 201 return t.price.Clone() 202 } 203 204 func (t tradeStub) MarketPrice() *num.Uint { 205 return t.price.Clone() 206 }