code.vegaprotocol.io/vega@v0.79.0/core/execution/future/liquidity_provision_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  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/events"
    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  	proto "code.vegaprotocol.io/vega/protos/vega"
    29  
    30  	"github.com/golang/mock/gomock"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func TestSubmit(t *testing.T) {
    36  	pMonitorSettings := &types.PriceMonitoringSettings{
    37  		Parameters: &types.PriceMonitoringParameters{
    38  			Triggers: []*types.PriceMonitoringTrigger{},
    39  		},
    40  	}
    41  	now := time.Unix(10, 0)
    42  	block := time.Second
    43  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
    44  
    45  	t.Run("check that we reject LP submission If fee is incorrect", func(t *testing.T) {
    46  		tm := getTestMarket(t, now, nil, nil)
    47  
    48  		// Create a new party account with very little funding
    49  		addAccountWithAmount(tm, "party-A", 100000000)
    50  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
    51  
    52  		// Start the opening auction
    53  		tm.mas.StartOpeningAuction(tm.now, &types.AuctionDuration{Duration: 10})
    54  		tm.mas.AuctionStarted(ctx, tm.now)
    55  		tm.market.EnterAuction(ctx)
    56  
    57  		// Submitting a zero or smaller fee should cause a reject
    58  		lps := &types.LiquidityProvisionSubmission{
    59  			Fee:              num.DecimalFromFloat(-0.50),
    60  			MarketID:         tm.market.GetID(),
    61  			CommitmentAmount: num.NewUint(1000),
    62  		}
    63  
    64  		err := tm.market.SubmitLiquidityProvision(ctx, lps, "party-A", vgcrypto.RandomHash())
    65  		require.Error(t, err)
    66  		assert.Equal(t, 0, tm.market.GetLPSCount())
    67  
    68  		// Submitting a fee greater than 1.0 should cause a reject
    69  		lps = &types.LiquidityProvisionSubmission{
    70  			Fee:              num.DecimalFromFloat(1.01),
    71  			MarketID:         tm.market.GetID(),
    72  			CommitmentAmount: num.NewUint(1000),
    73  		}
    74  
    75  		err = tm.market.SubmitLiquidityProvision(ctx, lps, "party-A", vgcrypto.RandomHash())
    76  		require.Error(t, err)
    77  		assert.Equal(t, 0, tm.market.GetLPSCount())
    78  	})
    79  
    80  	t.Run("test liquidity provision fee validation", func(t *testing.T) {
    81  		pMonitorSettings := &types.PriceMonitoringSettings{
    82  			Parameters: &types.PriceMonitoringParameters{
    83  				Triggers: []*types.PriceMonitoringTrigger{},
    84  			},
    85  		}
    86  		mktCfg := getMarket(pMonitorSettings, &types.AuctionDuration{
    87  			Duration: 10000,
    88  		})
    89  		mktCfg.Fees.Factors = &types.FeeFactors{
    90  			LiquidityFee:      num.DecimalFromFloat(0.001),
    91  			InfrastructureFee: num.DecimalFromFloat(0.0005),
    92  			MakerFee:          num.DecimalFromFloat(0.00025),
    93  		}
    94  		mktCfg.TradableInstrument.RiskModel = &types.TradableInstrumentLogNormalRiskModel{
    95  			LogNormalRiskModel: &types.LogNormalRiskModel{
    96  				RiskAversionParameter: num.DecimalFromFloat(0.001),
    97  				Tau:                   num.DecimalFromFloat(0.00011407711613050422),
    98  				Params: &types.LogNormalModelParams{
    99  					Mu:    num.DecimalZero(),
   100  					R:     num.DecimalFromFloat(0.016),
   101  					Sigma: num.DecimalFromFloat(20),
   102  				},
   103  			},
   104  		}
   105  
   106  		lpparty := "lp-party-1"
   107  
   108  		tm := newTestMarket(t, now).Run(ctx, mktCfg)
   109  		tm.StartOpeningAuction().
   110  			// the liquidity provider
   111  			WithAccountAndAmount(lpparty, 50000000000000)
   112  
   113  		tm.market.OnTick(ctx, tm.now)
   114  
   115  		// Add a LPSubmission
   116  		// this is a log of stake, enough to cover all
   117  		// the required stake for the market
   118  		lpSubmission := &types.LiquidityProvisionSubmission{
   119  			MarketID:         tm.market.GetID(),
   120  			CommitmentAmount: num.NewUint(70000),
   121  			Fee:              num.DecimalFromFloat(-0.1),
   122  			Reference:        "ref-lp-submission-1",
   123  		}
   124  
   125  		// submit our lp
   126  		require.EqualError(t,
   127  			tm.market.SubmitLiquidityProvision(
   128  				ctx, lpSubmission, lpparty, vgcrypto.RandomHash()),
   129  			"invalid liquidity provision fee",
   130  		)
   131  
   132  		lpSubmission.Fee = num.DecimalFromFloat(10)
   133  
   134  		// submit our lp
   135  		require.EqualError(t,
   136  			tm.market.SubmitLiquidityProvision(
   137  				ctx, lpSubmission, lpparty, vgcrypto.RandomHash()),
   138  			"invalid liquidity provision fee",
   139  		)
   140  
   141  		lpSubmission.Fee = num.DecimalZero()
   142  
   143  		// submit our lp
   144  		require.NoError(t,
   145  			tm.market.SubmitLiquidityProvision(
   146  				ctx, lpSubmission, lpparty, vgcrypto.RandomHash()),
   147  		)
   148  	})
   149  
   150  	t.Run("check we can submit LP during price auction", func(t *testing.T) {
   151  		hdec := num.DecimalFromFloat(60)
   152  		pMonitorSettings := &types.PriceMonitoringSettings{
   153  			Parameters: &types.PriceMonitoringParameters{
   154  				Triggers: []*types.PriceMonitoringTrigger{
   155  					{
   156  						Horizon:          60,
   157  						HorizonDec:       hdec,
   158  						Probability:      num.DecimalFromFloat(0.15),
   159  						AuctionExtension: 60,
   160  					},
   161  				},
   162  			},
   163  		}
   164  
   165  		tm := getTestMarket(t, now, pMonitorSettings, nil)
   166  		tm.market.OnMarketAuctionMinimumDurationUpdate(ctx, 10*time.Second)
   167  
   168  		// Create a new party account with very little funding
   169  		addAccountWithAmount(tm, "party-A", 70000000)
   170  		addAccountWithAmount(tm, "party-B", 10000000)
   171  		addAccountWithAmount(tm, "party-C", 10000000)
   172  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   173  
   174  		tm.mas.StartOpeningAuction(tm.now, &types.AuctionDuration{Duration: 10})
   175  		tm.mas.AuctionStarted(ctx, tm.now)
   176  		tm.market.EnterAuction(ctx)
   177  
   178  		lpParty := "lp-party-1"
   179  		addAccountWithAmount(tm, lpParty, 5000000)
   180  
   181  		// ensure LP is set
   182  		lps := &types.LiquidityProvisionSubmission{
   183  			Fee:              num.DecimalFromFloat(0.01),
   184  			MarketID:         tm.market.GetID(),
   185  			CommitmentAmount: num.NewUint(5000000),
   186  		}
   187  		require.NoError(t, tm.market.SubmitLiquidityProvision(
   188  			context.Background(), lps, lpParty, vgcrypto.RandomHash(),
   189  		))
   190  
   191  		// Create some normal orders to set the reference prices
   192  		o1 := getMarketOrder(tm, tm.now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order01", types.SideBuy, "party-B", 10, 1000)
   193  		o1conf, err := tm.market.SubmitOrder(ctx, o1)
   194  		require.NotNil(t, o1conf)
   195  		require.NoError(t, err)
   196  
   197  		o2 := getMarketOrder(tm, tm.now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order02", types.SideSell, "party-C", 2, 1000)
   198  		o2conf, err := tm.market.SubmitOrder(ctx, o2)
   199  		require.NotNil(t, o2conf)
   200  		require.NoError(t, err)
   201  
   202  		o3 := getMarketOrder(tm, tm.now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order03", types.SideSell, "party-C", 1, 2000)
   203  		o3conf, err := tm.market.SubmitOrder(ctx, o3)
   204  		require.NotNil(t, o3conf)
   205  		require.NoError(t, err)
   206  
   207  		o4 := getMarketOrder(tm, tm.now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order04", types.SideSell, "party-C", 10, 3000)
   208  		o4conf, err := tm.market.SubmitOrder(ctx, o4)
   209  		require.NotNil(t, o4conf)
   210  		require.NoError(t, err)
   211  
   212  		assert.Equal(t, types.AuctionTriggerOpening, tm.market.GetMarketData().Trigger)
   213  		// Leave the auction so we can uncross the book
   214  		tm.now = tm.now.Add(block * 11)
   215  		tm.market.OnTick(ctx, tm.now)
   216  		// ensure we left auction
   217  		assert.Equal(t, types.AuctionTriggerUnspecified, tm.market.GetMarketData().Trigger)
   218  
   219  		// Move the price enough that we go into a price auction
   220  		o5 := getMarketOrder(tm, tm.now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order05", types.SideBuy, "party-B", 3, 3000)
   221  		o5conf, err := tm.market.SubmitOrder(ctx, o5)
   222  		require.NotNil(t, o5conf)
   223  		require.NoError(t, err)
   224  
   225  		// Check we are in price auction
   226  		assert.Equal(t, types.AuctionTriggerPrice, tm.market.GetMarketData().Trigger)
   227  
   228  		// Submitting a correct entry
   229  		lps2 := &types.LiquidityProvisionSubmission{
   230  			Fee:              num.DecimalFromFloat(0.01),
   231  			MarketID:         tm.market.GetID(),
   232  			CommitmentAmount: num.NewUint(1000),
   233  		}
   234  
   235  		err = tm.market.SubmitLiquidityProvision(ctx, lps2, "party-A", vgcrypto.RandomHash())
   236  
   237  		tm.market.OnEpochEvent(ctx, types.Epoch{Action: proto.EpochAction_EPOCH_ACTION_START})
   238  		require.NoError(t, err)
   239  		require.Equal(t, types.LiquidityProvisionStatusActive.String(), tm.market.GetLPSState("party-A").String())
   240  		// Only 3 pegged orders as one fails due to price monitoring
   241  		assert.Equal(t, 0, tm.market.GetPeggedOrderCount())
   242  	})
   243  
   244  	t.Run("check that rejected market stops liquidity provision", func(t *testing.T) {
   245  		mktCfg := getMarket(pMonitorSettings, &types.AuctionDuration{
   246  			Duration: 10000,
   247  		})
   248  		mktCfg.Fees.Factors = &types.FeeFactors{
   249  			InfrastructureFee: num.DecimalFromFloat(0.0005),
   250  			MakerFee:          num.DecimalFromFloat(0.00025),
   251  		}
   252  
   253  		mktCfg.TradableInstrument.RiskModel = &types.TradableInstrumentLogNormalRiskModel{
   254  			LogNormalRiskModel: &types.LogNormalRiskModel{
   255  				RiskAversionParameter: num.DecimalFromFloat(0.001),
   256  				Tau:                   num.DecimalFromFloat(0.00011407711613050422),
   257  				Params: &types.LogNormalModelParams{
   258  					Mu:    num.DecimalZero(),
   259  					R:     num.DecimalFromFloat(0.016),
   260  					Sigma: num.DecimalFromFloat(2),
   261  				},
   262  			},
   263  		}
   264  
   265  		lpparty := "lp-party-1"
   266  
   267  		tm := newTestMarket(t, now).Run(ctx, mktCfg)
   268  		tm.WithAccountAndAmount(lpparty, 100000000000000)
   269  		tm.now = now
   270  		tm.market.OnTick(ctx, now)
   271  
   272  		// Add a LPSubmission
   273  		// this is a log of stake, enough to cover all
   274  		// the required stake for the market
   275  		lpSubmission := &types.LiquidityProvisionSubmission{
   276  			MarketID:         tm.market.GetID(),
   277  			CommitmentAmount: num.NewUint(10000000),
   278  			Fee:              num.DecimalFromFloat(0.5),
   279  			Reference:        "ref-lp-submission-1",
   280  		}
   281  
   282  		// submit our lp
   283  		tm.events = nil
   284  		require.NoError(t,
   285  			tm.market.SubmitLiquidityProvision(
   286  				ctx, lpSubmission, lpparty, vgcrypto.RandomHash()),
   287  		)
   288  
   289  		t.Run("lp submission is pending", func(t *testing.T) {
   290  			// First collect all the orders events
   291  			var found *proto.LiquidityProvision
   292  			for _, e := range tm.events {
   293  				switch evt := e.(type) {
   294  				case *events.LiquidityProvision:
   295  					found = evt.LiquidityProvision()
   296  				}
   297  			}
   298  			require.NotNil(t, found)
   299  			// no update to the liquidity fee
   300  			assert.Equal(t, found.Status.String(), types.LiquidityProvisionStatusActive.String())
   301  		})
   302  
   303  		tm.events = nil
   304  		require.NoError(
   305  			t,
   306  			tm.market.Reject(context.Background()),
   307  		)
   308  
   309  		t.Run("lp submission is stopped", func(t *testing.T) {
   310  			// First collect all the orders events
   311  			var found *proto.LiquidityProvision
   312  			for _, e := range tm.events {
   313  				switch evt := e.(type) {
   314  				case *events.LiquidityProvision:
   315  					found = evt.LiquidityProvision()
   316  				}
   317  			}
   318  			require.NotNil(t, found)
   319  			// no update to the liquidity fee
   320  			assert.Equal(t, found.Status.String(), types.LiquidityProvisionStatusStopped.String())
   321  		})
   322  	})
   323  }
   324  
   325  func TestAmend(t *testing.T) {
   326  	now := time.Unix(10, 0)
   327  	ctx := vegacontext.WithTraceID(context.Background(), vgcrypto.RandomHash())
   328  
   329  	t.Run("check that fee is selected properly after changes", func(t *testing.T) {
   330  		auctionEnd := now.Add(10001 * time.Second)
   331  		mktCfg := getMarket(defaultPriceMonitorSettings, &types.AuctionDuration{
   332  			Duration: 10000,
   333  		})
   334  		mktCfg.Fees.Factors = &types.FeeFactors{
   335  			InfrastructureFee: num.DecimalFromFloat(0.0005),
   336  			MakerFee:          num.DecimalFromFloat(0.00025),
   337  		}
   338  		mktCfg.TradableInstrument.RiskModel = &types.TradableInstrumentLogNormalRiskModel{
   339  			LogNormalRiskModel: &types.LogNormalRiskModel{
   340  				RiskAversionParameter: num.DecimalFromFloat(0.001),
   341  				Tau:                   num.DecimalFromFloat(0.00011407711613050422),
   342  				Params: &types.LogNormalModelParams{
   343  					Mu:    num.DecimalZero(),
   344  					R:     num.DecimalFromFloat(0.016),
   345  					Sigma: num.DecimalFromFloat(20),
   346  				},
   347  			},
   348  		}
   349  
   350  		lpparty := "lp-party-1"
   351  		lpparty2 := "lp-party-2"
   352  
   353  		tm := newTestMarket(t, now).Run(ctx, mktCfg)
   354  		tm.StartOpeningAuction().
   355  			WithAccountAndAmount(lpparty, 500000000000).
   356  			WithAccountAndAmount(lpparty2, 500000000000)
   357  
   358  		tm.now = now
   359  		tm.market.OnTick(ctx, now)
   360  
   361  		// Add a LPSubmission
   362  		// this is a log of stake, enough to cover all
   363  		// the required stake for the market
   364  		lpSubmission := &types.LiquidityProvisionSubmission{
   365  			MarketID:         tm.market.GetID(),
   366  			CommitmentAmount: num.NewUint(70000),
   367  			Fee:              num.DecimalFromFloat(0.5),
   368  			Reference:        "ref-lp-submission-1",
   369  		}
   370  
   371  		// submit our lp
   372  		tm.events = nil
   373  		require.NoError(t,
   374  			tm.market.SubmitLiquidityProvision(
   375  				ctx, lpSubmission, lpparty, vgcrypto.RandomHash()),
   376  		)
   377  
   378  		tm.market.OnEpochEvent(ctx, types.Epoch{Action: proto.EpochAction_EPOCH_ACTION_START})
   379  
   380  		t.Run("current liquidity fee is 0.5", func(t *testing.T) {
   381  			// First collect all the orders events
   382  			found := proto.Market{}
   383  			for _, e := range tm.events {
   384  				switch evt := e.(type) {
   385  				case *events.MarketUpdated:
   386  					found = evt.Market()
   387  				}
   388  			}
   389  
   390  			assert.Equal(t, found.Fees.Factors.LiquidityFee, "0.5")
   391  		})
   392  
   393  		tm.EndOpeningAuction(t, auctionEnd, false)
   394  
   395  		// now we submit a second LP, with a lower fee,
   396  		// but we still need the first LP to cover liquidity
   397  		// so its fee is selected
   398  		lpSubmission2 := &types.LiquidityProvisionSubmission{
   399  			MarketID:         tm.market.GetID(),
   400  			CommitmentAmount: num.NewUint(20000),
   401  			Fee:              num.DecimalFromFloat(0.1),
   402  			Reference:        "ref-lp-submission-1",
   403  		}
   404  
   405  		// submit our lp
   406  		tm.events = nil
   407  		require.NoError(t,
   408  			tm.market.SubmitLiquidityProvision(
   409  				ctx, lpSubmission2, lpparty2, vgcrypto.RandomHash()),
   410  		)
   411  
   412  		tm.market.OnEpochEvent(ctx, types.Epoch{Action: proto.EpochAction_EPOCH_ACTION_START})
   413  
   414  		t.Run("current liquidity fee is still 0.5", func(t *testing.T) {
   415  			// First collect all the orders events
   416  			var found *proto.Market
   417  			for _, e := range tm.events {
   418  				switch evt := e.(type) {
   419  				case *events.MarketUpdated:
   420  					mkt := evt.Market()
   421  					found = &mkt
   422  				}
   423  			}
   424  
   425  			// no update to the liquidity fee
   426  			assert.Nil(t, found)
   427  		})
   428  	})
   429  
   430  	// Liquidity fee must be updated when new LP submissions are added or existing ones
   431  	// removed.
   432  	t.Run("check that LP fee is correct after changes", func(t *testing.T) {
   433  		t.Skip()
   434  		now := time.Unix(10, 0)
   435  		tm := getTestMarket(t, now, nil, nil)
   436  		ctx := context.Background()
   437  
   438  		// Create a new party account with very little funding
   439  		addAccountWithAmount(tm, "party-A", 7000)
   440  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   441  
   442  		tm.mas.StartOpeningAuction(now, &types.AuctionDuration{Duration: 10})
   443  		tm.mas.AuctionStarted(ctx, now)
   444  		tm.market.EnterAuction(ctx)
   445  
   446  		// We shouldn't have a liquidity fee yet
   447  		// TODO	assert.Equal(t, 0.0, tm.market.GetLiquidityFee())
   448  
   449  		// Submitting a correct entry
   450  		lps := &types.LiquidityProvisionSubmission{
   451  			Fee:              num.DecimalFromFloat(0.01),
   452  			MarketID:         tm.market.GetID(),
   453  			CommitmentAmount: num.NewUint(1000),
   454  		}
   455  
   456  		err := tm.market.SubmitLiquidityProvision(ctx, lps, "party-A", vgcrypto.RandomHash())
   457  		require.NoError(t, err)
   458  
   459  		tm.market.OnEpochEvent(ctx, types.Epoch{Action: proto.EpochAction_EPOCH_ACTION_START})
   460  
   461  		// Update the fee
   462  		lpa := &types.LiquidityProvisionAmendment{
   463  			Fee: num.DecimalFromFloat(0.5),
   464  		}
   465  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   466  		require.NoError(t, err)
   467  
   468  		// Check the fee is correct
   469  		// TODO	assert.Equal(t, 0.5, tm.market.GetLiquidityFee())
   470  	})
   471  
   472  	t.Run("check that LP commitment reduction is prevented correctly", func(t *testing.T) {
   473  		t.Skip()
   474  		now := time.Unix(10, 0)
   475  		tm := getTestMarket(t, now, nil, nil)
   476  		ctx := context.Background()
   477  
   478  		// Create a new party account with very little funding
   479  		addAccountWithAmount(tm, "party-A", 10000000000)
   480  		addAccountWithAmount(tm, "party-B", 10000000)
   481  		addAccountWithAmount(tm, "party-C", 10000000)
   482  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   483  
   484  		// Start the opening auction
   485  		tm.mas.StartOpeningAuction(now, &types.AuctionDuration{Duration: 10})
   486  		tm.mas.AuctionStarted(ctx, now)
   487  		tm.market.EnterAuction(ctx)
   488  
   489  		// Create some normal orders to set the reference prices
   490  		o1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order01", types.SideBuy, "party-B", 10, 10)
   491  		o1conf, err := tm.market.SubmitOrder(ctx, o1)
   492  		require.NotNil(t, o1conf)
   493  		require.NoError(t, err)
   494  
   495  		o2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order02", types.SideSell, "party-C", 2, 10)
   496  		o2conf, err := tm.market.SubmitOrder(ctx, o2)
   497  		require.NotNil(t, o2conf)
   498  		require.NoError(t, err)
   499  
   500  		o3 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order03", types.SideSell, "party-C", 1, 20)
   501  		o3conf, err := tm.market.SubmitOrder(ctx, o3)
   502  		require.NotNil(t, o3conf)
   503  		require.NoError(t, err)
   504  
   505  		o4 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order04", types.SideBuy, "party-C", 1, 9)
   506  		o4conf, err := tm.market.SubmitOrder(ctx, o4)
   507  		require.NotNil(t, o4conf)
   508  		require.NoError(t, err)
   509  
   510  		// Leave auction
   511  		tm.market.LeaveAuctionWithIDGen(ctx, now.Add(time.Second*20), newTestIDGenerator())
   512  		// mark price is set at 10, orders on book
   513  
   514  		// Submitting a correct entry
   515  		lps := &types.LiquidityProvisionSubmission{
   516  			Fee:              num.DecimalFromFloat(0.01),
   517  			MarketID:         tm.market.GetID(),
   518  			CommitmentAmount: num.NewUint(1000),
   519  		}
   520  
   521  		err = tm.market.SubmitLiquidityProvision(ctx, lps, "party-A", vgcrypto.RandomHash())
   522  		require.NoError(t, err)
   523  		assert.Equal(t, 1, tm.market.GetLPSCount())
   524  
   525  		// Try to reduce our commitment to below the minimum level
   526  		lpa := &types.LiquidityProvisionAmendment{
   527  			Fee:              num.DecimalFromFloat(0.01),
   528  			MarketID:         tm.market.GetID(),
   529  			CommitmentAmount: num.NewUint(1),
   530  		}
   531  
   532  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   533  		require.Error(t, err)
   534  		assert.Equal(t, 1, tm.market.GetLPSCount())
   535  	})
   536  
   537  	t.Run("check that changing LP during auction works", func(t *testing.T) {
   538  		t.Skip()
   539  		tm := getTestMarket(t, now, nil, nil)
   540  
   541  		// Create a new party account with very little funding
   542  		addAccountWithAmount(tm, "party-A", 7000)
   543  		addAccountWithAmount(tm, "party-B", 10000000)
   544  		addAccountWithAmount(tm, "party-C", 10000000)
   545  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   546  
   547  		tm.mas.StartOpeningAuction(now, &types.AuctionDuration{Duration: 10})
   548  		tm.mas.AuctionStarted(ctx, now)
   549  		tm.market.EnterAuction(ctx)
   550  
   551  		// Create some normal orders to set the reference prices
   552  		o1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order01", types.SideBuy, "party-B", 10, 10)
   553  		o1conf, err := tm.market.SubmitOrder(ctx, o1)
   554  		require.NotNil(t, o1conf)
   555  		require.NoError(t, err)
   556  
   557  		o2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order02", types.SideSell, "party-C", 2, 10)
   558  		o2conf, err := tm.market.SubmitOrder(ctx, o2)
   559  		require.NotNil(t, o2conf)
   560  		require.NoError(t, err)
   561  
   562  		o3 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order03", types.SideSell, "party-C", 1, 20)
   563  		o3conf, err := tm.market.SubmitOrder(ctx, o3)
   564  		require.NotNil(t, o3conf)
   565  		require.NoError(t, err)
   566  
   567  		o31 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order031", types.SideSell, "party-C", 1, 30)
   568  		o31conf, err := tm.market.SubmitOrder(ctx, o31)
   569  		require.NotNil(t, o31conf)
   570  		require.NoError(t, err)
   571  
   572  		// Submitting a correct entry
   573  		lps := &types.LiquidityProvisionSubmission{
   574  			Fee:              num.DecimalFromFloat(0.01),
   575  			MarketID:         tm.market.GetID(),
   576  			CommitmentAmount: num.NewUint(1000),
   577  		}
   578  
   579  		err = tm.market.SubmitLiquidityProvision(ctx, lps, "party-A", vgcrypto.RandomHash())
   580  		require.NoError(t, err)
   581  		require.Equal(t, types.LiquidityProvisionStatusPending.String(), tm.market.GetLPSState("party-A").String())
   582  		assert.Equal(t, 0, tm.market.GetPeggedOrderCount())
   583  
   584  		// Check we have the right amount of bond balance
   585  		assert.Equal(t, num.NewUint(1000), tm.market.GetBondAccountBalance(ctx, "party-A", tm.market.GetID(), tm.asset))
   586  
   587  		// Amend the commitment
   588  		lpa := &types.LiquidityProvisionAmendment{
   589  			Fee:              lps.Fee,
   590  			MarketID:         lps.MarketID,
   591  			CommitmentAmount: num.NewUint(2000),
   592  		}
   593  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   594  		require.NoError(t, err)
   595  
   596  		tm.market.OnEpochEvent(ctx, types.Epoch{Action: proto.EpochAction_EPOCH_ACTION_START})
   597  
   598  		// Check we have the right amount of bond balance
   599  		assert.Equal(t, num.NewUint(2000), tm.market.GetBondAccountBalance(ctx, "party-A", tm.market.GetID(), tm.asset))
   600  
   601  		// Amend the commitment
   602  		lpa.CommitmentAmount = num.NewUint(500)
   603  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   604  		require.NoError(t, err)
   605  
   606  		// Check we have the right amount of bond balance
   607  		assert.Equal(t, num.NewUint(500), tm.market.GetBondAccountBalance(ctx, "party-A", tm.market.GetID(), tm.asset))
   608  
   609  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   610  		require.NoError(t, err)
   611  		assert.Equal(t, 0, tm.market.GetPeggedOrderCount())
   612  		assert.Equal(t, 0, tm.market.GetParkedOrderCount())
   613  	})
   614  
   615  	// Check that we are unable to directly cancel or amend a pegged order that was
   616  	// created by the LP system.
   617  	t.Run("check that it is not possible to cancel or amend LP order", func(t *testing.T) {
   618  		t.Skip()
   619  		now := time.Unix(10, 0)
   620  		tm := getTestMarket(t, now, nil, nil)
   621  		ctx := context.Background()
   622  
   623  		// Create a new party account with very little funding
   624  		addAccountWithAmount(tm, "party-A", 10000000)
   625  		addAccountWithAmount(tm, "party-B", 10000000)
   626  		addAccountWithAmount(tm, "party-C", 10000000)
   627  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   628  
   629  		tm.mas.StartOpeningAuction(now, &types.AuctionDuration{Duration: 10})
   630  		tm.mas.AuctionStarted(ctx, now)
   631  		tm.market.EnterAuction(ctx)
   632  
   633  		// Create some normal orders to set the reference prices
   634  		o1 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order01", types.SideBuy, "party-B", 10, 10)
   635  		o1conf, err := tm.market.SubmitOrder(ctx, o1)
   636  		require.NotNil(t, o1conf)
   637  		require.NoError(t, err)
   638  
   639  		o2 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order02", types.SideSell, "party-C", 2, 10)
   640  		o2conf, err := tm.market.SubmitOrder(ctx, o2)
   641  		require.NotNil(t, o2conf)
   642  		require.NoError(t, err)
   643  
   644  		o3 := getMarketOrder(tm, now, types.OrderTypeLimit, types.OrderTimeInForceGTC, "Order03", types.SideSell, "party-C", 1, 20)
   645  		o3conf, err := tm.market.SubmitOrder(ctx, o3)
   646  		require.NotNil(t, o3conf)
   647  		require.NoError(t, err)
   648  
   649  		// Submitting a correct entry
   650  		lps := &types.LiquidityProvisionSubmission{
   651  			Fee:              num.DecimalFromFloat(0.01),
   652  			MarketID:         tm.market.GetID(),
   653  			CommitmentAmount: num.NewUint(1000),
   654  		}
   655  
   656  		err = tm.market.SubmitLiquidityProvision(ctx, lps, "party-A", vgcrypto.RandomHash())
   657  		require.NoError(t, err)
   658  
   659  		// Leave auction
   660  		tm.market.LeaveAuctionWithIDGen(ctx, now.Add(time.Second*20), newTestIDGenerator())
   661  
   662  		// Check we have an accepted LP submission
   663  		assert.Equal(t, 1, tm.market.GetLPSCount())
   664  
   665  		// Check we have the right number of live orders
   666  		assert.Equal(t, int64(6), tm.market.GetOrdersOnBookCount())
   667  
   668  		// FIXME(): REDO THIS TEST
   669  		// Attempt to cancel one of the pegged orders and it is rejected
   670  		// orders := tm.market.GetPeggedOrders("party-A")
   671  		// assert.GreaterOrEqual(t, len(orders), 0)
   672  
   673  		// cancelConf, err := tm.market.CancelOrder(ctx, "party-A", orders[0].Id)
   674  		// require.Nil(t, cancelConf)
   675  		// require.Error(t, err)
   676  		// assert.Equal(t, types.OrderError_ORDER_ERROR_EDIT_NOT_ALLOWED, err)
   677  
   678  		// // Attempt to amend one of the pegged orders
   679  		// amend := &commandspb.OrderAmendment{OrderId: orders[0].Id,
   680  		// 	MarketId:  orders[0].MarketId,
   681  		// 	SizeDelta: +5}
   682  		// amendConf, err := tm.market.AmendOrder(ctx, amend, orders[0].PartyId)
   683  		// require.Error(t, err)
   684  		// require.Nil(t, amendConf)
   685  		// assert.Equal(t, types.OrderError_ORDER_ERROR_EDIT_NOT_ALLOWED, err)
   686  	})
   687  
   688  	// If we submit a valid LP submission but then try ot alter it to something non valid
   689  	// the amendment should be rejected and the original submission is still valid.
   690  	t.Run("check that failed amend does not break existing LP", func(t *testing.T) {
   691  		t.Skip()
   692  		tm := getTestMarket(t, now, nil, nil)
   693  
   694  		// Create a new party account with very little funding
   695  		addAccountWithAmount(tm, "party-A", 7000)
   696  		addAccountWithAmount(tm, "party-B", 10000000)
   697  		addAccountWithAmount(tm, "party-C", 10000000)
   698  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   699  
   700  		tm.mas.StartOpeningAuction(now, &types.AuctionDuration{Duration: 10})
   701  		tm.mas.AuctionStarted(ctx, now)
   702  		tm.market.EnterAuction(ctx)
   703  
   704  		// Submitting a correct entry
   705  		lps := &types.LiquidityProvisionSubmission{
   706  			Fee:              num.DecimalFromFloat(0.01),
   707  			MarketID:         tm.market.GetID(),
   708  			CommitmentAmount: num.NewUint(1000),
   709  		}
   710  
   711  		err := tm.market.SubmitLiquidityProvision(ctx, lps, "party-A", vgcrypto.RandomHash())
   712  		require.NoError(t, err)
   713  		require.Equal(t, types.LiquidityProvisionStatusPending.String(), tm.market.GetLPSState("party-A").String())
   714  		assert.Equal(t, 0, tm.market.GetPeggedOrderCount())
   715  		assert.Equal(t, num.NewUint(1000), tm.market.GetBondAccountBalance(ctx, "party-A", tm.market.GetID(), tm.asset))
   716  
   717  		// Now attempt to amend the LP submission with empty fee
   718  		lpa := &types.LiquidityProvisionAmendment{
   719  			MarketID:         lps.MarketID,
   720  			CommitmentAmount: lps.CommitmentAmount,
   721  		}
   722  
   723  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   724  		require.NoError(t, err)
   725  
   726  		// Now attempt to amend the LP submission with empty fee and commitment amount
   727  		lpa = &types.LiquidityProvisionAmendment{
   728  			MarketID: lps.MarketID,
   729  		}
   730  
   731  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   732  		require.NoError(t, err)
   733  
   734  		// Now attempt to amend the LP submission with empty buys
   735  		lpa = &types.LiquidityProvisionAmendment{
   736  			Fee:              lps.Fee,
   737  			MarketID:         lps.MarketID,
   738  			CommitmentAmount: lps.CommitmentAmount,
   739  		}
   740  
   741  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   742  		require.NoError(t, err)
   743  
   744  		// Now attempt to amend the LP submission with no changes with nil buys and nil sells
   745  		lpa = &types.LiquidityProvisionAmendment{
   746  			Fee:              num.DecimalZero(),
   747  			MarketID:         lps.MarketID,
   748  			CommitmentAmount: num.UintZero(),
   749  		}
   750  
   751  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   752  		require.EqualError(t, err, "empty liquidity provision amendment content")
   753  
   754  		// Now attempt to amend the LP submission with no changes with sells and buys empty lists
   755  		lpa = &types.LiquidityProvisionAmendment{
   756  			Fee:              num.DecimalZero(),
   757  			MarketID:         lps.MarketID,
   758  			CommitmentAmount: num.UintZero(),
   759  		}
   760  
   761  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   762  		require.EqualError(t, err, "empty liquidity provision amendment content")
   763  
   764  		// Check that the original LP submission is still working fine
   765  		require.Equal(t, types.LiquidityProvisionStatusPending.String(), tm.market.GetLPSState("party-A").String())
   766  	})
   767  
   768  	// Reference must be updated when LP submissions are amended.
   769  	t.Run("check reference is correct after changes", func(t *testing.T) {
   770  		t.Skip()
   771  		tm := getTestMarket(t, now, nil, nil)
   772  
   773  		// Create a new party account with very little funding
   774  		addAccountWithAmount(tm, "party-A", 7000)
   775  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   776  
   777  		tm.mas.StartOpeningAuction(now, &types.AuctionDuration{Duration: 10})
   778  		tm.mas.AuctionStarted(ctx, now)
   779  		tm.market.EnterAuction(ctx)
   780  
   781  		// Submitting a correct entry
   782  		lps := &types.LiquidityProvisionSubmission{
   783  			Fee:              num.DecimalFromFloat(0.01),
   784  			MarketID:         tm.market.GetID(),
   785  			CommitmentAmount: num.NewUint(1000),
   786  			Reference:        "ref-lp-1",
   787  		}
   788  
   789  		err := tm.market.SubmitLiquidityProvision(ctx, lps, "party-A", vgcrypto.RandomHash())
   790  		require.NoError(t, err)
   791  
   792  		// Update the fee
   793  		lpa := &types.LiquidityProvisionAmendment{
   794  			Fee: num.DecimalFromFloat(0.2),
   795  		}
   796  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   797  		require.NoError(t, err)
   798  
   799  		// Update the fee again with a new reference
   800  		lpa = &types.LiquidityProvisionAmendment{
   801  			Fee:       num.DecimalFromFloat(0.5),
   802  			Reference: "ref-lp-2",
   803  		}
   804  		err = tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   805  		require.NoError(t, err)
   806  
   807  		t.Run("expect LP references", func(t *testing.T) {
   808  			// First collect all the lp events
   809  			found := map[string]*proto.LiquidityProvision{}
   810  			for _, e := range tm.events {
   811  				switch evt := e.(type) {
   812  				case *events.LiquidityProvision:
   813  					lp := evt.LiquidityProvision()
   814  					found[lp.Fee] = lp
   815  				}
   816  			}
   817  			expectedStatus := map[string]string{"0.01": "ref-lp-1", "0.2": "ref-lp-1", "0.5": "ref-lp-2"}
   818  			require.Len(t, found, len(expectedStatus))
   819  
   820  			for k, v := range expectedStatus {
   821  				assert.Equal(t, v, found[k].Reference)
   822  			}
   823  		})
   824  	})
   825  
   826  	t.Run("should reject LP amendment if no current LP", func(t *testing.T) {
   827  		t.Skip()
   828  		now := time.Unix(10, 0)
   829  		tm := getTestMarket(t, now, nil, nil)
   830  		ctx := context.Background()
   831  
   832  		// Create a new party account with very little funding
   833  		addAccountWithAmount(tm, "party-A", 7000)
   834  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   835  
   836  		tm.mas.StartOpeningAuction(now, &types.AuctionDuration{Duration: 10})
   837  		tm.mas.AuctionStarted(ctx, now)
   838  		tm.market.EnterAuction(ctx)
   839  
   840  		// Try to update the fee
   841  		lpa := &types.LiquidityProvisionAmendment{
   842  			Fee: num.DecimalFromFloat(0.5),
   843  		}
   844  		err := tm.market.AmendLiquidityProvision(ctx, lpa, "party-A", vgcrypto.RandomHash())
   845  		require.EqualError(t, err, "party is not a liquidity provider")
   846  	})
   847  
   848  	t.Run("should reject LP cancellation if no current LP", func(t *testing.T) {
   849  		t.Skip()
   850  		tm := getTestMarket(t, now, nil, nil)
   851  
   852  		// Create a new party account with very little funding
   853  		addAccountWithAmount(tm, "party-A", 7000)
   854  		tm.broker.EXPECT().Send(gomock.Any()).AnyTimes()
   855  
   856  		tm.mas.StartOpeningAuction(now, &types.AuctionDuration{Duration: 10})
   857  		tm.mas.AuctionStarted(ctx, now)
   858  		tm.market.EnterAuction(ctx)
   859  
   860  		lpc := &types.LiquidityProvisionCancellation{
   861  			MarketID: tm.market.GetID(),
   862  		}
   863  		err := tm.market.CancelLiquidityProvision(ctx, lpc, "party-A")
   864  		require.EqualError(t, err, "party is not a liquidity provider")
   865  	})
   866  }