code.vegaprotocol.io/vega@v0.79.0/core/execution/future/bond_slashing_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 future_test
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	vegacontext "code.vegaprotocol.io/vega/libs/context"
    26  	vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  
    29  	"github.com/golang/mock/gomock"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  //nolint:unparam
    35  func setMarkPrice(t *testing.T, mkt *testMarket, duration *types.AuctionDuration, now time.Time, price uint64) {
    36  	t.Helper()
    37  	// all parties
    38  	parties := []string{"oo-p1", "oo-p4", "oo-p2", "oo-p3"}
    39  	// create accounts for the parties
    40  	for _, p := range parties {
    41  		addAccount(t, mkt, p)
    42  	}
    43  	delta := num.NewUint(10)
    44  	mPrice := num.NewUint(price)
    45  	orders := []*types.Order{
    46  		{
    47  			MarketID:    mkt.market.GetID(),
    48  			Party:       parties[0],
    49  			Side:        types.SideBuy,
    50  			Price:       num.UintZero().Sub(mPrice, delta),
    51  			Size:        1,
    52  			Remaining:   1,
    53  			TimeInForce: types.OrderTimeInForceGTC,
    54  			Type:        types.OrderTypeLimit,
    55  			CreatedAt:   now.UnixNano(),
    56  			Reference:   "oo-no-trade-buy",
    57  		},
    58  		{
    59  			MarketID:    mkt.market.GetID(),
    60  			Party:       parties[2],
    61  			Side:        types.SideBuy,
    62  			Price:       mPrice,
    63  			Size:        1,
    64  			Remaining:   1,
    65  			TimeInForce: types.OrderTimeInForceGFA,
    66  			Type:        types.OrderTypeLimit,
    67  			CreatedAt:   now.UnixNano(),
    68  			Reference:   "oo-trade-buy",
    69  		},
    70  		{
    71  			MarketID:    mkt.market.GetID(),
    72  			Party:       parties[3],
    73  			Side:        types.SideSell,
    74  			Price:       mPrice,
    75  			Size:        1,
    76  			Remaining:   1,
    77  			TimeInForce: types.OrderTimeInForceGFA,
    78  			Type:        types.OrderTypeLimit,
    79  			CreatedAt:   now.UnixNano(),
    80  			Reference:   "oo-trade-sell",
    81  		},
    82  		{
    83  			MarketID:    mkt.market.GetID(),
    84  			Party:       parties[1],
    85  			Side:        types.SideSell,
    86  			Price:       num.Sum(mPrice, delta),
    87  			Size:        1,
    88  			Remaining:   1,
    89  			TimeInForce: types.OrderTimeInForceGTC,
    90  			Type:        types.OrderTypeLimit,
    91  			CreatedAt:   now.UnixNano(),
    92  			Reference:   "oo-no-trade-sell",
    93  		},
    94  	}
    95  	for _, o := range orders {
    96  		_, err := mkt.market.SubmitOrder(context.Background(), o)
    97  		require.NoError(t, err)
    98  	}
    99  	// now fast-forward the market so the auction ends
   100  	now = now.Add(time.Duration(duration.Duration+1) * time.Second)
   101  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
   102  	mkt.now = now
   103  	mkt.market.OnTick(ctx, now)
   104  
   105  	// opening auction ended, mark-price set
   106  	mktData := mkt.market.GetMarketData()
   107  	require.NotNil(t, mktData)
   108  	require.Equal(t, types.MarketTradingModeContinuous, mktData.MarketTradingMode)
   109  }
   110  
   111  const lpprov = "lpprov-party"
   112  
   113  func addSimpleLP(t *testing.T, mkt *testMarket, amt uint64) {
   114  	t.Helper()
   115  
   116  	lps := &types.LiquidityProvisionSubmission{
   117  		Fee:              num.DecimalFromFloat(0.01),
   118  		MarketID:         mkt.market.GetID(),
   119  		CommitmentAmount: num.NewUint(amt),
   120  	}
   121  	require.NoError(t, mkt.market.SubmitLiquidityProvision(
   122  		context.Background(), lps, lpprov, vgcrypto.RandomHash(),
   123  	))
   124  }
   125  
   126  func TestAcceptLiquidityProvisionWithSufficientFunds(t *testing.T) {
   127  	mainParty := "mainParty"
   128  	now := time.Unix(10, 0)
   129  	openingAuction := &types.AuctionDuration{
   130  		Duration: 1,
   131  	}
   132  	tm := getTestMarket(t, now, nil, openingAuction)
   133  	initialMarkPrice := uint64(99)
   134  	ctx := context.Background()
   135  
   136  	asset := tm.asset
   137  
   138  	addAccountWithAmount(tm, lpprov, 50000000)
   139  	addSimpleLP(t, tm, 5000000)
   140  	// end opening auction
   141  	setMarkPrice(t, tm, openingAuction, now, initialMarkPrice)
   142  
   143  	mainPartyInitialDeposit := uint64(794) // 794 is the amount required to cover the initial margin on open orderss
   144  	addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit)
   145  
   146  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   147  
   148  	orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2)
   149  
   150  	confirmationSell, err := tm.market.SubmitOrder(ctx, orderSell1)
   151  	require.NotNil(t, confirmationSell)
   152  	require.NoError(t, err)
   153  
   154  	orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2)
   155  
   156  	confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1)
   157  	assert.NotNil(t, confirmationBuy)
   158  	assert.NoError(t, err)
   159  
   160  	require.Equal(t, 0, len(confirmationBuy.Trades))
   161  
   162  	lp1 := &types.LiquidityProvisionSubmission{
   163  		MarketID:         tm.market.GetID(),
   164  		CommitmentAmount: num.NewUint(200),
   165  		Fee:              num.DecimalFromFloat(0.05),
   166  	}
   167  
   168  	err = tm.market.SubmitLiquidityProvision(ctx, lp1, mainParty, vgcrypto.RandomHash())
   169  	require.NoError(t, err)
   170  
   171  	bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   172  	require.NoError(t, err)
   173  	require.NotNil(t, bondAcc)
   174  	require.Equal(t, lp1.CommitmentAmount, bondAcc.Balance)
   175  }
   176  
   177  func TestRejectLiquidityProvisionWithInsufficientFundsForInitialMargin(t *testing.T) {
   178  	mainParty := "mainParty"
   179  	now := time.Unix(10, 0)
   180  	openingAuction := &types.AuctionDuration{
   181  		Duration: 1,
   182  	}
   183  	tm := getTestMarket(t, now, nil, openingAuction)
   184  	initialMarkPrice := uint64(99)
   185  	ctx := context.Background()
   186  
   187  	asset := tm.asset
   188  
   189  	addAccountWithAmount(tm, lpprov, 5000000)
   190  	addSimpleLP(t, tm, 5000000)
   191  	// end opening auction
   192  	setMarkPrice(t, tm, openingAuction, now, initialMarkPrice)
   193  
   194  	mainPartyInitialDeposit := uint64(347) // 348 is the minimum required amount to meet the commitment amount and maintenance margin on resulting orders
   195  	transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit)
   196  	mainPartyGenAccID := transferResp.Entries[0].ToAccount
   197  
   198  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   199  
   200  	orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2)
   201  
   202  	confirmationSell, err := tm.market.SubmitOrder(ctx, orderSell1)
   203  	require.NotNil(t, confirmationSell)
   204  	require.NoError(t, err)
   205  
   206  	orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2)
   207  
   208  	confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1)
   209  	assert.NotNil(t, confirmationBuy)
   210  	assert.NoError(t, err)
   211  
   212  	require.Equal(t, 0, len(confirmationBuy.Trades))
   213  
   214  	lp1 := &types.LiquidityProvisionSubmission{
   215  		MarketID:         tm.market.GetID(),
   216  		CommitmentAmount: num.NewUint(200),
   217  		Fee:              num.DecimalFromFloat(0.05),
   218  	}
   219  
   220  	// Assure that at least the commitment amount can be covered with the initial deposit, otherwise it's a trivial failure (LP can't even afford the bond)
   221  	require.Greater(t, mainPartyInitialDeposit, lp1.CommitmentAmount.Uint64())
   222  
   223  	numLpsPriorToSubmission := tm.market.GetLPSCount()
   224  
   225  	err = tm.market.SubmitLiquidityProvision(ctx, lp1, mainParty, vgcrypto.RandomHash())
   226  	require.Error(t, err)
   227  
   228  	assert.Equal(t, numLpsPriorToSubmission, tm.market.GetLPSCount())
   229  
   230  	bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   231  	require.NoError(t, err)
   232  	require.NotNil(t, bondAcc)
   233  	require.Equal(t, num.UintZero(), bondAcc.Balance)
   234  
   235  	insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset)
   236  	insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   237  	require.NoError(t, err)
   238  	require.NotNil(t, insurancePool)
   239  	require.Equal(t, num.UintZero(), insurancePool.Balance)
   240  
   241  	// TODO: JEREMY: funds are staying in margin ACCOUNT, let's
   242  	// fix that latert.
   243  	marginAcc, err := tm.collateralEngine.GetPartyMarginAccount(tm.mktCfg.ID, mainParty, asset)
   244  	require.NoError(t, err)
   245  	require.NotNil(t, marginAcc)
   246  
   247  	exp := num.UintZero().Sub(num.NewUint(mainPartyInitialDeposit), marginAcc.Balance)
   248  	genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID.ID())
   249  	require.NoError(t, err)
   250  	require.NotNil(t, genAcc)
   251  	require.Equal(t, genAcc.Balance, exp)
   252  }
   253  
   254  func TestCloseoutLPWhenCannotCoverMargin(t *testing.T) {
   255  	t.Skip("to be fixed @witold")
   256  	mainParty := "mainParty"
   257  	auxParty1 := "auxParty1"
   258  	now := time.Unix(10, 0)
   259  	ctx := context.Background()
   260  	openingAuction := &types.AuctionDuration{
   261  		Duration: 1,
   262  	}
   263  	tm := getTestMarket(t, now, nil, openingAuction)
   264  	initialMarkPrice := uint64(99)
   265  
   266  	setMarkPrice(t, tm, openingAuction, now, initialMarkPrice)
   267  
   268  	asset := tm.asset
   269  
   270  	var mainPartyInitialDeposit uint64 = 527 // 794 is the minimum amount to cover additional orders after orderBuyAux1 fills
   271  	transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit)
   272  	mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID()
   273  	addAccount(t, tm, auxParty1)
   274  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   275  
   276  	orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 10, initialMarkPrice+2)
   277  	confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1)
   278  	require.NotNil(t, confirmationSell1)
   279  	require.NoError(t, err)
   280  
   281  	orderSell2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-2", types.SideSell, mainParty, 1, initialMarkPrice+5)
   282  	confirmationSell2, err := tm.market.SubmitOrder(ctx, orderSell2)
   283  	require.NotNil(t, confirmationSell2)
   284  	require.NoError(t, err)
   285  
   286  	orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2)
   287  
   288  	confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1)
   289  	assert.NotNil(t, confirmationBuy)
   290  	assert.NoError(t, err)
   291  
   292  	require.Equal(t, 0, len(confirmationBuy.Trades))
   293  
   294  	lp := &types.LiquidityProvisionSubmission{
   295  		MarketID:         tm.market.GetID(),
   296  		CommitmentAmount: num.NewUint(200),
   297  		Fee:              num.DecimalFromFloat(0.05),
   298  	}
   299  
   300  	err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash())
   301  	require.NoError(t, err)
   302  
   303  	require.Equal(t, 1, tm.market.GetLPSCount())
   304  
   305  	bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   306  	require.NoError(t, err)
   307  	require.NotNil(t, bondAcc)
   308  	require.Equal(t, lp.CommitmentAmount, bondAcc.Balance)
   309  
   310  	genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   311  	require.NoError(t, err)
   312  	require.NotNil(t, genAcc)
   313  	require.Equal(t, genAcc.Balance, num.UintZero())
   314  
   315  	insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset)
   316  	insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   317  	require.NoError(t, err)
   318  	insurancePoolBalanceBeforeLPCloseout := insurancePool.Balance.Clone()
   319  	require.Equal(t, num.UintZero(), insurancePoolBalanceBeforeLPCloseout)
   320  
   321  	orderBuyAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideBuy, auxParty1, orderSell1.Size+1, orderSell1.Price.Uint64())
   322  	confirmationBuyAux1, err := tm.market.SubmitOrder(ctx, orderBuyAux1)
   323  	require.NotNil(t, confirmationBuyAux1)
   324  	require.NoError(t, err)
   325  	require.Equal(t, 2, len(confirmationBuyAux1.Trades))
   326  
   327  	assert.Equal(t, 0, tm.market.GetLPSCount())
   328  
   329  	genAcc, err = tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   330  	require.NoError(t, err)
   331  	require.NotNil(t, genAcc)
   332  	require.Equal(t, num.UintZero(), genAcc.Balance)
   333  
   334  	bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   335  	require.NoError(t, err)
   336  	require.NotNil(t, bondAcc)
   337  	require.Equal(t, num.UintZero(), bondAcc.Balance)
   338  
   339  	insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   340  	require.NoError(t, err)
   341  	require.NotNil(t, insurancePool)
   342  	insurancePoolBalanceAfterLPCloseout := insurancePool.Balance.Clone()
   343  	require.Greater(t, insurancePoolBalanceAfterLPCloseout, insurancePoolBalanceBeforeLPCloseout)
   344  }
   345  
   346  func TestBondAccountNotUsedForMarginShortageWhenEnoughMoneyInGeneral(t *testing.T) {
   347  	t.Skip("to be fixed @witold")
   348  	mainParty := "mainParty"
   349  	auxParty1 := "auxParty1"
   350  	now := time.Unix(10, 0)
   351  	initialMarkPrice := uint64(99)
   352  	ctx := context.Background()
   353  	openingAuction := &types.AuctionDuration{
   354  		Duration: 1,
   355  	}
   356  	tm := getTestMarket(t, now, nil, openingAuction)
   357  
   358  	setMarkPrice(t, tm, openingAuction, now, initialMarkPrice)
   359  
   360  	asset := tm.asset
   361  
   362  	var mainPartyInitialDeposit uint64 = 1020 // 1020 is the minimum required amount to cover margin without dipping into the bond account
   363  	transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit)
   364  	mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID()
   365  	addAccount(t, tm, auxParty1)
   366  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   367  
   368  	orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2)
   369  	confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1)
   370  	require.NotNil(t, confirmationSell1)
   371  	require.NoError(t, err)
   372  
   373  	orderSell2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-2", types.SideSell, mainParty, 1, initialMarkPrice+5)
   374  	confirmationSell2, err := tm.market.SubmitOrder(ctx, orderSell2)
   375  	require.NotNil(t, confirmationSell2)
   376  	require.NoError(t, err)
   377  
   378  	orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2)
   379  	confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1)
   380  	assert.NotNil(t, confirmationBuy)
   381  	assert.NoError(t, err)
   382  	require.Equal(t, 0, len(confirmationBuy.Trades))
   383  
   384  	lp := &types.LiquidityProvisionSubmission{
   385  		MarketID:         tm.market.GetID(),
   386  		CommitmentAmount: num.NewUint(200),
   387  		Fee:              num.DecimalFromFloat(0.05),
   388  	}
   389  
   390  	err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash())
   391  	require.NoError(t, err)
   392  
   393  	bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   394  	require.NoError(t, err)
   395  	require.NotNil(t, bondAcc)
   396  	require.Equal(t, lp.CommitmentAmount, bondAcc.Balance)
   397  
   398  	insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset)
   399  	insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   400  	require.NoError(t, err)
   401  	insurancePoolBalanceBeforeMarketMove := insurancePool.Balance.Clone()
   402  	require.Equal(t, num.UintZero(), insurancePoolBalanceBeforeMarketMove)
   403  
   404  	orderBuyAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideBuy, auxParty1, orderSell1.Size+1, orderSell1.Price.Uint64())
   405  	confirmationBuyAux1, err := tm.market.SubmitOrder(ctx, orderBuyAux1)
   406  	require.NotNil(t, confirmationBuyAux1)
   407  	require.NoError(t, err)
   408  	require.Equal(t, 2, len(confirmationBuyAux1.Trades))
   409  
   410  	genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   411  	require.NoError(t, err)
   412  	require.NotNil(t, genAcc)
   413  	require.Equal(t, num.UintZero(), genAcc.Balance)
   414  
   415  	bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   416  	require.NoError(t, err)
   417  	require.NotNil(t, bondAcc)
   418  	require.Equal(t, lp.CommitmentAmount, bondAcc.Balance)
   419  
   420  	insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   421  	insurancePoolBalanceAfterMarketMove := insurancePool.Balance.Clone()
   422  
   423  	require.NoError(t, err)
   424  	require.NotNil(t, insurancePool)
   425  	require.Equal(t, num.UintZero(), insurancePoolBalanceAfterMarketMove)
   426  }
   427  
   428  func TestBondAccountUsedForMarginShortage_PenaltyPaidFromBondAccount(t *testing.T) {
   429  	t.Skip("to be fixed @witold")
   430  	mainParty := "mainParty"
   431  	auxParty1 := "auxParty1"
   432  	now := time.Unix(10, 0)
   433  	initialMarkPrice := uint64(99)
   434  	ctx := context.Background()
   435  	openingAuction := &types.AuctionDuration{
   436  		Duration: 1,
   437  	}
   438  	tm := getTestMarket(t, now, nil, openingAuction)
   439  
   440  	setMarkPrice(t, tm, openingAuction, now, initialMarkPrice)
   441  
   442  	asset := tm.asset
   443  
   444  	bondPenaltyParameter := 0.1
   445  	tm.market.OnMarketLiquidityV2BondPenaltyFactorUpdate(num.DecimalFromFloat(bondPenaltyParameter))
   446  	// No fees
   447  	tm.market.OnFeeFactorsInfrastructureFeeUpdate(ctx, num.DecimalFromFloat(0))
   448  	tm.market.OnFeeFactorsMakerFeeUpdate(ctx, num.DecimalFromFloat(0))
   449  
   450  	var mainPartyInitialDeposit uint64 = 1000 // 1020 is the minimum required amount to cover margin without dipping into the bond account
   451  	transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit)
   452  	mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID()
   453  	mainPartyMarginAccID := fmt.Sprintf("%smainParty%s3", tm.market.GetID(), tm.asset)
   454  	addAccount(t, tm, auxParty1)
   455  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   456  
   457  	orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2)
   458  	confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1)
   459  	require.NotNil(t, confirmationSell1)
   460  	require.NoError(t, err)
   461  
   462  	orderSell2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-2", types.SideSell, mainParty, 1, initialMarkPrice+5)
   463  	confirmationSell2, err := tm.market.SubmitOrder(ctx, orderSell2)
   464  	require.NotNil(t, confirmationSell2)
   465  	require.NoError(t, err)
   466  
   467  	orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2)
   468  
   469  	confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1)
   470  	assert.NotNil(t, confirmationBuy)
   471  	assert.NoError(t, err)
   472  
   473  	require.Equal(t, 0, len(confirmationBuy.Trades))
   474  
   475  	lp := &types.LiquidityProvisionSubmission{
   476  		MarketID:         tm.market.GetID(),
   477  		CommitmentAmount: num.NewUint(200),
   478  		Fee:              num.DecimalFromFloat(0.0),
   479  	}
   480  
   481  	err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash())
   482  	require.NoError(t, err)
   483  
   484  	genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   485  	require.NoError(t, err)
   486  	require.NotNil(t, genAcc)
   487  	require.False(t, genAcc.Balance.IsZero())
   488  	genAccBalanceBeforeMarketMove := genAcc.Balance.Clone()
   489  
   490  	marginAcc, err := tm.collateralEngine.GetAccountByID(mainPartyMarginAccID)
   491  	require.NoError(t, err)
   492  	require.NotNil(t, marginAcc)
   493  	marginAccBalanceBeforeMarketMove := marginAcc.Balance.Clone()
   494  
   495  	bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   496  	require.NoError(t, err)
   497  	require.NotNil(t, bondAcc)
   498  	bondAccBalanceBeforeMarketMove := bondAcc.Balance.Clone()
   499  	require.Equal(t, lp.CommitmentAmount, bondAccBalanceBeforeMarketMove)
   500  
   501  	insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset)
   502  	insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   503  	require.NoError(t, err)
   504  	insurancePoolBalanceBeforeMarketMove := insurancePool.Balance.Clone()
   505  	require.Equal(t, num.UintZero(), insurancePoolBalanceBeforeMarketMove)
   506  
   507  	orderBuyAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideBuy, auxParty1, orderSell1.Size+1, orderSell1.Price.Uint64())
   508  	confirmationBuyAux1, err := tm.market.SubmitOrder(ctx, orderBuyAux1)
   509  	require.NotNil(t, confirmationBuyAux1)
   510  	require.NoError(t, err)
   511  	require.Equal(t, 2, len(confirmationBuyAux1.Trades))
   512  
   513  	genAcc, err = tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   514  	require.NoError(t, err)
   515  	require.NotNil(t, genAcc)
   516  	genAccBalanceAfterMarketMove := genAcc.Balance.Clone()
   517  	require.True(t, genAcc.Balance.IsZero())
   518  
   519  	marginAcc, err = tm.collateralEngine.GetAccountByID(mainPartyMarginAccID)
   520  	require.NoError(t, err)
   521  	require.NotNil(t, marginAcc)
   522  	marginAccBalanceAfterMarketMove := marginAcc.Balance.Clone()
   523  
   524  	bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   525  	require.NoError(t, err)
   526  	require.NotNil(t, bondAcc)
   527  	bondAccBalanceAfterMarketMove := bondAcc.Balance.Clone()
   528  	require.Less(t, bondAccBalanceAfterMarketMove, bondAccBalanceBeforeMarketMove)
   529  	require.False(t, bondAccBalanceAfterMarketMove.IsZero())
   530  
   531  	insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   532  	insurancePoolBalanceAfterMarketMove := insurancePool.Balance.Clone()
   533  
   534  	require.NoError(t, err)
   535  	require.NotNil(t, insurancePool)
   536  	require.True(t, insurancePoolBalanceAfterMarketMove.GT(insurancePoolBalanceBeforeMarketMove))
   537  
   538  	genAccBalanceChange, gNeg := num.UintZero().Delta(genAccBalanceAfterMarketMove, genAccBalanceBeforeMarketMove)
   539  	marginAccBalanceChange, mNeg := num.UintZero().Delta(marginAccBalanceAfterMarketMove, marginAccBalanceBeforeMarketMove)
   540  	insurancePoolBalanceChange, iNeg := num.UintZero().Delta(insurancePoolBalanceAfterMarketMove, insurancePoolBalanceBeforeMarketMove)
   541  	// assume all positive
   542  	expBB := num.Sum(bondAccBalanceBeforeMarketMove, genAccBalanceChange, marginAccBalanceChange, insurancePoolBalanceChange)
   543  	if gNeg {
   544  		// we've added, so subtract twice
   545  		expBB.Sub(expBB, num.Sum(genAccBalanceChange, genAccBalanceChange))
   546  	}
   547  	if mNeg {
   548  		expBB.Sub(expBB, num.Sum(marginAccBalanceChange, marginAccBalanceChange))
   549  	}
   550  	if iNeg {
   551  		expBB.Sub(expBB, num.Sum(insurancePoolBalanceChange, insurancePoolBalanceChange))
   552  	}
   553  
   554  	require.Equal(t, expBB, bondAccBalanceAfterMarketMove)
   555  }
   556  
   557  func TestBondAccountUsedForMarginShortagePenaltyPaidFromMarginAccount_NoCloseout(t *testing.T) {
   558  	t.Skip("to be fixed @witold")
   559  	mainParty := "mainParty"
   560  	auxParty1 := "auxParty1"
   561  	now := time.Unix(10, 0)
   562  	initialMarkPrice := uint64(99)
   563  	ctx := context.Background()
   564  	openingAuction := &types.AuctionDuration{
   565  		Duration: 1,
   566  	}
   567  	tm := getTestMarket(t, now, nil, openingAuction)
   568  
   569  	setMarkPrice(t, tm, openingAuction, now, initialMarkPrice)
   570  
   571  	asset := tm.asset
   572  
   573  	bondPenaltyParameter := 0.1
   574  	tm.market.OnMarketLiquidityV2BondPenaltyFactorUpdate(num.DecimalFromFloat(bondPenaltyParameter))
   575  
   576  	var mainPartyInitialDeposit uint64 = 800
   577  	transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit)
   578  	mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID()
   579  	mainPartyMarginAccID := fmt.Sprintf("%smainParty%s3", tm.market.GetID(), tm.asset)
   580  	addAccount(t, tm, auxParty1)
   581  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   582  
   583  	orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2)
   584  	confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1)
   585  	require.NotNil(t, confirmationSell1)
   586  	require.NoError(t, err)
   587  
   588  	orderSell2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-2", types.SideSell, mainParty, 1, initialMarkPrice+5)
   589  	confirmationSell2, err := tm.market.SubmitOrder(ctx, orderSell2)
   590  	require.NotNil(t, confirmationSell2)
   591  	require.NoError(t, err)
   592  
   593  	orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2)
   594  
   595  	confirmationBuy, err := tm.market.SubmitOrder(ctx, orderBuy1)
   596  	assert.NotNil(t, confirmationBuy)
   597  	assert.NoError(t, err)
   598  
   599  	require.Equal(t, 0, len(confirmationBuy.Trades))
   600  
   601  	lp := &types.LiquidityProvisionSubmission{
   602  		MarketID:         tm.market.GetID(),
   603  		CommitmentAmount: num.NewUint(200),
   604  		Fee:              num.DecimalFromFloat(0.05),
   605  	}
   606  
   607  	err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash())
   608  	require.NoError(t, err)
   609  
   610  	marginAcc, err := tm.collateralEngine.GetAccountByID(mainPartyMarginAccID)
   611  	require.NoError(t, err)
   612  	require.NotNil(t, marginAcc)
   613  	require.False(t, marginAcc.Balance.IsZero())
   614  
   615  	bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   616  	require.NoError(t, err)
   617  	require.NotNil(t, bondAcc)
   618  	bondAccBalanceBeforeMarketMove := bondAcc.Balance.Clone()
   619  	require.Equal(t, lp.CommitmentAmount, bondAccBalanceBeforeMarketMove)
   620  
   621  	insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset)
   622  	insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   623  	require.NoError(t, err)
   624  	insurancePoolBalanceBeforeMarketMove := insurancePool.Balance.Clone()
   625  
   626  	// Add sell order so LP can be closed out
   627  	orderSellAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideSell, auxParty1, 10, orderSell1.Price.Uint64()+1)
   628  	confirmationSellAux1, err := tm.market.SubmitOrder(ctx, orderSellAux1)
   629  	require.NotNil(t, confirmationSellAux1)
   630  	require.NoError(t, err)
   631  	require.Equal(t, 0, len(confirmationSellAux1.Trades))
   632  
   633  	orderBuyAux1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party2-buy-order-1", types.SideBuy, auxParty1, orderSell1.Size+1, orderSell1.Price.Uint64())
   634  	confirmationBuyAux1, err := tm.market.SubmitOrder(ctx, orderBuyAux1)
   635  	require.NotNil(t, confirmationBuyAux1)
   636  	require.NoError(t, err)
   637  	require.Equal(t, 2, len(confirmationBuyAux1.Trades))
   638  
   639  	genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   640  	require.NoError(t, err)
   641  	require.NotNil(t, genAcc)
   642  	require.True(t, genAcc.Balance.IsZero())
   643  
   644  	marginAccount, err := tm.collateralEngine.GetAccountByID(mainPartyMarginAccID)
   645  	require.NoError(t, err)
   646  	require.False(t, marginAccount.Balance.IsZero())
   647  
   648  	bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset)
   649  	require.NoError(t, err)
   650  	require.NotNil(t, bondAcc)
   651  	require.True(t, bondAcc.Balance.LT(bondAccBalanceBeforeMarketMove))
   652  	require.True(t, bondAcc.Balance.IsZero())
   653  
   654  	require.Equal(t, 1, tm.market.GetLPSCount())
   655  
   656  	insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   657  
   658  	require.NoError(t, err)
   659  	require.NotNil(t, insurancePool)
   660  	require.True(t, insurancePool.Balance.GT(insurancePoolBalanceBeforeMarketMove))
   661  	require.True(t, bondAcc.Balance.IsZero())
   662  }
   663  
   664  func TestBondAccountUsedForMarginShortagePenaltyNotPaidOnTransitionFromAuction(t *testing.T) {
   665  	t.Skip("to be fixed @witold")
   666  	mainParty := "mainParty"
   667  	auxParty1 := "auxParty1"
   668  	now := time.Unix(10, 0)
   669  	ctx := context.Background()
   670  	openingAuctionDuration := &types.AuctionDuration{Duration: 10}
   671  	tm := getTestMarket2(t, now, nil, openingAuctionDuration, true, 0.99)
   672  
   673  	mktData := tm.market.GetMarketData()
   674  	require.NotNil(t, mktData)
   675  	require.Equal(t, types.MarketTradingModeOpeningAuction, mktData.MarketTradingMode)
   676  
   677  	initialMarkPrice := uint64(99)
   678  
   679  	asset, err := tm.mktCfg.GetAssets()
   680  	require.NoError(t, err)
   681  
   682  	var mainPartyInitialDeposit uint64 = 784 // 794 is the minimum required amount to cover margin without dipping into the bond account
   683  	transferResp := addAccountWithAmount(tm, mainParty, mainPartyInitialDeposit)
   684  	mainPartyGenAccID := transferResp.Entries[0].ToAccount.ID()
   685  	addAccount(t, tm, auxParty1)
   686  	tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   687  
   688  	orderSell1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-sell-order-1", types.SideSell, mainParty, 5, initialMarkPrice+2)
   689  	confirmationSell1, err := tm.market.SubmitOrder(ctx, orderSell1)
   690  	require.NotNil(t, confirmationSell1)
   691  	require.NoError(t, err)
   692  
   693  	orderBuy1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "party1-buy-order-1", types.SideBuy, mainParty, 4, initialMarkPrice-2)
   694  	confirmationBuy1, err := tm.market.SubmitOrder(ctx, orderBuy1)
   695  	assert.NotNil(t, confirmationBuy1)
   696  	assert.NoError(t, err)
   697  	require.Equal(t, 0, len(confirmationBuy1.Trades))
   698  
   699  	lp := &types.LiquidityProvisionSubmission{
   700  		MarketID:         tm.market.GetID(),
   701  		CommitmentAmount: num.NewUint(200),
   702  		Fee:              num.DecimalFromFloat(0.05),
   703  	}
   704  
   705  	genAcc, err := tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   706  	require.NoError(t, err)
   707  	require.NotNil(t, genAcc)
   708  	genAccBalanceBeforeLPSubmission := genAcc.Balance.Clone()
   709  	require.False(t, genAcc.Balance.IsZero())
   710  
   711  	err = tm.market.SubmitLiquidityProvision(ctx, lp, mainParty, vgcrypto.RandomHash())
   712  	require.NoError(t, err)
   713  
   714  	genAcc, err = tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   715  	require.NoError(t, err)
   716  	require.NotNil(t, genAcc)
   717  	require.False(t, genAcc.Balance.IsZero())
   718  	require.Equal(t, genAcc.Balance, num.UintZero().Sub(genAccBalanceBeforeLPSubmission, lp.CommitmentAmount))
   719  
   720  	bondAcc, err := tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset[0])
   721  	require.NoError(t, err)
   722  	require.NotNil(t, bondAcc)
   723  	bondAccBalanceDuringAuction := bondAcc.Balance.Clone()
   724  	require.True(t, lp.CommitmentAmount.EQ(bondAcc.Balance))
   725  
   726  	insurancePoolAccID := fmt.Sprintf("%s*%s1", tm.market.GetID(), asset[0])
   727  	insurancePool, err := tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   728  	require.NoError(t, err)
   729  	insurancePoolDuringAuction := insurancePool.Balance.Clone()
   730  	require.True(t, insurancePool.Balance.IsZero())
   731  
   732  	// End auction
   733  	setMarkPrice(t, tm, openingAuctionDuration, now, initialMarkPrice)
   734  
   735  	mktData = tm.market.GetMarketData()
   736  	require.NotNil(t, mktData)
   737  	require.Equal(t, types.MarketTradingModeContinuous, mktData.MarketTradingMode)
   738  
   739  	genAcc, err = tm.collateralEngine.GetAccountByID(mainPartyGenAccID)
   740  	require.NoError(t, err)
   741  	require.NotNil(t, genAcc)
   742  	require.True(t, genAcc.Balance.IsZero())
   743  
   744  	bondAcc, err = tm.collateralEngine.GetOrCreatePartyBondAccount(ctx, mainParty, tm.mktCfg.ID, asset[0])
   745  	require.NoError(t, err)
   746  	require.NotNil(t, bondAcc)
   747  	require.True(t, bondAcc.Balance.LT(bondAccBalanceDuringAuction))
   748  	require.False(t, bondAcc.Balance.IsZero())
   749  	require.True(t, bondAcc.Balance.LT(lp.CommitmentAmount))
   750  
   751  	insurancePool, err = tm.collateralEngine.GetAccountByID(insurancePoolAccID)
   752  	require.NotNil(t, insurancePool)
   753  	require.NoError(t, err)
   754  	require.True(t, insurancePool.Balance.EQ(insurancePoolDuringAuction))
   755  	require.True(t, insurancePool.Balance.IsZero())
   756  }