Skip to content

Commit

Permalink
Merge pull request #10161 from vegaprotocol/10158-panic-fix
Browse files Browse the repository at this point in the history
fix: Add unit test with small fix
  • Loading branch information
EVODelavega authored Nov 22, 2023
2 parents 07b6292 + 08f5824 commit efed0e3
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
- [10127](https://github.com/vegaprotocol/vega/issues/10127) - Untangle `ApplyReferralCode` and `JoinTeam` command verification.
- [10153](https://github.com/vegaprotocol/vega/issues/10153) - Add metrics and reduce amount of request sent to the Ethereum `RPC`.
- [10147](https://github.com/vegaprotocol/vega/issues/10147) - Add network transfer largest share to the transfers if needed.
- [10158](https://github.com/vegaprotocol/vega/issues/10158) - Add the network as the zero-share default party in settlement engine.

## 0.73.0

Expand Down
32 changes: 29 additions & 3 deletions core/settlement/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ func (e *Engine) Settle(t time.Time, settlementData *num.Uint) ([]*types.Transfe
// each change in position has to be calculated using the exact price of the trade.
func (e *Engine) AddTrade(trade *types.Trade) {
e.mu.Lock()
defer e.mu.Unlock()
// network registers a wash trade to update its position
if trade.Buyer == types.NetworkParty && trade.Buyer == trade.Seller {
e.addNetworkTrade(trade)
return
}
var buyerSize, sellerSize int64
// checking the len of cd shouldn't be required here, but it is needed in the second if
// in case the buyer and seller are one and the same...
Expand Down Expand Up @@ -189,7 +195,22 @@ func (e *Engine) AddTrade(trade *types.Trade) {
size: -size,
newSize: sellerSize - size,
})
e.mu.Unlock()
}

func (e *Engine) addNetworkTrade(trade *types.Trade) {
tSize := int64(trade.Size)
// sell wash trade, the settled position of the network decreases.
if trade.Aggressor == types.SideSell {
tSize *= -1
}
e.settledPosition[types.NetworkParty] += tSize
trades := e.trades[types.NetworkParty]
// all trades should be based on the new settled position size
for i, t := range trades {
t.newSize += tSize
trades[i] = t
}
e.trades[types.NetworkParty] = trades
}

func (e *Engine) HasTraded() bool {
Expand Down Expand Up @@ -356,6 +377,9 @@ func (e *Engine) SettleMTM(ctx context.Context, markPrice *num.Uint, positions [
delta := num.UintZero().Sub(lossTotal, winTotal)
if !delta.IsZero() {
if zeroAmts {
if appendLargest {
zeroShares = append(zeroShares, largestShare)
}
zRound := num.DecimalFromInt64(int64(len(zeroShares)))
// there are more transfers from losses than we pay out to wins, but some winning parties have zero transfers
// this delta should == combined win decimals, let's sanity check this!
Expand Down Expand Up @@ -387,10 +411,12 @@ func (e *Engine) SettleMTM(ctx context.Context, markPrice *num.Uint, positions [
}
// append wins after loss transfers
transfers = append(transfers, wins...)
if len(transfers) > 0 && appendLargest {
if len(transfers) > 0 && appendLargest && largestShare.transfer != nil {
transfers = append(transfers, largestShare)
}
e.broker.SendBatch(evts)
if len(evts) > 0 {
e.broker.SendBatch(evts)
}
timer.EngineTimeCounterAdd()
return transfers
}
Expand Down
293 changes: 291 additions & 2 deletions core/settlement/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,10 +752,299 @@ func testAddNewPartySelfTrade(t *testing.T) {
engine.Update(init)
engine.AddTrade(trade)
noTransfers := engine.SettleMTM(context.Background(), markPrice, positions)
assert.Len(t, noTransfers, 2)
assert.Len(t, noTransfers, 1)
assert.Nil(t, noTransfers[0].Transfer())
}

func TestNetworkPartyCloseout(t *testing.T) {
engine := getTestEngine(t)
currentMP := num.NewUint(1000)
// network trade
nTrade := &types.Trade{
Buyer: types.NetworkParty,
Seller: types.NetworkParty,
Price: currentMP.Clone(),
Size: 10,
}
nPosition := testPos{
party: types.NetworkParty,
size: 10,
price: currentMP.Clone(),
}
sellPrice := num.NewUint(990)
// now trade with health party, at some different price
// trigger a loss for the network.
cTrade := &types.Trade{
Buyer: "party1",
Seller: types.NetworkParty,
Size: 2,
Price: sellPrice.Clone(),
}
init := []events.MarketPosition{
nPosition,
testPos{
party: "party1",
size: 0,
price: currentMP.Clone(),
},
}
positions := []events.MarketPosition{
testPos{
party: types.NetworkParty,
size: 8,
price: currentMP.Clone(),
},
testPos{
party: "party1",
size: 2,
price: currentMP.Clone(),
},
}
engine.Update(init)
engine.AddTrade(nTrade)
engine.AddTrade(cTrade)
transfers := engine.SettleMTM(context.Background(), currentMP, positions)
assert.NotEmpty(t, transfers)
assert.Len(t, transfers, 2)
}

func TestNetworkCloseoutZero(t *testing.T) {
engine := getTestEngine(t)
currentMP := num.NewUint(1000)
// network trade
nTrade := &types.Trade{
Buyer: types.NetworkParty,
Seller: types.NetworkParty,
Price: currentMP.Clone(),
Size: 10,
}
nPosition := testPos{
party: types.NetworkParty,
size: 10,
price: currentMP.Clone(),
}
sellPrice := num.NewUint(999)
// now trade with health party, at some different price
// trigger a loss for the network.
cTrades := []*types.Trade{
{
Buyer: "party1",
Seller: types.NetworkParty,
Size: 1,
Price: sellPrice.Clone(),
},
{
Buyer: "party2",
Seller: types.NetworkParty,
Size: 1,
Price: sellPrice.Clone(),
},
}
init := []events.MarketPosition{
nPosition,
testPos{
party: "party1",
size: 0,
price: currentMP.Clone(),
},
testPos{
party: "party2",
size: 0,
price: currentMP.Clone(),
},
testPos{
party: "party3",
size: -5,
price: currentMP.Clone(),
},
testPos{
party: "party4",
size: -5,
price: currentMP.Clone(),
},
}
positions := []events.MarketPosition{
testPos{
party: types.NetworkParty,
size: 8,
price: currentMP.Clone(),
},
testPos{
party: "party1",
size: 1,
price: currentMP.Clone(),
},
testPos{
party: "party2",
size: 1,
price: currentMP.Clone(),
},
testPos{
party: "party3",
size: -5,
price: currentMP.Clone(),
},
testPos{
party: "party4",
size: -5,
price: currentMP.Clone(),
},
}
engine.Update(init)
engine.AddTrade(nTrade)
for _, cTrade := range cTrades {
engine.AddTrade(cTrade)
}
transfers := engine.SettleMTM(context.Background(), currentMP, positions)
assert.NotEmpty(t, transfers)
// now that the network has an established long position, make short positions close out and mark to market
// party 3 closes their position, lowering the mark price
newMP := num.NewUint(990)
trade := &types.Trade{
Buyer: "party3",
Seller: "party1",
Price: newMP,
Size: 1,
}
positions = []events.MarketPosition{
testPos{
party: types.NetworkParty,
size: 8,
price: newMP.Clone(),
},
testPos{
party: "party1",
size: 0,
price: newMP.Clone(),
},
testPos{
party: "party2",
size: 1,
price: newMP.Clone(),
},
testPos{
party: "party3",
size: -4,
price: newMP.Clone(),
},
testPos{
party: "party4",
size: -5,
price: newMP.Clone(),
},
}
engine.AddTrade(trade)
transfers = engine.SettleMTM(context.Background(), newMP, positions)
assert.NotEmpty(t, transfers)
// now make it look like party2 got distressed because of this MTM settlement
nTrade = &types.Trade{
Price: newMP.Clone(),
Size: 1,
Buyer: types.NetworkParty,
Seller: types.NetworkParty,
}
positions = []events.MarketPosition{
testPos{
party: types.NetworkParty,
size: 9,
price: newMP.Clone(),
},
testPos{
party: "party3",
size: -4,
price: newMP.Clone(),
},
testPos{
party: "party4",
size: -5,
price: newMP.Clone(),
},
}
engine.AddTrade(nTrade)
transfers = engine.SettleMTM(context.Background(), newMP, positions) // amounts are zero here (was trade only)
assert.NotEmpty(t, transfers)
newMP = num.NewUint(995)
positions = []events.MarketPosition{
testPos{
party: types.NetworkParty,
size: 9,
price: newMP.Clone(),
},
testPos{
party: "party3",
size: -4,
price: newMP.Clone(),
},
testPos{
party: "party4",
size: -5,
price: newMP.Clone(),
},
}
transfers = engine.SettleMTM(context.Background(), newMP.Clone(), positions)
assert.NotEmpty(t, transfers)
// now the same, but network loses
newMP = num.NewUint(990)
positions = []events.MarketPosition{
testPos{
party: types.NetworkParty,
size: 9,
price: newMP.Clone(),
},
testPos{
party: "party3",
size: -4,
price: newMP.Clone(),
},
testPos{
party: "party4",
size: -5,
price: newMP.Clone(),
},
}
transfers = engine.SettleMTM(context.Background(), newMP.Clone(), positions)
assert.NotEmpty(t, transfers)
// assume no trades occurred, but the mark price has changed (shouldn't happen, but this could end up with a situation where network profits without trading)
// network disposes of its position and profits
disposePrice := num.NewUint(1010)
trades := []*types.Trade{
{
Seller: types.NetworkParty,
Buyer: "party3",
Price: disposePrice.Clone(),
Size: 4,
},
{
Seller: types.NetworkParty,
Buyer: "party4",
Price: disposePrice.Clone(),
Size: 5,
},
}
positions = []events.MarketPosition{
testPos{
party: types.NetworkParty,
size: 0,
price: newMP.Clone(),
},
testPos{
party: "party3",
size: 0,
price: newMP.Clone(),
},
testPos{
party: "party4",
size: 0,
price: newMP.Clone(),
},
}
for _, tr := range trades {
engine.AddTrade(tr)
}
transfers = engine.SettleMTM(context.Background(), num.NewUint(1000), positions)
assert.NotEmpty(t, transfers)
}

func testAddNewParty(t *testing.T) {
engine := getTestEngine(t)
defer engine.Finish()
Expand Down Expand Up @@ -796,7 +1085,7 @@ func testAddNewParty(t *testing.T) {
engine.Update(init)
engine.AddTrade(trade)
noTransfers := engine.SettleMTM(context.Background(), markPrice, positions)
assert.Len(t, noTransfers, 3)
assert.Len(t, noTransfers, 2)
for _, v := range noTransfers {
assert.Nil(t, v.Transfer())
}
Expand Down

0 comments on commit efed0e3

Please sign in to comment.