Skip to content

Commit

Permalink
Merge pull request #2239 from vegaprotocol/feature/check-for-funds-to…
Browse files Browse the repository at this point in the history
…-release

ensure margin accounts funds are release each time a position goes back completely to 0.
  • Loading branch information
3jtechtest committed Sep 15, 2020
2 parents 4526ebf + c7ded40 commit fae69a1
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
33 changes: 33 additions & 0 deletions collateral/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,39 @@ func (e *Engine) RemoveDistressed(ctx context.Context, traders []events.MarketPo
return &resp, nil
}

func (e *Engine) ClearPartyMarginAccount(ctx context.Context, party, market, asset string) (*types.TransferResponse, error) {
acc, err := e.GetAccountByID(e.accountID(market, party, asset, types.AccountType_ACCOUNT_TYPE_MARGIN))
if err != nil {
return nil, err
}
resp := types.TransferResponse{
Transfers: []*types.LedgerEntry{},
}

if acc.Balance > 0 {
genAcc, err := e.GetAccountByID(e.accountID(noMarket, party, asset, types.AccountType_ACCOUNT_TYPE_GENERAL))
if err != nil {
return nil, err
}

resp.Transfers = append(resp.Transfers, &types.LedgerEntry{
FromAccount: acc.Id,
ToAccount: genAcc.Id,
Amount: acc.Balance,
Reference: types.TransferType_TRANSFER_TYPE_MARGIN_HIGH.String(),
Type: types.TransferType_TRANSFER_TYPE_MARGIN_HIGH.String(),
Timestamp: e.currentTime,
})
if err := e.IncrementBalance(ctx, genAcc.Id, acc.Balance); err != nil {
return nil, err
}
if err := e.UpdateBalance(ctx, acc.Id, 0); err != nil {
return nil, err
}
}
return &resp, nil
}

// CreateMarketAccounts will create all required accounts for a market once
// a new market is accepted through the network
func (e *Engine) CreateMarketAccounts(ctx context.Context, marketID, asset string, insurance uint64) (insuranceID, settleID string, err error) {
Expand Down
32 changes: 32 additions & 0 deletions collateral/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func TestCollateralTransfer(t *testing.T) {
t.Run("test collecting sells - cases where settle account is full + where insurance pool is tapped", testDistributeWin)
t.Run("test collecting both buys and sells - Successfully collect buy and sell in a single call", testProcessBoth)
t.Run("test distribution insufficient funds - Transfer losses (partial), distribute wins pro-rate", testProcessBothProRated)
t.Run("test releas party margin account", testReleasePartyMarginAccount)
}

func TestCollateralMarkToMarket(t *testing.T) {
Expand Down Expand Up @@ -98,6 +99,36 @@ func testFeesTransferContinuousNoTransfer(t *testing.T) {
assert.Nil(t, err)
}

func testReleasePartyMarginAccount(t *testing.T) {
eng := getTestEngine(t, "test-market", 0)
defer eng.Finish()

trader := "mytrader"
// create trader
eng.broker.EXPECT().Send(gomock.Any()).Times(4)
gen, err := eng.Engine.CreatePartyGeneralAccount(context.Background(), trader, testMarketAsset)
mar, err := eng.Engine.CreatePartyMarginAccount(context.Background(), trader, testMarketID, testMarketAsset)
assert.Nil(t, err)

// add funds
eng.broker.EXPECT().Send(gomock.Any()).Times(1)
err = eng.Engine.UpdateBalance(context.Background(), gen, 100)
assert.Nil(t, err)
eng.broker.EXPECT().Send(gomock.Any()).Times(1)
err = eng.Engine.UpdateBalance(context.Background(), mar, 500)
assert.Nil(t, err)

eng.broker.EXPECT().Send(gomock.Any()).Times(2)
_, err = eng.ClearPartyMarginAccount(
context.Background(), trader, testMarketID, testMarketAsset)
assert.NoError(t, err)
generalAcc, _ := eng.GetAccountByID(gen)
assert.Equal(t, 600, int(generalAcc.Balance))
marginAcc, _ := eng.GetAccountByID(mar)
assert.Equal(t, 0, int(marginAcc.Balance))

}

func testFeeTransferContinuousNoFunds(t *testing.T) {
eng := getTestEngine(t, "test-market", 0)
defer eng.Finish()
Expand Down Expand Up @@ -127,6 +158,7 @@ func testFeeTransferContinuousNoFunds(t *testing.T) {
context.Background(), testMarketID, testMarketAsset, transferFeesReq)
assert.Nil(t, transfers)
assert.EqualError(t, err, collateral.ErrInsufficientFundsToPayFees.Error())

}

func testFeeTransferContinuousNotEnoughFunds(t *testing.T) {
Expand Down
33 changes: 33 additions & 0 deletions execution/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,33 @@ func (m *Market) validateAccounts(ctx context.Context, order *types.Order) error
return nil
}

func (m *Market) releaseMarginExcess(ctx context.Context, partyID string) {
// if this position went 0
pos, ok := m.position.GetPositionByPartyID(partyID)
if !ok {
// position was never created or party went distressed and don't exist
// all good we can return
return
}

// now chec if all buy/sell/size are 0
if pos.Buy() != 0 || pos.Sell() != 0 || pos.Size() != 0 || pos.VWBuy() != 0 || pos.VWSell() != 0 {
// position is not 0, nothing to release surely
return
}

asset, _ := m.mkt.GetAsset()
transfers, err := m.collateral.ClearPartyMarginAccount(
ctx, partyID, m.GetID(), asset)
if err != nil {
m.log.Error("unable to clear party margin account", logging.Error(err))
return
}
evt := events.NewTransferResponse(
ctx, []*types.TransferResponse{transfers})
m.broker.Send(evt)
}

// SubmitOrder submits the given order
func (m *Market) SubmitOrder(ctx context.Context, order *types.Order) (*types.OrderConfirmation, error) {
timer := metrics.NewTimeCounter(m.mkt.Id, "market", "SubmitOrder")
Expand Down Expand Up @@ -692,6 +719,10 @@ func (m *Market) SubmitOrder(ctx context.Context, order *types.Order) (*types.Or
return nil, ErrMarginCheckFailed
}

// from here we may have assigned some margin.
// we add the check to roll it back in case we have a 0 positions after this
defer m.releaseMarginExcess(ctx, order.PartyID)

// If we are not in an opening auction, apply fees
var trades []*types.Trade
if m.mkt.OpeningAuction == nil &&
Expand Down Expand Up @@ -1541,6 +1572,8 @@ func (m *Market) CancelOrder(ctx context.Context, partyID, orderID string) (*typ
return nil, types.ErrInvalidPartyID
}

defer m.releaseMarginExcess(ctx, partyID)

cancellation, err := m.matching.CancelOrder(order)
if cancellation == nil || err != nil {
if m.log.GetLevel() == logging.DebugLevel {
Expand Down
92 changes: 92 additions & 0 deletions integration/features/ensure-funds-are-released.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
Feature: Test margins releases on position = 0

Background:
Given the insurance pool initial balance for the markets is "0":
And the executon engine have these markets:
| name | baseName | quoteName | asset | markprice | risk model | lamd/long | tau/short | mu | r | sigma | release factor | initial factor | search factor | settlementPrice | openAuction | trading mode | makerFee | infrastructureFee | liquidityFee |
| ETH/DEC19 | ETH | BTC | BTC | 94 | simple | 0.2 | 0.1 | 0 | 0.016 | 2.0 | 5 | 4 | 3.2 | 42 | 0 | continuous | 0 | 0 | 0 |

Scenario: No margin left for fok order as first order
# setup accounts
Given the following traders:
| name | amount |
| traderGuy | 1000000000 |
Then I Expect the traders to have new general account:
| name | asset |
| traderGuy | BTC |

# setup previous mark price
Then traders place following orders:
| trader | id | type | volume | price | resulting trades | type | tif |

Then traders place following orders:
| trader | id | type | volume | price | resulting trades | type | tif |
| traderGuy | ETH/DEC19 | buy | 13 | 15000 | 0 | TYPE_LIMIT | TIF_FOK |

# checking margins
Then I expect the trader to have a margin:
| trader | asset | id | margin | general |
| traderGuy | BTC | ETH/DEC19 | 0 | 1000000000 |

Scenario: No margin left for wash trade
# setup accounts
Given the following traders:
| name | amount |
| traderGuy | 1000000000 |
Then I Expect the traders to have new general account:
| name | asset |
| traderGuy | BTC |

# setup previous mark price
Then traders place following orders:
| trader | id | type | volume | price | resulting trades | type | tif |

Then traders place following orders:
| trader | id | type | volume | price | resulting trades | type | tif |
| traderGuy | ETH/DEC19 | buy | 13 | 15000 | 0 | TYPE_LIMIT | TIF_GTC |

# checking margins
Then I expect the trader to have a margin:
| trader | asset | id | margin | general |
| traderGuy | BTC | ETH/DEC19 | 980 | 999999020 |

# now we place an order which would wash trade and see
Then traders place following orders:
| trader | id | type | volume | price | resulting trades | type | tif |
| traderGuy | ETH/DEC19 | sell | 13 | 15000 | 0 | TYPE_LIMIT | TIF_GTC |

# checking margins, should have the margins required for the current order
Then I expect the trader to have a margin:
| trader | asset | id | margin | general |
| traderGuy | BTC | ETH/DEC19 | 980 | 999999020 |

Scenario: No margin left after cancelling order and getting back to 0 position
# setup accounts
Given the following traders:
| name | amount |
| traderGuy | 1000000000 |
Then I Expect the traders to have new general account:
| name | asset |
| traderGuy | BTC |

# setup previous mark price
Then traders place following orders:
| trader | id | type | volume | price | resulting trades | type | tif |

Then traders place following orders with references:
| trader | id | type | volume | price | resulting trades | type | tif | reference |
| traderGuy | ETH/DEC19 | buy | 13 | 15000 | 0 | TYPE_LIMIT | TIF_GTC | ref-1 |

# checking margins
Then I expect the trader to have a margin:
| trader | asset | id | margin | general |
| traderGuy | BTC | ETH/DEC19 | 980 | 999999020 |

# cancel the order
Then traders cancels the following orders reference:
| trader | reference |
| traderGuy | ref-1 |

Then I expect the trader to have a margin:
| trader | asset | id | margin | general |
| traderGuy | BTC | ETH/DEC19 | 0 | 1000000000 |

0 comments on commit fae69a1

Please sign in to comment.