code.vegaprotocol.io/vega@v0.79.0/datanode/entities/position_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 entities_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/types"
    27  	"code.vegaprotocol.io/vega/datanode/entities"
    28  	"code.vegaprotocol.io/vega/libs/num"
    29  	"code.vegaprotocol.io/vega/protos/vega"
    30  
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func TestMultipleTradesOfSameSize(t *testing.T) {
    36  	ctx := context.Background()
    37  	market := "market-id"
    38  	party := "party1"
    39  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
    40  	ps := events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1000), []events.TradeSettlement{
    41  		tradeStub{
    42  			size:  -1,
    43  			price: num.NewUint(1000),
    44  		},
    45  		tradeStub{
    46  			size:  -1,
    47  			price: num.NewUint(1000),
    48  		},
    49  	}, 1, num.DecimalFromFloat(1))
    50  	position.UpdateWithPositionSettlement(ps)
    51  	pp := position.ToProto()
    52  	// average entry price should be 1k
    53  	assert.Equal(t, ps.Price().String(), pp.AverageEntryPrice)
    54  }
    55  
    56  func TestMultipleTradesAndLossSocializationPartyNoOpenVolume(t *testing.T) {
    57  	ctx := context.Background()
    58  	market := "market-id"
    59  	party := "party1"
    60  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
    61  
    62  	ps := events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1000), []events.TradeSettlement{
    63  		tradeStub{
    64  			size:  2,
    65  			price: num.NewUint(1000),
    66  		},
    67  		tradeStub{
    68  			size:  -2,
    69  			price: num.NewUint(1500),
    70  		},
    71  	}, 1, num.DecimalFromFloat(1))
    72  	position.UpdateWithPositionSettlement(ps)
    73  	pp := position.ToProto()
    74  	assert.Equal(t, "1000", pp.RealisedPnl)
    75  
    76  	// then we process the event for LossSocialization
    77  	lsevt := events.NewLossSocializationEvent(ctx, party, market, num.NewUint(300), true, 1, types.LossTypeUnspecified)
    78  	position.UpdateWithLossSocialization(lsevt)
    79  	pp = position.ToProto()
    80  	assert.Equal(t, "700", pp.RealisedPnl)
    81  	assert.Equal(t, "0", pp.UnrealisedPnl)
    82  }
    83  
    84  func TestDistressedPartyUpdate(t *testing.T) {
    85  	ctx := context.Background()
    86  	market := "market-id"
    87  	party := "party1"
    88  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
    89  	pf := num.DecimalFromFloat(1)
    90  
    91  	ps := events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1000), []events.TradeSettlement{
    92  		tradeStub{
    93  			size:  2,
    94  			price: num.NewUint(1000),
    95  		},
    96  		tradeStub{
    97  			size:  3,
    98  			price: num.NewUint(1200),
    99  		},
   100  	}, 1, pf)
   101  	position.UpdateWithPositionSettlement(ps)
   102  	pp := position.ToProto()
   103  	assert.Equal(t, "0", pp.RealisedPnl)
   104  	assert.Equal(t, "-600", pp.UnrealisedPnl)
   105  
   106  	// then we process the event for LossSocialization
   107  	lsevt := events.NewLossSocializationEvent(ctx, party, market, num.NewUint(300), true, 1, types.LossTypeUnspecified)
   108  	position.UpdateWithLossSocialization(lsevt)
   109  	pp = position.ToProto()
   110  	assert.Equal(t, "-300", pp.RealisedPnl)
   111  	assert.Equal(t, "-600", pp.UnrealisedPnl)
   112  
   113  	// now assume this party is distressed, and we've taken all their funds
   114  	sde := events.NewSettleDistressed(ctx, party, market, num.UintZero(), num.NewUint(100), 1)
   115  	position.UpdateWithSettleDistressed(sde)
   116  	// ensure the position is flagged as distressed.
   117  	assert.Equal(t, entities.PositionStatusClosedOut, position.DistressedStatus)
   118  	pp = position.ToProto()
   119  	assert.Equal(t, "0", pp.UnrealisedPnl)
   120  	assert.Equal(t, "-1000", pp.RealisedPnl)
   121  
   122  	// now submit process a closeout trade event
   123  	position.UpdateWithTrade(vega.Trade{
   124  		Size:       5,
   125  		Price:      "1200",
   126  		AssetPrice: "1200",
   127  		Type:       vega.Trade_TYPE_NETWORK_CLOSE_OUT_BAD,
   128  	}, true, pf)
   129  	// now ensure the position status still is what we expect it to be
   130  	assert.Equal(t, entities.PositionStatusClosedOut, position.DistressedStatus)
   131  
   132  	// next, assume the party has topped up, and traded again.
   133  	position.UpdateWithTrade(vega.Trade{
   134  		Size:       1,
   135  		Price:      "1200",
   136  		AssetPrice: "1200",
   137  		Type:       vega.Trade_TYPE_DEFAULT,
   138  	}, true, pf)
   139  	// now the distressed status ought to be cleared.
   140  	assert.Equal(t, entities.PositionStatusUnspecified, position.DistressedStatus)
   141  }
   142  
   143  func TestMultipleTradesAndLossSocializationPartyWithOpenVolume(t *testing.T) {
   144  	ctx := context.Background()
   145  	market := "market-id"
   146  	party := "party1"
   147  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
   148  
   149  	ps := events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1000), []events.TradeSettlement{
   150  		tradeStub{
   151  			size:  2,
   152  			price: num.NewUint(1000),
   153  		},
   154  		tradeStub{
   155  			size:  3,
   156  			price: num.NewUint(1200),
   157  		},
   158  	}, 1, num.DecimalFromFloat(1))
   159  	position.UpdateWithPositionSettlement(ps)
   160  	pp := position.ToProto()
   161  	assert.Equal(t, "0", pp.RealisedPnl)
   162  	assert.Equal(t, "-600", pp.UnrealisedPnl)
   163  
   164  	// then we process the event for LossSocialization
   165  	lsevt := events.NewLossSocializationEvent(ctx, party, market, num.NewUint(300), true, 1, types.LossTypeUnspecified)
   166  	position.UpdateWithLossSocialization(lsevt)
   167  	pp = position.ToProto()
   168  	assert.Equal(t, "-300", pp.RealisedPnl)
   169  	assert.Equal(t, "-600", pp.UnrealisedPnl)
   170  }
   171  
   172  func TestPnLWithPositionDecimals(t *testing.T) {
   173  	ctx := context.Background()
   174  	market := "market-id"
   175  	party := "party1"
   176  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
   177  	dp := num.DecimalFromFloat(10).Pow(num.DecimalFromInt64(3))
   178  
   179  	// first update with trades
   180  	trade := vega.Trade{
   181  		Id:         "t1",
   182  		MarketId:   market,
   183  		Price:      "1000",
   184  		Size:       2,
   185  		Buyer:      party,
   186  		Seller:     "seller",
   187  		AssetPrice: "1000",
   188  	}
   189  	position.UpdateWithTrade(trade, false, dp)
   190  	trade.Id = "t2"
   191  	trade.Size = 3
   192  	trade.Price = "1200"
   193  	trade.AssetPrice = "1200"
   194  	position.UpdateWithTrade(trade, false, dp)
   195  	pp := position.ToProto()
   196  	assert.Equal(t, "0", pp.RealisedPnl)
   197  	assert.Equal(t, "0", pp.UnrealisedPnl)
   198  	// now MTM settlement event, contains the same trades, mark price is 1k
   199  	ps := events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1000), []events.TradeSettlement{
   200  		tradeStub{
   201  			size:  2,
   202  			price: num.NewUint(1000),
   203  		},
   204  		tradeStub{
   205  			size:  3,
   206  			price: num.NewUint(1200),
   207  		},
   208  	}, 1, dp)
   209  	position.UpdateWithPositionSettlement(ps)
   210  	pp = position.ToProto()
   211  	assert.Equal(t, "0", pp.RealisedPnl)
   212  	assert.Equal(t, "-1", pp.UnrealisedPnl)
   213  	assert.EqualValues(t, 5, pp.OpenVolume)
   214  
   215  	// let's make it look like this party is trading, buyer in this case
   216  	trade = vega.Trade{
   217  		Id:         "t3",
   218  		MarketId:   market,
   219  		Price:      "1150",
   220  		Size:       1,
   221  		Buyer:      party,
   222  		Seller:     "seller",
   223  		AssetPrice: "1150",
   224  	}
   225  	// position.UpdateWithTrade(trade, false, num.DecimalFromFloat(1))
   226  	position.UpdateWithTrade(trade, false, dp)
   227  	pp = position.ToProto()
   228  	assert.Equal(t, "0", pp.RealisedPnl)
   229  	assert.Equal(t, "0", pp.UnrealisedPnl)
   230  	assert.EqualValues(t, 6, pp.OpenVolume)
   231  	// now assume this last trade was the only trade that occurred before MTM
   232  	ps = events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1150), []events.TradeSettlement{
   233  		tradeStub{
   234  			size:  1,
   235  			price: num.NewUint(1150),
   236  		},
   237  	}, 1, dp)
   238  	position.UpdateWithPositionSettlement(ps)
   239  	pp = position.ToProto()
   240  	assert.Equal(t, "0", pp.RealisedPnl)
   241  	assert.Equal(t, "0", pp.UnrealisedPnl)
   242  	assert.EqualValues(t, 6, pp.OpenVolume)
   243  	// now close a position to see some realised PnL
   244  	trade = vega.Trade{
   245  		Id:         "t4",
   246  		MarketId:   market,
   247  		Price:      "1250",
   248  		Size:       1,
   249  		Buyer:      "buyer",
   250  		Seller:     party,
   251  		AssetPrice: "1250",
   252  	}
   253  	position.UpdateWithTrade(trade, true, dp)
   254  	pp = position.ToProto()
   255  	assert.Equal(t, "0", pp.RealisedPnl)
   256  	assert.Equal(t, "1", pp.UnrealisedPnl)
   257  	assert.EqualValues(t, 5, pp.OpenVolume)
   258  	ps = events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1250), []events.TradeSettlement{
   259  		tradeStub{
   260  			size:  -1,
   261  			price: num.NewUint(1250),
   262  		},
   263  	}, 1, dp)
   264  	position.UpdateWithPositionSettlement(ps)
   265  	pp = position.ToProto()
   266  	assert.Equal(t, "0", pp.RealisedPnl)
   267  	assert.Equal(t, "1", pp.UnrealisedPnl)
   268  	assert.EqualValues(t, 5, pp.OpenVolume)
   269  	// now close the position
   270  	trade = vega.Trade{
   271  		Id:         "t5",
   272  		MarketId:   market,
   273  		Price:      "1300",
   274  		Size:       5,
   275  		Buyer:      "buyer",
   276  		Seller:     party,
   277  		AssetPrice: "1300",
   278  	}
   279  	position.UpdateWithTrade(trade, true, dp)
   280  	pp = position.ToProto()
   281  	assert.Equal(t, "1", pp.RealisedPnl)
   282  	assert.Equal(t, "0", pp.UnrealisedPnl)
   283  	assert.EqualValues(t, 0, pp.OpenVolume)
   284  	ps = events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1250), []events.TradeSettlement{
   285  		tradeStub{
   286  			size:  -5,
   287  			price: num.NewUint(1300),
   288  		},
   289  	}, 1, dp)
   290  	position.UpdateWithPositionSettlement(ps)
   291  	pp = position.ToProto()
   292  	assert.Equal(t, "1", pp.RealisedPnl)
   293  	assert.Equal(t, "0", pp.UnrealisedPnl)
   294  	assert.EqualValues(t, 0, pp.OpenVolume)
   295  }
   296  
   297  func TestTradeFees(t *testing.T) {
   298  	// ctx := context.Background()
   299  	market := "market-id"
   300  	party := "party1"
   301  	sParty := "seller"
   302  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
   303  	sPos := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(sParty))
   304  	dp := num.DecimalFromFloat(10).Pow(num.DecimalFromInt64(3))
   305  
   306  	// first update with trades
   307  	trade := vega.Trade{
   308  		Id:         "t1",
   309  		MarketId:   market,
   310  		Price:      "1000",
   311  		Size:       2,
   312  		Buyer:      party,
   313  		Seller:     sParty,
   314  		AssetPrice: "1000",
   315  		Aggressor:  types.SideSell,
   316  		BuyerFee: &vega.Fee{
   317  			MakerFee:           "10",
   318  			InfrastructureFee:  "1",
   319  			LiquidityFee:       "2",
   320  			TreasuryFee:        "1",
   321  			BuyBackFee:         "0",
   322  			HighVolumeMakerFee: "0",
   323  		},
   324  		SellerFee: &vega.Fee{
   325  			MakerFee:           "20",
   326  			InfrastructureFee:  "2",
   327  			LiquidityFee:       "3",
   328  			TreasuryFee:        "2",
   329  			BuyBackFee:         "1",
   330  			HighVolumeMakerFee: "1",
   331  		},
   332  	}
   333  	bFeesPaid := num.NewDecimalFromFloat(4)
   334  	sFeesPaid := num.NewDecimalFromFloat(9)
   335  	bMaker := num.NewDecimalFromFloat(10)
   336  	sMaker := num.NewDecimalFromFloat(20)
   337  	position.UpdateWithTrade(trade, false, dp)
   338  	sPos.UpdateWithTrade(trade, true, dp)
   339  	require.True(t, position.FeesPaid.Equal(bFeesPaid))
   340  	require.True(t, sPos.FeesPaid.Equal(sFeesPaid))
   341  	// maker fees swap
   342  	require.True(t, sPos.TakerFeesPaid.Equal(sMaker))
   343  	require.True(t, position.MakerFeesReceived.Equal(sMaker))
   344  	// we have an aggressor, so only one side received maker fees
   345  	require.True(t, sPos.MakerFeesReceived.Equal(num.DecimalZero()))
   346  	require.True(t, position.TakerFeesPaid.Equal(bMaker))
   347  	// now the same trade but with no aggressor
   348  	trade.Aggressor = types.SideUnspecified
   349  	position = entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
   350  	sPos = entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(sParty))
   351  	position.UpdateWithTrade(trade, false, dp)
   352  	sPos.UpdateWithTrade(trade, true, dp)
   353  	require.True(t, position.FeesPaid.Equal(bFeesPaid))
   354  	require.True(t, sPos.FeesPaid.Equal(sFeesPaid))
   355  	// maker fees swap
   356  	require.True(t, sPos.TakerFeesPaid.Equal(sMaker))
   357  	require.True(t, position.MakerFeesReceived.Equal(sMaker))
   358  	// we have an aggressor, so only one side received maker fees
   359  	require.True(t, sPos.MakerFeesReceived.Equal(bMaker))
   360  	require.True(t, position.TakerFeesPaid.Equal(bMaker))
   361  }
   362  
   363  func TestPnLWithTradeDecimals(t *testing.T) {
   364  	ctx := context.Background()
   365  	market := "market-id"
   366  	party := "party1"
   367  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
   368  	dp := num.DecimalFromFloat(3)
   369  
   370  	ps := events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1000), []events.TradeSettlement{
   371  		tradeStub{
   372  			size:  2,
   373  			price: num.NewUint(1000),
   374  		},
   375  		tradeStub{
   376  			size:  3,
   377  			price: num.NewUint(1200),
   378  		},
   379  	}, 1, dp)
   380  	position.UpdateWithPositionSettlement(ps)
   381  	pp := position.ToProto()
   382  	assert.Equal(t, "0", pp.RealisedPnl)
   383  	assert.Equal(t, "-200", pp.UnrealisedPnl)
   384  
   385  	// then we process the event for LossSocialization
   386  	lsevt := events.NewLossSocializationEvent(ctx, party, market, num.NewUint(300), true, 1, types.LossTypeUnspecified)
   387  	position.UpdateWithLossSocialization(lsevt)
   388  	pp = position.ToProto()
   389  	assert.Equal(t, "-300", pp.RealisedPnl)
   390  	assert.Equal(t, "-200", pp.UnrealisedPnl)
   391  	// let's make it look like this party is trading, buyer in this case
   392  	trade := vega.Trade{
   393  		Id:         "t1",
   394  		MarketId:   market,
   395  		Price:      "1150",
   396  		Size:       1,
   397  		Buyer:      party,
   398  		Seller:     "seller",
   399  		AssetPrice: "1150",
   400  	}
   401  	// position.UpdateWithTrade(trade, false, num.DecimalFromFloat(1))
   402  	position.UpdateWithTrade(trade, false, dp)
   403  	pp = position.ToProto()
   404  	assert.Equal(t, "-300", pp.RealisedPnl)
   405  	assert.Equal(t, "50", pp.UnrealisedPnl)
   406  }
   407  
   408  func TestUpdateWithTradesAndFundingPayment(t *testing.T) {
   409  	ctx := context.Background()
   410  	market := "market-id"
   411  	party := "party1"
   412  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
   413  	dp := num.DecimalFromFloat(3)
   414  	trades := []tradeStub{
   415  		{
   416  			size:  2,
   417  			price: num.NewUint(1200),
   418  		},
   419  		{
   420  			size:  3,
   421  			price: num.NewUint(1000),
   422  		},
   423  	}
   424  	// this is the order in which the events will be sent/received
   425  	position.UpdateWithTrade(trades[0].ToVega(dp), false, dp)
   426  	pp := position.ToProto()
   427  	assert.Equal(t, "0", pp.RealisedPnl)
   428  	assert.Equal(t, "0", pp.UnrealisedPnl)
   429  	position.ApplyFundingPayment(num.NewInt(100))
   430  	pp = position.ToProto()
   431  	assert.Equal(t, "100", position.FundingPaymentAmount.String())
   432  	assert.Equal(t, "0", pp.UnrealisedPnl, pp.AverageEntryPrice)
   433  	position.UpdateWithTrade(trades[1].ToVega(dp), false, dp)
   434  	pp = position.ToProto()
   435  	assert.Equal(t, "100", position.FundingPaymentAmount.String())
   436  	assert.Equal(t, "-133", pp.UnrealisedPnl, pp.AverageEntryPrice)
   437  	ps := events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1000), []events.TradeSettlement{trades[0], trades[1]}, 1, dp)
   438  	position.UpdateWithPositionSettlement(ps)
   439  	psp := position.ToProto()
   440  	assert.Equal(t, "100", position.FundingPaymentAmount.String())
   441  	assert.Equal(t, "-133", psp.UnrealisedPnl)
   442  	position.ApplyFundingPayment(num.NewInt(-50))
   443  	pp = position.ToProto()
   444  	assert.Equal(t, "50", position.FundingPaymentAmount.String())
   445  	assert.Equal(t, "-133", pp.UnrealisedPnl, pp.AverageEntryPrice)
   446  }
   447  
   448  type tradeStub struct {
   449  	size        int64
   450  	price       *num.Uint
   451  	marketPrice *num.Uint
   452  }
   453  
   454  func (t tradeStub) Size() int64 {
   455  	return t.size
   456  }
   457  
   458  func (t tradeStub) Price() *num.Uint {
   459  	return t.price.Clone()
   460  }
   461  
   462  func (t tradeStub) MarketPrice() *num.Uint {
   463  	if t.marketPrice != nil {
   464  		return t.marketPrice.Clone()
   465  	}
   466  	return t.price.Clone()
   467  }
   468  
   469  func (t tradeStub) ToVega(dp num.Decimal) vega.Trade {
   470  	// dp = num.DecimalFromFloat(10).Pow(dp)
   471  	// size, _ := num.DecimalFromInt64(t.size).Abs().Mul(dp).Float64()
   472  	size := uint64(t.size)
   473  	if t.size < 0 {
   474  		size = uint64(-t.size)
   475  	}
   476  	return vega.Trade{
   477  		Size:       size,
   478  		Price:      t.price.String(),
   479  		AssetPrice: t.price.String(),
   480  	}
   481  }
   482  
   483  func TestCalculateOpenClosedVolume(t *testing.T) {
   484  	open := int64(0)
   485  	closed := int64(0)
   486  	// no pending volume, new buy trade of 100, expect to open 100 close 0
   487  	open, closed = entities.CalculateOpenClosedVolume(0, 100)
   488  	require.Equal(t, int64(100), open)
   489  	require.Equal(t, int64(0), closed)
   490  
   491  	// no pending volume, new sell trade of 100, expect to open -100 close 0
   492  	open, closed = entities.CalculateOpenClosedVolume(0, -100)
   493  	require.Equal(t, int64(-100), open)
   494  	require.Equal(t, int64(0), closed)
   495  
   496  	// we have a pending open volume of 100 and we get a new buy trade of 50, expect to return opened 50, close 0
   497  	open, closed = entities.CalculateOpenClosedVolume(100, 50)
   498  	require.Equal(t, int64(50), open)
   499  	require.Equal(t, int64(0), closed)
   500  
   501  	// we have a pending open volume of -100 and we get a new sell trade of 50, expect to return opened -50, close 0
   502  	open, closed = entities.CalculateOpenClosedVolume(-100, -50)
   503  	require.Equal(t, int64(-50), open)
   504  	require.Equal(t, int64(0), closed)
   505  
   506  	// we have a pending open volume of 100 and we get a new sell trade of 50, expect to return opened 0, close 50
   507  	open, closed = entities.CalculateOpenClosedVolume(100, -50)
   508  	require.Equal(t, int64(0), open)
   509  	require.Equal(t, int64(50), closed)
   510  
   511  	// we have a pending open volume of -100 and we get a new buy trade of 50, expect to return opened 0, close -50
   512  	open, closed = entities.CalculateOpenClosedVolume(-100, 50)
   513  	require.Equal(t, int64(0), open)
   514  	require.Equal(t, int64(-50), closed)
   515  
   516  	// we have a pending open volume of 100 and we get a new sell trade of 150, expect to return opened -50, close 100
   517  	open, closed = entities.CalculateOpenClosedVolume(100, -150)
   518  	require.Equal(t, int64(-50), open)
   519  	require.Equal(t, int64(100), closed)
   520  
   521  	// we have a pending open volume of -100 and we get a new buy trade of 150, expect to return opened 50, close -100
   522  	open, closed = entities.CalculateOpenClosedVolume(-100, 150)
   523  	require.Equal(t, int64(50), open)
   524  	require.Equal(t, int64(-100), closed)
   525  }
   526  
   527  func TestWithDifferingMarketAssetPrecision(t *testing.T) {
   528  	ctx := context.Background()
   529  	market := "market-id"
   530  	party := "party1"
   531  	position := entities.NewEmptyPosition(entities.MarketID(market), entities.PartyID(party))
   532  	ps := events.NewSettlePositionEvent(ctx, party, market, num.NewUint(1000), []events.TradeSettlement{
   533  		tradeStub{
   534  			size:        -5,
   535  			price:       num.NewUint(10000000),
   536  			marketPrice: num.NewUint(1000),
   537  		},
   538  		tradeStub{
   539  			size:        -5,
   540  			price:       num.NewUint(10000000),
   541  			marketPrice: num.NewUint(1000),
   542  		},
   543  	}, 1, num.DecimalFromFloat(1))
   544  	position.UpdateWithPositionSettlement(ps)
   545  	pp := position.ToProto()
   546  
   547  	// average entry price should be 1k in market precision
   548  	assert.Equal(t, ps.Price().String(), pp.AverageEntryPrice)
   549  	assert.Equal(t, "1000", position.AverageEntryMarketPrice.String())
   550  	assert.Equal(t, "10000000", position.AverageEntryPrice.String())
   551  
   552  	// now update with a trade
   553  	trade := vega.Trade{
   554  		Price:      "2000",
   555  		AssetPrice: "20000000",
   556  		Size:       10,
   557  	}
   558  	position.UpdateWithTrade(trade, true, num.DecimalOne())
   559  	assert.Equal(t, "1500", position.PendingAverageEntryMarketPrice.String())
   560  	assert.Equal(t, "15000000", position.PendingAverageEntryPrice.String())
   561  	assert.Equal(t, int64(-20), position.PendingOpenVolume)
   562  
   563  	trade = vega.Trade{
   564  		Price:      "1000",
   565  		AssetPrice: "10000000",
   566  		Size:       5,
   567  	}
   568  	position.UpdateWithTrade(trade, false, num.DecimalOne())
   569  	assert.Equal(t, "1500", position.PendingAverageEntryMarketPrice.String())
   570  	assert.Equal(t, "15000000", position.PendingAverageEntryPrice.String())
   571  	assert.Equal(t, int64(-15), position.PendingOpenVolume)
   572  	assert.Equal(t, "25000000", position.PendingRealisedPnl.String())
   573  	assert.Equal(t, "75000000", position.PendingUnrealisedPnl.String())
   574  }