code.vegaprotocol.io/vega@v0.79.0/datanode/sqlsubscribers/position.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 sqlsubscribers
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sync"
    22  
    23  	"code.vegaprotocol.io/vega/core/events"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/datanode/entities"
    26  	"code.vegaprotocol.io/vega/libs/num"
    27  	"code.vegaprotocol.io/vega/protos/vega"
    28  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    29  
    30  	"github.com/pkg/errors"
    31  )
    32  
    33  type fundingPaymentsEvent interface {
    34  	MarketID() string
    35  	IsParty(id string) bool
    36  	FundingPayments() *eventspb.FundingPayments
    37  }
    38  
    39  type tradeEvent interface {
    40  	MarketID() string
    41  	IsParty(id string) bool // we don't use this one, but it's to make sure we identify the event correctly
    42  	Trade() vega.Trade
    43  }
    44  
    45  type positionEventBase interface {
    46  	events.Event
    47  	MarketID() string
    48  	PartyID() string
    49  	Timestamp() int64
    50  }
    51  
    52  type positionSettlement interface {
    53  	positionEventBase
    54  	Price() *num.Uint
    55  	PositionFactor() num.Decimal
    56  	Trades() []events.TradeSettlement
    57  }
    58  
    59  type lossSocialization interface {
    60  	positionEventBase
    61  	Amount() *num.Int
    62  	IsFunding() bool
    63  }
    64  
    65  type settleDistressed interface {
    66  	positionEventBase
    67  	Margin() *num.Uint
    68  }
    69  
    70  type ordersClosed interface {
    71  	MarketID() string
    72  	Parties() []string
    73  }
    74  
    75  type settleMarket interface {
    76  	positionEventBase
    77  	SettledPrice() *num.Uint
    78  	PositionFactor() num.Decimal
    79  }
    80  
    81  type distressedPositions interface {
    82  	MarketID() string
    83  	SafeParties() []string
    84  	DistressedParties() []string
    85  }
    86  
    87  type PositionStore interface {
    88  	Add(context.Context, entities.Position) error
    89  	GetByMarket(ctx context.Context, marketID string) ([]entities.Position, error)
    90  	GetByMarketAndParty(ctx context.Context, marketID string, partyID string) (entities.Position, error)
    91  	GetByMarketAndParties(ctx context.Context, marketID string, parties []string) ([]entities.Position, error)
    92  	Flush(ctx context.Context) error
    93  }
    94  
    95  type MarketSvc interface {
    96  	GetMarketScalingFactor(ctx context.Context, marketID string) (num.Decimal, bool)
    97  	IsSpotMarket(ctx context.Context, marketID string) bool
    98  }
    99  
   100  type Position struct {
   101  	subscriber
   102  	store  PositionStore
   103  	mktSvc MarketSvc
   104  	mutex  sync.Mutex
   105  }
   106  
   107  func NewPosition(store PositionStore, mktSvc MarketSvc) *Position {
   108  	t := &Position{
   109  		store:  store,
   110  		mktSvc: mktSvc,
   111  	}
   112  	return t
   113  }
   114  
   115  func (p *Position) Types() []events.Type {
   116  	return []events.Type{
   117  		events.SettlePositionEvent,
   118  		events.SettleDistressedEvent,
   119  		events.LossSocializationEvent,
   120  		events.SettleMarketEvent,
   121  		events.TradeEvent,
   122  		events.DistressedOrdersClosedEvent,
   123  		events.DistressedPositionsEvent,
   124  		events.FundingPaymentsEvent,
   125  	}
   126  }
   127  
   128  func (p *Position) Flush(ctx context.Context) error {
   129  	err := p.store.Flush(ctx)
   130  	return errors.Wrap(err, "flushing positions")
   131  }
   132  
   133  func (p *Position) Push(ctx context.Context, evt events.Event) error {
   134  	switch event := evt.(type) {
   135  	case positionSettlement:
   136  		return p.handlePositionSettlement(ctx, event)
   137  	case lossSocialization:
   138  		return p.handleLossSocialization(ctx, event)
   139  	case settleDistressed:
   140  		return p.handleSettleDistressed(ctx, event)
   141  	case settleMarket:
   142  		return p.handleSettleMarket(ctx, event)
   143  	case tradeEvent:
   144  		return p.handleTradeEvent(ctx, event)
   145  	case ordersClosed:
   146  		return p.handleOrdersClosedEvent(ctx, event)
   147  	case distressedPositions:
   148  		return p.handleDistressedPositions(ctx, event)
   149  	case fundingPaymentsEvent:
   150  		return p.handleFundingPayments(ctx, event)
   151  	default:
   152  		return errors.Errorf("unknown event type %s", evt.Type().String())
   153  	}
   154  }
   155  
   156  func (p *Position) handleFundingPayments(ctx context.Context, event fundingPaymentsEvent) error {
   157  	p.mutex.Lock()
   158  	defer p.mutex.Unlock()
   159  	mkt := event.MarketID()
   160  	evt := event.FundingPayments()
   161  	parties := make([]string, 0, len(evt.Payments))
   162  	amounts := make(map[string]*num.Int, len(evt.Payments))
   163  	for _, pay := range evt.Payments {
   164  		// amount is integer, but can be negative
   165  		amt, _ := num.IntFromString(pay.Amount, 10)
   166  		parties = append(parties, pay.PartyId)
   167  		amounts[pay.PartyId] = amt
   168  	}
   169  	positions, err := p.store.GetByMarketAndParties(ctx, mkt, parties)
   170  	if err != nil {
   171  		return err
   172  	}
   173  	for _, pos := range positions {
   174  		amt, ok := amounts[pos.PartyID.String()]
   175  		if !ok {
   176  			// should not be possible, but we may want to return an error here
   177  			continue
   178  		}
   179  		pos.ApplyFundingPayment(amt)
   180  		if err := p.updatePosition(ctx, pos); err != nil {
   181  			return fmt.Errorf("failed to apply funding payment: %w", err)
   182  		}
   183  	}
   184  	return nil
   185  }
   186  
   187  func (p *Position) handleDistressedPositions(ctx context.Context, event distressedPositions) error {
   188  	p.mutex.Lock()
   189  	defer p.mutex.Unlock()
   190  	parties := append(event.DistressedParties(), event.SafeParties()...)
   191  	positions, err := p.store.GetByMarketAndParties(ctx, event.MarketID(), parties)
   192  	if err != nil {
   193  		return fmt.Errorf("failed to get positions: %w", err)
   194  	}
   195  	for _, pos := range positions {
   196  		pos.ToggleDistressedStatus()
   197  		if err := p.updatePosition(ctx, pos); err != nil {
   198  			return fmt.Errorf("failed to update position: %w", err)
   199  		}
   200  	}
   201  	return nil
   202  }
   203  
   204  func (p *Position) handleOrdersClosedEvent(ctx context.Context, event ordersClosed) error {
   205  	p.mutex.Lock()
   206  	defer p.mutex.Unlock()
   207  	if sm := p.mktSvc.IsSpotMarket(ctx, event.MarketID()); sm {
   208  		return nil
   209  	}
   210  
   211  	positions, err := p.store.GetByMarketAndParties(ctx, event.MarketID(), event.Parties())
   212  	if err != nil {
   213  		return fmt.Errorf("failed to get positions: %w", err)
   214  	}
   215  	for _, pos := range positions {
   216  		pos.UpdateOrdersClosed()
   217  		if err := p.updatePosition(ctx, pos); err != nil {
   218  			return fmt.Errorf("failed to update position: %w", err)
   219  		}
   220  	}
   221  	return nil
   222  }
   223  
   224  func (p *Position) handleTradeEvent(ctx context.Context, event tradeEvent) error {
   225  	trade := event.Trade()
   226  	p.mutex.Lock()
   227  	defer p.mutex.Unlock()
   228  	if sm := p.mktSvc.IsSpotMarket(ctx, trade.MarketId); sm {
   229  		return nil
   230  	}
   231  	sf, ok := p.mktSvc.GetMarketScalingFactor(ctx, trade.MarketId)
   232  	if !ok {
   233  		return fmt.Errorf("failed to get market scaling factor for market %s", trade.MarketId)
   234  	}
   235  
   236  	if trade.Type == types.TradeTypeNetworkCloseOutBad {
   237  		pos := p.getNetworkPosition(ctx, trade.MarketId)
   238  		seller := trade.Seller == types.NetworkParty
   239  		pos.UpdateWithTrade(trade, seller, sf)
   240  		return p.updatePosition(ctx, pos)
   241  	}
   242  	buyer, seller := p.getPositionsByTrade(ctx, trade)
   243  	buyer.UpdateWithTrade(trade, false, sf)
   244  	// this can't really result in an error...
   245  	_ = p.updatePosition(ctx, buyer)
   246  	seller.UpdateWithTrade(trade, true, sf)
   247  	return p.updatePosition(ctx, seller)
   248  }
   249  
   250  func (p *Position) handlePositionSettlement(ctx context.Context, event positionSettlement) error {
   251  	p.mutex.Lock()
   252  	defer p.mutex.Unlock()
   253  	pos := p.getPosition(ctx, event)
   254  	pos.UpdateWithPositionSettlement(event)
   255  	return p.updatePosition(ctx, pos)
   256  }
   257  
   258  func (p *Position) handleLossSocialization(ctx context.Context, event lossSocialization) error {
   259  	p.mutex.Lock()
   260  	defer p.mutex.Unlock()
   261  	pos := p.getPosition(ctx, event)
   262  	pos.UpdateWithLossSocialization(event)
   263  	return p.updatePosition(ctx, pos)
   264  }
   265  
   266  func (p *Position) handleSettleDistressed(ctx context.Context, event settleDistressed) error {
   267  	p.mutex.Lock()
   268  	defer p.mutex.Unlock()
   269  	pos := p.getPosition(ctx, event)
   270  	pos.UpdateWithSettleDistressed(event)
   271  	return p.updatePosition(ctx, pos)
   272  }
   273  
   274  func (p *Position) handleSettleMarket(ctx context.Context, event settleMarket) error {
   275  	p.mutex.Lock()
   276  	defer p.mutex.Unlock()
   277  	pos, err := p.store.GetByMarket(ctx, event.MarketID())
   278  	if err != nil {
   279  		return errors.Wrap(err, "error getting positions")
   280  	}
   281  	if sm := p.mktSvc.IsSpotMarket(ctx, event.MarketID()); sm {
   282  		return nil
   283  	}
   284  	for i := range pos {
   285  		pos[i].UpdateWithSettleMarket(event)
   286  		err := p.updatePosition(ctx, pos[i])
   287  		if err != nil {
   288  			return errors.Wrap(err, "error updating position")
   289  		}
   290  	}
   291  
   292  	return nil
   293  }
   294  
   295  func (p *Position) getPositionsByTrade(ctx context.Context, trade vega.Trade) (buyer entities.Position, seller entities.Position) {
   296  	mID := entities.MarketID(trade.MarketId)
   297  	bID := entities.PartyID(trade.Buyer)
   298  	sID := entities.PartyID(trade.Seller)
   299  
   300  	var err error
   301  	buyer, err = p.store.GetByMarketAndParty(ctx, mID.String(), bID.String())
   302  	if errors.Is(err, entities.ErrNotFound) {
   303  		buyer = entities.NewEmptyPosition(mID, bID)
   304  	} else if err != nil {
   305  		// this is a really bad thing to happen :)
   306  		panic("unable to query for existing position")
   307  	}
   308  	seller, err = p.store.GetByMarketAndParty(ctx, mID.String(), sID.String())
   309  	if errors.Is(err, entities.ErrNotFound) {
   310  		seller = entities.NewEmptyPosition(mID, sID)
   311  	} else if err != nil {
   312  		// this is a really bad thing to happen :)
   313  		panic("unable to query for existing position")
   314  	}
   315  	return buyer, seller
   316  }
   317  
   318  func (p *Position) getNetworkPosition(ctx context.Context, market string) entities.Position {
   319  	mID := entities.MarketID(market)
   320  	pID := entities.PartyID(types.NetworkParty)
   321  	pos, err := p.store.GetByMarketAndParty(ctx, mID.String(), pID.String())
   322  	if errors.Is(err, entities.ErrNotFound) {
   323  		return entities.NewEmptyPosition(mID, pID)
   324  	}
   325  	if err != nil {
   326  		panic("unable to query existing positions")
   327  	}
   328  	return pos
   329  }
   330  
   331  func (p *Position) getPosition(ctx context.Context, e positionEventBase) entities.Position {
   332  	mID := entities.MarketID(e.MarketID())
   333  	pID := entities.PartyID(e.PartyID())
   334  
   335  	position, err := p.store.GetByMarketAndParty(ctx, mID.String(), pID.String())
   336  	if errors.Is(err, entities.ErrNotFound) {
   337  		return entities.NewEmptyPosition(mID, pID)
   338  	}
   339  
   340  	if err != nil {
   341  		// TODO: Can we do something less drastic here? If we can't get existing positions
   342  		//       things are a bit screwed as we'll start writing down wrong aggregates.
   343  		panic("unable to query for existing position")
   344  	}
   345  
   346  	return position
   347  }
   348  
   349  func (p *Position) updatePosition(ctx context.Context, pos entities.Position) error {
   350  	if pos.PartyID == entities.PartyID(types.NetworkParty) {
   351  		pos.PendingRealisedPnl = num.DecimalZero()
   352  		pos.RealisedPnl = num.DecimalZero()
   353  		pos.PendingUnrealisedPnl = num.DecimalZero()
   354  		pos.UnrealisedPnl = num.DecimalZero()
   355  	}
   356  	pos.VegaTime = p.vegaTime
   357  
   358  	err := p.store.Add(ctx, pos)
   359  	return errors.Wrap(err, "error updating position")
   360  }
   361  
   362  func (p *Position) Name() string {
   363  	return "Position"
   364  }