Skip to content

Commit

Permalink
Merge pull request #10144 from vegaprotocol/network-position
Browse files Browse the repository at this point in the history
Network position
  • Loading branch information
EVODelavega authored Nov 20, 2023
2 parents 364ab0b + fbaf3f1 commit 7f99ce0
Show file tree
Hide file tree
Showing 14 changed files with 230 additions and 217 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- [9231](https://github.com/vegaprotocol/vega/issues/9231) - Add a `JoinTeam API`
- [9981](https://github.com/vegaprotocol/vega/issues/9981) - Support filtering on epoch range on transfers.
- [9981](https://github.com/vegaprotocol/vega/issues/9981) - Support filtering on status on transfers.
- [10104](https://github.com/vegaprotocol/vega/issues/10104) - Add network position tracking.

### 🐛 Fixes

Expand Down
21 changes: 21 additions & 0 deletions core/collateral/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,19 @@ func (e *Engine) mtmOrFundingSettlement(ctx context.Context, marketID string, tr

// GetPartyMargin will return the current margin for a given party.
func (e *Engine) GetPartyMargin(pos events.MarketPosition, asset, marketID string) (events.Margin, error) {
if pos.Party() == types.NetworkParty {
ins, err := e.GetMarketInsurancePoolAccount(marketID, asset)
if err != nil {
return nil, ErrAccountDoesNotExist
}
return marginUpdate{
MarketPosition: pos,
margin: ins,
asset: asset,
marketID: marketID,
marginShortFall: num.UintZero(),
}, nil
}
genID := e.accountID(noMarket, pos.Party(), asset, types.AccountTypeGeneral)
marginID := e.accountID(marketID, pos.Party(), asset, types.AccountTypeMargin)
bondID := e.accountID(marketID, pos.Party(), asset, types.AccountTypeBond)
Expand Down Expand Up @@ -1644,6 +1657,10 @@ func (e *Engine) MarginUpdate(ctx context.Context, marketID string, updates []ev
)
// create "fake" settle account for market ID
for _, update := range updates {
if update.Party() == types.NetworkParty {
// network party is ignored here
continue
}
transfer := update.Transfer()
// although this is mainly a duplicate event, we need to pass it to getTransferRequest
mevt := &marginUpdate{
Expand Down Expand Up @@ -1951,6 +1968,10 @@ func (e *Engine) RemoveBondAccount(partyID, marketID, asset string) error {

// MarginUpdateOnOrder will run the margin updates over a set of risk events (margin updates).
func (e *Engine) MarginUpdateOnOrder(ctx context.Context, marketID string, update events.Risk) (*types.LedgerMovement, events.Margin, error) {
// network party is ignored for margin stuff.
if update.Party() == types.NetworkParty {
return nil, nil, nil
}
// create "fake" settle account for market ID
settle := &types.Account{
MarketID: marketID,
Expand Down
70 changes: 28 additions & 42 deletions core/execution/future/liquidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,28 @@ func (m *Market) checkNetwork(ctx context.Context, now time.Time) error {
if order == nil {
return nil
}
// register the network order on the positions engine
_ = m.position.RegisterOrder(ctx, order)
order.OriginalPrice = num.UintZero().Div(order.Price, m.priceFactor)
m.broker.Send(events.NewOrderEvent(ctx, order))
conf, err := m.matching.SubmitOrder(order)
if err != nil {
m.log.Panic("Failure after submitting order to matching engine",
logging.Order(order),
logging.Error(err),
)
// order failed to uncross, reject and done
return m.unregisterAndReject(ctx, order, err)
}
order.ClearUpExtraRemaining()
// this should not be possible (network position can't really flip)
if order.ReduceOnly && order.Remaining > 0 {
order.Status = types.OrderStatusStopped
}

// if the order is not staying in the book, then we remove it
// from the potential positions
if order.IsFinished() && order.Remaining > 0 {
_ = m.position.UnregisterOrder(ctx, order)
}
// send the event with the order in its final state
m.broker.Send(events.NewOrderEvent(ctx, order))
m.handleConfirmationPassiveOrders(ctx, conf)

// no trades...
if len(conf.Trades) == 0 {
Expand All @@ -51,6 +63,8 @@ func (m *Market) checkNetwork(ctx context.Context, now time.Time) error {
fees, _ := m.fee.GetFeeForPositionResolution(conf.Trades)
tresps, err := m.collateral.TransferFees(ctx, m.GetID(), m.settlementAsset, fees)
if err != nil {
// we probably should reject the order, although if we end up here we have a massive problem.
_ = m.position.UnregisterOrder(ctx, order)
// if we get an eror transfer fees, we are missing accounts, and something is terribly wrong.
// the fees we get from the fee engine result in transfers with the minimum amount set to 0,
// so the only thing that could go wrong is missing accounts.
Expand All @@ -62,48 +76,20 @@ func (m *Market) checkNetwork(ctx context.Context, now time.Time) error {
if len(tresps) > 0 {
m.broker.Send(events.NewLedgerMovements(ctx, tresps))
}
// Now that the fees have been taken care of, get the current last traded price:
lastTraded := m.getLastTradedPrice()
// now handle the confirmation like you would any other order/trade confirmation
m.handleConfirmation(ctx, conf)
// restore the last traded price, the network trades do not count towards the mark price
// nor do they factor in to the price monitoring logic.
m.lastTradedPrice = lastTraded
// update the liquidation engine to reflect the trades have happened
m.liquidation.UpdateNetworkPosition(conf.Trades)

// now update the network position, finish up the trades, update positions, etc...
// essentially, handle trades
// Insert all trades resulted from the executed order
tradeEvts := make([]events.Event, 0, len(conf.Trades))
// get total traded volume
tradedValue, _ := num.UintFromDecimal(
conf.TradedValue().ToDecimal().Div(m.positionFactor))
for idx, trade := range conf.Trades {
trade.SetIDs(m.idgen.NextID(), conf.Order, conf.PassiveOrdersAffected[idx])

// setup the type of the trade to network
// this trade did happen with a GOOD trader to
// 0 out the BAD trader position
trade.Type = types.TradeTypeNetworkCloseOutGood
tradeEvts = append(tradeEvts, events.NewTradeEvent(ctx, *trade))

// Update positions - this is a special trade involving the network as party
// so rather than checking this every time we call Update, call special UpdateNetwork
m.position.UpdateNetwork(ctx, trade, conf.PassiveOrdersAffected[idx])
// record the updated passive side's position
partyPos, _ := m.position.GetPositionByPartyID(conf.PassiveOrdersAffected[idx].Party)
m.marketActivityTracker.RecordPosition(m.settlementAsset, conf.PassiveOrdersAffected[idx].Party, m.mkt.ID, partyPos.Size(), trade.Price, m.positionFactor, m.timeService.GetTimeNow())

if err := m.tsCalc.RecordOpenInterest(m.position.GetOpenInterest(), now); err != nil {
m.log.Debug("unable record open interest",
logging.String("market-id", m.GetID()),
logging.Error(err))
}

m.settlement.AddTrade(trade)
}
m.feeSplitter.AddTradeValue(tradedValue)
m.marketActivityTracker.AddValueTraded(m.settlementAsset, m.mkt.ID, tradedValue)
if len(tradeEvts) > 0 {
m.broker.SendBatch(tradeEvts)
}
// perform a MTM settlement after the trades
m.confirmMTM(ctx, false)
// check for reference moves
// check for reference moves again? We should've already done this
// This can probably be removed
m.checkForReferenceMoves(ctx, conf.PassiveOrdersAffected, false)
return nil
}
2 changes: 1 addition & 1 deletion core/execution/future/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ func NewMarket(
if mkt.LiquidationStrategy == nil {
mkt.LiquidationStrategy = liquidation.GetLegacyStrat()
}
le := liquidation.New(mkt.LiquidationStrategy, mkt.GetID(), broker, book, auctionState, timeService, marketLiquidity)
le := liquidation.New(mkt.LiquidationStrategy, mkt.GetID(), broker, book, auctionState, timeService, marketLiquidity, positionEngine, settleEngine)

marketType := mkt.MarketType()
market := &Market{
Expand Down
2 changes: 1 addition & 1 deletion core/execution/future/market_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func NewMarketFromSnapshot(
if mkt.LiquidationStrategy == nil {
mkt.LiquidationStrategy = liquidation.GetLegacyStrat()
}
le := liquidation.New(mkt.LiquidationStrategy, mkt.GetID(), broker, book, as, timeService, marketLiquidity)
le := liquidation.New(mkt.LiquidationStrategy, mkt.GetID(), broker, book, as, timeService, marketLiquidity, positionEngine, settleEngine)

now := timeService.GetTimeNow()
marketType := mkt.MarketType()
Expand Down
55 changes: 39 additions & 16 deletions core/execution/liquidation/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ import (
"code.vegaprotocol.io/vega/core/events"
"code.vegaprotocol.io/vega/core/execution/common"
"code.vegaprotocol.io/vega/core/idgeneration"
"code.vegaprotocol.io/vega/core/positions"
"code.vegaprotocol.io/vega/core/types"
vegacontext "code.vegaprotocol.io/vega/libs/context"
"code.vegaprotocol.io/vega/libs/crypto"
"code.vegaprotocol.io/vega/libs/num"
)

//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/execution/liquidation Book,MarketLiquidity,IDGen
//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/execution/liquidation Book,MarketLiquidity,IDGen,Positions,Settlement

type Book interface {
GetVolumeAtPrice(price *num.Uint, side types.Side) uint64
Expand All @@ -44,6 +45,15 @@ type IDGen interface {
NextID() string
}

type Positions interface {
RegisterOrder(ctx context.Context, order *types.Order) *positions.MarketPosition
Update(ctx context.Context, trade *types.Trade, passiveOrder, aggressiveOrder *types.Order) []events.MarketPosition
}

type Settlement interface {
AddTrade(trade *types.Trade)
}

type Engine struct {
// settings, orderbook, network pos data
cfg *types.LiquidationStrategy
Expand All @@ -55,6 +65,8 @@ type Engine struct {
nextStep time.Time
tSvc common.TimeService
ml MarketLiquidity
position Positions
settle Settlement
stopped bool
}

Expand Down Expand Up @@ -89,20 +101,22 @@ func GetLegacyStrat() *types.LiquidationStrategy {
return legacyStrat.DeepClone()
}

func New(cfg *types.LiquidationStrategy, mktID string, broker common.Broker, book Book, as common.AuctionState, tSvc common.TimeService, ml MarketLiquidity) *Engine {
func New(cfg *types.LiquidationStrategy, mktID string, broker common.Broker, book Book, as common.AuctionState, tSvc common.TimeService, ml MarketLiquidity, pe Positions, se Settlement) *Engine {
// NOTE: This can be removed after protocol upgrade
if cfg == nil {
cfg = legacyStrat.DeepClone()
}
return &Engine{
cfg: cfg,
broker: broker,
mID: mktID,
book: book,
as: as,
tSvc: tSvc,
ml: ml,
pos: &Pos{},
cfg: cfg,
broker: broker,
mID: mktID,
book: book,
as: as,
tSvc: tSvc,
ml: ml,
position: pe,
settle: se,
pos: &Pos{},
}
}

Expand Down Expand Up @@ -195,7 +209,7 @@ func (e *Engine) ClearDistressedParties(ctx context.Context, idgen IDGen, closed
for _, cp := range closed {
e.pos.open += cp.Size()
// get the orders and trades so we can send events to update the datanode
o1, o2, t := e.getOrdersAndTrade(cp, idgen, now, mp, mmp)
o1, o2, t := e.getOrdersAndTrade(ctx, cp, idgen, now, mp, mmp)
orders = append(orders, events.NewOrderEvent(ctx, o1), events.NewOrderEvent(ctx, o2))
trades = append(trades, events.NewTradeEvent(ctx, *t))
netTrades = append(netTrades, t)
Expand Down Expand Up @@ -239,7 +253,7 @@ func (e *Engine) UpdateNetworkPosition(trades []*types.Trade) {
}
}

func (e *Engine) getOrdersAndTrade(pos events.Margin, idgen IDGen, now time.Time, price, dpPrice *num.Uint) (*types.Order, *types.Order, *types.Trade) {
func (e *Engine) getOrdersAndTrade(ctx context.Context, pos events.Margin, idgen IDGen, now time.Time, price, dpPrice *num.Uint) (*types.Order, *types.Order, *types.Trade) {
tSide, nSide := types.SideSell, types.SideBuy // one of them will have to sell
s := pos.Size()
size := uint64(s)
Expand All @@ -261,14 +275,16 @@ func (e *Engine) getOrdersAndTrade(pos events.Margin, idgen IDGen, now time.Time
TimeInForce: types.OrderTimeInForceFOK, // this is an all-or-nothing order, so TIME_IN_FORCE == FOK
Type: types.OrderTypeNetwork,
Size: size,
Remaining: 0,
Remaining: size,
Side: nSide,
}
e.position.RegisterOrder(ctx, &order)
order.Remaining = 0
partyOrder := types.Order{
ID: idgen.NextID(),
MarketID: e.mID,
Size: size,
Remaining: 0,
Remaining: size,
Status: types.OrderStatusFilled,
Party: pos.Party(),
Side: tSide, // assume sell, price is zero in that case anyway
Expand All @@ -279,6 +295,8 @@ func (e *Engine) getOrdersAndTrade(pos events.Margin, idgen IDGen, now time.Time
TimeInForce: types.OrderTimeInForceFOK, // this is an all-or-nothing order, so TIME_IN_FORCE == FOK
Type: types.OrderTypeNetwork,
}
e.position.RegisterOrder(ctx, &partyOrder)
partyOrder.Remaining = 0
buyParty = order.Party
sellParty = partyOrder.Party
sellID = partyOrder.ID
Expand All @@ -296,12 +314,17 @@ func (e *Engine) getOrdersAndTrade(pos events.Margin, idgen IDGen, now time.Time
Aggressor: order.Side, // we consider network to be aggressor
BuyOrder: buyID,
SellOrder: sellID,
Buyer: buyParty,
Seller: sellParty,
Buyer: types.NetworkParty,
Seller: types.NetworkParty,
Timestamp: now.UnixNano(),
Type: types.TradeTypeNetworkCloseOutBad,
SellerFee: types.NewFee(),
BuyerFee: types.NewFee(),
}
// settlement engine should see this as a wash trade
e.settle.AddTrade(&trade)
// the for the rest of the core, this should not seem like a wash trade though...
trade.Buyer, trade.Seller = buyParty, sellParty
e.position.Update(ctx, &trade, &order, &partyOrder)
return &order, &partyOrder, &trade
}
Loading

0 comments on commit 7f99ce0

Please sign in to comment.