From 97fd8c8959f011e025c718e5c14ade6f147ad115 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 11:18:12 +0100 Subject: [PATCH 01/14] feat: use liquidation parameter to set price range Signed-off-by: Elias Van Ootegem --- CHANGELOG.md | 1 + core/execution/future/liquidation.go | 2 +- core/execution/future/market.go | 17 +++- core/execution/future/market_snapshot.go | 2 +- core/execution/liquidation/engine.go | 29 +++--- core/execution/liquidation/engine_test.go | 78 ++++++---------- core/execution/liquidation/mocks/mocks.go | 41 +-------- core/types/liquidation.go | 6 ++ protos/sources/vega/markets.proto | 3 + protos/vega/markets.pb.go | 105 ++++++++++++---------- 10 files changed, 132 insertions(+), 152 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52cc25d56df..ab481de1b0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - [1102](https://github.com/vegaprotocol/core-test-coverage/issues/1102) - Add coverage for `0042-LIQF-084` - [11121](https://github.com/vegaprotocol/vega/issues/11121) - Remove auction trigger staleness functionality - [11127](https://github.com/vegaprotocol/vega/issues/11127) - Price monitoring engine should record all observations with the same weight +- [10995](https://github.com/vegaprotocol/vega/issues/10995) - Liquidation range defined by its own parameter. ### 🐛 Fixes diff --git a/core/execution/future/liquidation.go b/core/execution/future/liquidation.go index 55b6284dd0d..b430083db9d 100644 --- a/core/execution/future/liquidation.go +++ b/core/execution/future/liquidation.go @@ -30,7 +30,7 @@ func (m *Market) checkNetwork(ctx context.Context, now time.Time) error { return nil } // this only returns an error if we couldn't get the price range, incidating no orders on book - order, _ := m.liquidation.OnTick(ctx, now) + order, _ := m.liquidation.OnTick(ctx, now, m.getMarketMidPrice()) if order == nil { return nil } diff --git a/core/execution/future/market.go b/core/execution/future/market.go index 069a2b61b7b..ed3c8611b52 100644 --- a/core/execution/future/market.go +++ b/core/execution/future/market.go @@ -290,7 +290,7 @@ func NewMarket( if mkt.LiquidationStrategy == nil { mkt.LiquidationStrategy = liquidation.GetLegacyStrat() } - le := liquidation.New(log, mkt.LiquidationStrategy, mkt.GetID(), broker, book, auctionState, timeService, marketLiquidity, positionEngine, pMonitor) + le := liquidation.New(log, mkt.LiquidationStrategy, mkt.GetID(), broker, book, auctionState, timeService, positionEngine, pMonitor) marketType := mkt.MarketType() market := &Market{ @@ -709,6 +709,21 @@ func (m *Market) midPrice() *num.Uint { return midPrice } +func (m *Market) getMarketMidPrice() *num.Uint { + mp := num.UintZero() + if m.as.InAuction() { + return mp + } + bb, _, _ := m.matching.BestBidPriceAndVolume() + if bb.IsZero() { + return mp + } + if bo, _, _ := m.matching.BestOfferPriceAndVolume(); !bo.IsZero() { + return mp.Div(num.Sum(bb, bo), num.NewUint(2)) + } + return mp +} + func (m *Market) GetMarketData() types.MarketData { bestBidPrice, bestBidVolume, _ := m.matching.BestBidPriceAndVolume() bestOfferPrice, bestOfferVolume, _ := m.matching.BestOfferPriceAndVolume() diff --git a/core/execution/future/market_snapshot.go b/core/execution/future/market_snapshot.go index 4578204ac11..30fbb7314af 100644 --- a/core/execution/future/market_snapshot.go +++ b/core/execution/future/market_snapshot.go @@ -187,7 +187,7 @@ func NewMarketFromSnapshot( if mkt.LiquidationStrategy == nil { mkt.LiquidationStrategy = liquidation.GetLegacyStrat() } - le := liquidation.New(log, mkt.LiquidationStrategy, mkt.GetID(), broker, book, as, timeService, marketLiquidity, positionEngine, pMonitor) + le := liquidation.New(log, mkt.LiquidationStrategy, mkt.GetID(), broker, book, as, timeService, positionEngine, pMonitor) partyMargin := make(map[string]num.Decimal, len(em.PartyMarginFactors)) for _, pmf := range em.PartyMarginFactors { diff --git a/core/execution/liquidation/engine.go b/core/execution/liquidation/engine.go index 8df84c4c36a..3448e7db939 100644 --- a/core/execution/liquidation/engine.go +++ b/core/execution/liquidation/engine.go @@ -32,7 +32,7 @@ import ( "code.vegaprotocol.io/vega/logging" ) -//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/execution/liquidation Book,MarketLiquidity,IDGen,Positions,PriceMonitor +//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/execution/liquidation Book,IDGen,Positions,PriceMonitor type PriceMonitor interface { GetValidPriceRange() (num.WrappedDecimal, num.WrappedDecimal) @@ -42,10 +42,6 @@ type Book interface { GetVolumeAtPrice(price *num.Uint, side types.Side) uint64 } -type MarketLiquidity interface { - ValidOrdersPriceRange() (*num.Uint, *num.Uint, error) -} - type IDGen interface { NextID() string } @@ -66,7 +62,6 @@ type Engine struct { as common.AuctionState nextStep time.Time tSvc common.TimeService - ml MarketLiquidity position Positions stopped bool pmon PriceMonitor @@ -79,6 +74,7 @@ var ( DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.05), + DisposalSlippage: num.DecimalFromFloat(0.1), } // this comes closest to the existing behaviour (trying to close the network position in full in one go). @@ -87,7 +83,11 @@ var ( DisposalFraction: num.DecimalOne(), FullDisposalSize: math.MaxUint64, MaxFractionConsumed: num.DecimalOne(), + DisposalSlippage: num.DecimalFromFloat(10.0), } + + // just because we use it for min-max + dOne = num.DecimalFromFloat(1.0) ) // GetDefaultStrat is exporeted, expected to be used to update existing proposals on protocol upgrade @@ -103,7 +103,7 @@ func GetLegacyStrat() *types.LiquidationStrategy { return legacyStrat.DeepClone() } -func New(log *logging.Logger, cfg *types.LiquidationStrategy, mktID string, broker common.Broker, book Book, as common.AuctionState, tSvc common.TimeService, ml MarketLiquidity, pe Positions, pmon PriceMonitor) *Engine { +func New(log *logging.Logger, cfg *types.LiquidationStrategy, mktID string, broker common.Broker, book Book, as common.AuctionState, tSvc common.TimeService, pe Positions, pmon PriceMonitor) *Engine { // NOTE: This can be removed after protocol upgrade if cfg == nil { cfg = legacyStrat.DeepClone() @@ -116,7 +116,6 @@ func New(log *logging.Logger, cfg *types.LiquidationStrategy, mktID string, brok book: book, as: as, tSvc: tSvc, - ml: ml, position: pe, pos: &Pos{}, pmon: pmon, @@ -132,14 +131,20 @@ func (e *Engine) Update(cfg *types.LiquidationStrategy) { e.cfg = cfg } -func (e *Engine) OnTick(ctx context.Context, now time.Time) (*types.Order, error) { +func (e *Engine) OnTick(ctx context.Context, now time.Time, midPrice *num.Uint) (*types.Order, error) { if e.pos.open == 0 || e.as.InAuction() || e.nextStep.After(now) { return nil, nil } - minP, maxP, err := e.ml.ValidOrdersPriceRange() - if err != nil { - return nil, err + + // get the min/max price from the range based on slippage paramter + mpDec := num.DecimalFromUint(midPrice) + minP := num.UintZero() + if e.cfg.DisposalSlippage.LessThan(dOne) { + minD := mpDec.Mul(dOne.Sub(e.cfg.DisposalSlippage)) + minP, _ = num.UintFromDecimal(minD) } + maxD := mpDec.Mul(dOne.Add(e.cfg.DisposalSlippage)) + maxP, _ := num.UintFromDecimal(maxD) minB, maxB := e.pmon.GetValidPriceRange() diff --git a/core/execution/liquidation/engine_test.go b/core/execution/liquidation/engine_test.go index 196906f48bb..9fff759c66b 100644 --- a/core/execution/liquidation/engine_test.go +++ b/core/execution/liquidation/engine_test.go @@ -17,7 +17,6 @@ package liquidation_test import ( "context" - "errors" "fmt" "testing" "time" @@ -41,7 +40,6 @@ type tstEngine struct { *liquidation.Engine ctrl *gomock.Controller book *mocks.MockBook - ml *mocks.MockMarketLiquidity idgen *mocks.MockIDGen as *cmocks.MockAuctionState broker *bmocks.MockBroker @@ -107,23 +105,22 @@ func TestNetworkReducesOverTime(t *testing.T) { require.Equal(t, len(closed), len(pos)) require.Equal(t, len(closed), len(parties)) require.Equal(t, closed[0].Party(), parties[0]) + midPrice := num.NewUint(100) t.Run("call to ontick within the time step does nothing", func(t *testing.T) { now = now.Add(2 * time.Second) eng.as.EXPECT().InAuction().Times(1).Return(false) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.Nil(t, order) require.NoError(t, err) }) t.Run("after the time step passes, the first batch is disposed of", func(t *testing.T) { now = now.Add(3 * time.Second) - minP, maxP := num.UintZero(), num.UintOne() eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) // return a large volume so the full step is disposed eng.book.EXPECT().GetVolumeAtPrice(gomock.Any(), gomock.Any()).Times(1).Return(uint64(1000)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.NotNil(t, order) require.Equal(t, uint64(5), order.Size) @@ -132,7 +129,7 @@ func TestNetworkReducesOverTime(t *testing.T) { t.Run("ensure the next time step is set", func(t *testing.T) { now = now.Add(2 * time.Second) eng.as.EXPECT().InAuction().Times(1).Return(false) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.Nil(t, order) require.NoError(t, err) }) @@ -142,28 +139,16 @@ func TestNetworkReducesOverTime(t *testing.T) { // pass another step now = now.Add(3 * time.Second) eng.as.EXPECT().InAuction().Times(1).Return(true) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.Nil(t, order) require.NoError(t, err) }) - t.Run("when not in auction, if there is no price range, there is no trade", func(t *testing.T) { - eng.as.EXPECT().InAuction().Times(1).Return(false) - mlErr := errors.New("some error") - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(nil, nil, mlErr) - order, err := eng.OnTick(ctx, now) - require.Nil(t, order) - require.Error(t, err) - require.Equal(t, mlErr, err) - }) - t.Run("No longer in auction and we have a price range finally generates the order", func(t *testing.T) { - minP, maxP := num.UintZero(), num.UintOne() eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) // return a large volume so the full step is disposed eng.book.EXPECT().GetVolumeAtPrice(gomock.Any(), gomock.Any()).Times(1).Return(uint64(1000)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.NotNil(t, order) require.Equal(t, uint64(5), order.Size) @@ -191,12 +176,10 @@ func TestNetworkReducesOverTime(t *testing.T) { totalSize++ // now increase time by 4 seconds should dispose 5.1 -> 5 now = now.Add(4 * time.Second) - minP, maxP := num.UintZero(), num.UintOne() eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) // return a large volume so the full step is disposed eng.book.EXPECT().GetVolumeAtPrice(gomock.Any(), gomock.Any()).Times(1).Return(uint64(1000)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.NotNil(t, order) require.Equal(t, uint64(num.DecimalFromFloat(float64(totalSize)).Div(num.DecimalFromFloat(float64(10))).Ceil().IntPart()), order.Size) @@ -205,7 +188,7 @@ func TestNetworkReducesOverTime(t *testing.T) { t.Run("Updating the config changes the time left until the next step", func(t *testing.T) { now = now.Add(time.Second) eng.as.EXPECT().InAuction().Times(1).Return(false) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.Nil(t, order) require.NoError(t, err) // 4s to go, but... @@ -213,12 +196,10 @@ func TestNetworkReducesOverTime(t *testing.T) { eng.Update(config.DeepClone()) now = now.Add(2 * time.Second) // only 3 seconds later and we dispose of the next batch - minP, maxP := num.UintZero(), num.UintOne() eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) // return a large volume so the full step is disposed eng.book.EXPECT().GetVolumeAtPrice(gomock.Any(), gomock.Any()).Times(1).Return(uint64(1000)) - order, err = eng.OnTick(ctx, now) + order, err = eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.NotNil(t, order) require.Equal(t, uint64(num.DecimalFromFloat(float64(totalSize)).Div(num.DecimalFromFloat(float64(10))).Ceil().IntPart()), order.Size) @@ -237,12 +218,10 @@ func TestNetworkReducesOverTime(t *testing.T) { require.True(t, uint64(eng.GetNetworkPosition().Size()) <= config.FullDisposalSize) now = now.Add(3 * time.Second) // only 3 seconds later and we dispose of the next batch - minP, maxP := num.UintZero(), num.UintOne() eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) // return a large volume so the full step is disposed eng.book.EXPECT().GetVolumeAtPrice(gomock.Any(), gomock.Any()).Times(1).Return(uint64(1000)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.NotNil(t, order) require.Equal(t, config.FullDisposalSize, order.Size) @@ -255,8 +234,10 @@ func testOrderbookHasNoVolume(t *testing.T) { eng := getTestEngine(t, mID, nil) defer eng.Finish() + minP, midPrice := num.NewUint(90), num.NewUint(100) + // make sure the lower bound does not override the price range eng.pmon.EXPECT().GetValidPriceRange().AnyTimes().Return( - num.NewWrappedDecimal(num.UintZero(), num.DecimalZero()), + num.NewWrappedDecimal(num.NewUint(90), num.DecimalFromFloat(90.0)), num.NewWrappedDecimal(num.MaxUint(), num.MaxDecimal()), ) @@ -280,11 +261,9 @@ func testOrderbookHasNoVolume(t *testing.T) { require.Equal(t, len(closed), len(parties)) require.Equal(t, closed[0].Party(), parties[0]) // now when we close out, the book returns a volume of 0 is available - minP, maxP := num.UintZero(), num.UintOne() eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) eng.book.EXPECT().GetVolumeAtPrice(minP, types.SideBuy).Times(1).Return(uint64(0)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.Nil(t, order) } @@ -297,6 +276,7 @@ func testOrderbookFractionRounding(t *testing.T) { DisposalFraction: num.DecimalOne(), FullDisposalSize: 1000000, // plenty MaxFractionConsumed: num.DecimalFromFloat(0.5), + DisposalSlippage: num.DecimalFromFloat(10), } eng := getTestEngine(t, mID, &config) defer eng.Finish() @@ -330,11 +310,10 @@ func testOrderbookFractionRounding(t *testing.T) { require.Equal(t, closed[0].Party(), parties[0]) // now the available volume on the book is 1, with the fraction that gets rounded to 0.5 // which should be rounded UP to 1. - minP, maxP := num.UintZero(), num.UintOne() + minP, midPrice := num.UintZero(), num.NewUint(100) eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) eng.book.EXPECT().GetVolumeAtPrice(minP, types.SideBuy).Times(1).Return(uint64(1)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.Equal(t, uint64(1), order.Size) } @@ -347,6 +326,7 @@ func testOrderbookExceedsVolume(t *testing.T) { DisposalFraction: num.DecimalOne(), FullDisposalSize: 1000000, // plenty MaxFractionConsumed: num.DecimalFromFloat(0.5), + DisposalSlippage: num.DecimalFromFloat(10), } eng := getTestEngine(t, mID, &config) defer eng.Finish() @@ -378,12 +358,11 @@ func testOrderbookExceedsVolume(t *testing.T) { require.Equal(t, len(closed), len(pos)) require.Equal(t, len(closed), len(parties)) require.Equal(t, closed[0].Party(), parties[0]) - minP, maxP := num.UintZero(), num.UintOne() + minP, midPrice := num.UintZero(), num.NewUint(100) eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) // orderbook has 100x the available volume, with a factor of 0.5, that's still 50x eng.book.EXPECT().GetVolumeAtPrice(minP, types.SideBuy).Times(1).Return(uint64(netVol * 10)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.Equal(t, uint64(netVol), order.Size) } @@ -396,12 +375,13 @@ func testOrderCappedByPriceMonitor(t *testing.T) { DisposalFraction: num.DecimalOne(), FullDisposalSize: 1000000, // plenty MaxFractionConsumed: num.DecimalFromFloat(0.5), + DisposalSlippage: num.DecimalFromFloat(10), } eng := getTestEngine(t, mID, &config) defer eng.Finish() // these are the bounds given by the order book - minP, maxP := num.NewUint(100), num.NewUint(200) + midPrice := num.NewUint(150) // these are the price monitoring bounds minB := num.NewUint(150) @@ -434,11 +414,10 @@ func testOrderCappedByPriceMonitor(t *testing.T) { require.Equal(t, closed[0].Party(), parties[0]) eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) // we will check for volume at the price monitoring minimum eng.book.EXPECT().GetVolumeAtPrice(minB, types.SideBuy).Times(1).Return(uint64(netVol * 10)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.Equal(t, uint64(netVol), order.Size) } @@ -501,11 +480,10 @@ func TestLegacySupport(t *testing.T) { require.Equal(t, len(closed), len(parties)) require.Equal(t, closed[0].Party(), parties[0]) // now we should see an order for size 15 returned - minP, maxP := num.UintZero(), num.UintOne() + minP, midPrice := num.UintZero(), num.NewUint(100) eng.as.EXPECT().InAuction().Times(1).Return(false) - eng.ml.EXPECT().ValidOrdersPriceRange().Times(1).Return(minP, maxP, nil) eng.book.EXPECT().GetVolumeAtPrice(minP, types.SideBuy).Times(1).Return(uint64(netVol)) - order, err := eng.OnTick(ctx, now) + order, err := eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.Equal(t, uint64(netVol), order.Size) // now reduce the network size through distressed short position @@ -533,7 +511,7 @@ func TestLegacySupport(t *testing.T) { require.Equal(t, closed[0].Party(), parties[0]) require.Equal(t, netVol, eng.GetNetworkPosition().Size()) // now we should see no error, and no order returned - order, err = eng.OnTick(ctx, now) + order, err = eng.OnTick(ctx, now, midPrice) require.NoError(t, err) require.Nil(t, order) // now just make sure stopping for snapshots works as expected @@ -625,19 +603,17 @@ func getTestEngine(t *testing.T, marketID string, config *types.LiquidationStrat t.Helper() ctrl := gomock.NewController(t) book := mocks.NewMockBook(ctrl) - ml := mocks.NewMockMarketLiquidity(ctrl) idgen := mocks.NewMockIDGen(ctrl) as := cmocks.NewMockAuctionState(ctrl) broker := bmocks.NewMockBroker(ctrl) tSvc := cmocks.NewMockTimeService(ctrl) pe := mocks.NewMockPositions(ctrl) pmon := mocks.NewMockPriceMonitor(ctrl) - engine := liquidation.New(logging.NewDevLogger(), config, marketID, broker, book, as, tSvc, ml, pe, pmon) + engine := liquidation.New(logging.NewDevLogger(), config, marketID, broker, book, as, tSvc, pe, pmon) return &tstEngine{ Engine: engine, ctrl: ctrl, book: book, - ml: ml, idgen: idgen, as: as, broker: broker, diff --git a/core/execution/liquidation/mocks/mocks.go b/core/execution/liquidation/mocks/mocks.go index 24b220e4eab..36564847590 100644 --- a/core/execution/liquidation/mocks/mocks.go +++ b/core/execution/liquidation/mocks/mocks.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/vega/core/execution/liquidation (interfaces: Book,MarketLiquidity,IDGen,Positions,PriceMonitor) +// Source: code.vegaprotocol.io/vega/core/execution/liquidation (interfaces: Book,IDGen,Positions,PriceMonitor) // Package mocks is a generated GoMock package. package mocks @@ -53,45 +53,6 @@ func (mr *MockBookMockRecorder) GetVolumeAtPrice(arg0, arg1 interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeAtPrice", reflect.TypeOf((*MockBook)(nil).GetVolumeAtPrice), arg0, arg1) } -// MockMarketLiquidity is a mock of MarketLiquidity interface. -type MockMarketLiquidity struct { - ctrl *gomock.Controller - recorder *MockMarketLiquidityMockRecorder -} - -// MockMarketLiquidityMockRecorder is the mock recorder for MockMarketLiquidity. -type MockMarketLiquidityMockRecorder struct { - mock *MockMarketLiquidity -} - -// NewMockMarketLiquidity creates a new mock instance. -func NewMockMarketLiquidity(ctrl *gomock.Controller) *MockMarketLiquidity { - mock := &MockMarketLiquidity{ctrl: ctrl} - mock.recorder = &MockMarketLiquidityMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockMarketLiquidity) EXPECT() *MockMarketLiquidityMockRecorder { - return m.recorder -} - -// ValidOrdersPriceRange mocks base method. -func (m *MockMarketLiquidity) ValidOrdersPriceRange() (*num.Uint, *num.Uint, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidOrdersPriceRange") - ret0, _ := ret[0].(*num.Uint) - ret1, _ := ret[1].(*num.Uint) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ValidOrdersPriceRange indicates an expected call of ValidOrdersPriceRange. -func (mr *MockMarketLiquidityMockRecorder) ValidOrdersPriceRange() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidOrdersPriceRange", reflect.TypeOf((*MockMarketLiquidity)(nil).ValidOrdersPriceRange)) -} - // MockIDGen is a mock of IDGen interface. type MockIDGen struct { ctrl *gomock.Controller diff --git a/core/types/liquidation.go b/core/types/liquidation.go index 8a57cb4abb7..429f0d64ec3 100644 --- a/core/types/liquidation.go +++ b/core/types/liquidation.go @@ -28,6 +28,7 @@ type LiquidationStrategy struct { DisposalFraction num.Decimal FullDisposalSize uint64 MaxFractionConsumed num.Decimal + DisposalSlippage num.Decimal // this has to be a pointer for the time being, with the need to default to 0.1 } type LiquidationNode struct { @@ -109,11 +110,16 @@ func LiquidationStrategyFromProto(p *vegapb.LiquidationStrategy) (*LiquidationSt if err != nil { return nil, err } + slippage, err := num.DecimalFromString(p.DisposalSlippageRange) + if err != nil { + return nil, err + } return &LiquidationStrategy{ DisposalTimeStep: time.Second * time.Duration(p.DisposalTimeStep), DisposalFraction: df, FullDisposalSize: p.FullDisposalSize, MaxFractionConsumed: mfc, + DisposalSlippage: slippage, }, nil } diff --git a/protos/sources/vega/markets.proto b/protos/sources/vega/markets.proto index ffc510ae131..4a801fb3727 100644 --- a/protos/sources/vega/markets.proto +++ b/protos/sources/vega/markets.proto @@ -413,6 +413,9 @@ message LiquidationStrategy { uint64 full_disposal_size = 3; // Max fraction of the total volume of the orderbook, within liquidity bounds, that the network can use to close its position; range 0 through 1. string max_fraction_consumed = 4; + // Decimal > 0 defining the price range within which the network will trade to dispose of its position. Retroactively defaults to 0.1 (10%). + // The value can be > 1, if set to 1.5, the minimum price will be 0 (min(0, mid_price * (1 - 1.))), the maximum price will be mid_price * (1 + 1.5). + string disposal_slippage_range = 5; } enum CompositePriceType { diff --git a/protos/vega/markets.pb.go b/protos/vega/markets.pb.go index 05049616413..99e0f6c93c6 100644 --- a/protos/vega/markets.pb.go +++ b/protos/vega/markets.pb.go @@ -2333,6 +2333,9 @@ type LiquidationStrategy struct { FullDisposalSize uint64 `protobuf:"varint,3,opt,name=full_disposal_size,json=fullDisposalSize,proto3" json:"full_disposal_size,omitempty"` // Max fraction of the total volume of the orderbook, within liquidity bounds, that the network can use to close its position; range 0 through 1. MaxFractionConsumed string `protobuf:"bytes,4,opt,name=max_fraction_consumed,json=maxFractionConsumed,proto3" json:"max_fraction_consumed,omitempty"` + // Decimal > 0 defining the price range within which the network will trade to dispose of its position. Retroactively defaults to 0.1 (10%). + // The value can be > 1, if set to 1.5, the minimum price will be 0 (min(0, mid_price * (1 - 1.))), the maximum price will be mid_price * (1 + 1.5). + DisposalSlippageRange string `protobuf:"bytes,5,opt,name=disposal_slippage_range,json=disposalSlippageRange,proto3" json:"disposal_slippage_range,omitempty"` } func (x *LiquidationStrategy) Reset() { @@ -2395,6 +2398,13 @@ func (x *LiquidationStrategy) GetMaxFractionConsumed() string { return "" } +func (x *LiquidationStrategy) GetDisposalSlippageRange() string { + if x != nil { + return x.DisposalSlippageRange + } + return "" +} + // Mark price configuration parameters. type CompositePriceConfiguration struct { state protoimpl.MessageState @@ -2925,7 +2935,7 @@ var file_vega_markets_proto_rawDesc = []byte{ 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x22, 0xd2, 0x01, 0x0a, 0x13, 0x4c, + 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x22, 0x8a, 0x02, 0x0a, 0x13, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, @@ -2938,51 +2948,54 @@ var file_vega_markets_proto_rawDesc = []byte{ 0x69, 0x73, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x46, - 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x22, - 0xda, 0x03, 0x0a, 0x1b, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x69, - 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x63, 0x61, 0x79, 0x57, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x70, 0x6f, 0x77, 0x65, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x61, 0x79, 0x50, 0x6f, - 0x77, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x73, 0x68, 0x5f, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x73, 0x68, 0x41, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x77, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x6c, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, - 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x6e, 0x65, 0x73, 0x73, - 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x14, 0x63, 0x6f, 0x6d, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x2e, 0x43, - 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x69, 0x63, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x11, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x64, 0x61, - 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x5d, 0x0a, - 0x19, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, 0x73, 0x70, - 0x65, 0x63, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x42, 0x69, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, - 0x72, 0x69, 0x63, 0x65, 0x52, 0x16, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x53, 0x70, 0x65, 0x63, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2a, 0xa3, 0x01, 0x0a, - 0x12, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x20, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, - 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4f, 0x4d, - 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x57, 0x45, 0x49, 0x47, 0x48, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, - 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x4e, 0x10, 0x02, 0x12, 0x23, 0x0a, - 0x1f, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x41, 0x53, 0x54, 0x5f, 0x54, 0x52, 0x41, 0x44, 0x45, - 0x10, 0x03, 0x42, 0x27, 0x5a, 0x25, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x65, 0x67, 0x61, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x65, 0x67, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x12, + 0x36, 0x0a, 0x17, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x73, 0x6c, 0x69, 0x70, + 0x70, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x15, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x53, 0x6c, 0x69, 0x70, 0x70, 0x61, + 0x67, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xda, 0x03, 0x0a, 0x1b, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x63, 0x61, 0x79, + 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x65, 0x63, 0x61, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, + 0x63, 0x61, 0x79, 0x5f, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x64, 0x65, 0x63, 0x61, 0x79, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, + 0x61, 0x73, 0x68, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x63, 0x61, 0x73, 0x68, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x57, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x74, + 0x61, 0x6c, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, + 0x74, 0x61, 0x6c, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x12, 0x4a, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x70, + 0x72, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x18, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, + 0x50, 0x72, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x46, 0x0a, + 0x11, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, 0x73, 0x70, + 0x65, 0x63, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x5d, 0x0a, 0x19, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x2e, + 0x53, 0x70, 0x65, 0x63, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x43, 0x6f, + 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x52, 0x16, 0x64, 0x61, + 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x53, 0x70, 0x65, 0x63, 0x42, 0x69, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x2a, 0xa3, 0x01, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x20, 0x43, + 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x5f, 0x50, + 0x52, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x45, 0x49, 0x47, 0x48, 0x54, + 0x45, 0x44, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, + 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x44, + 0x49, 0x41, 0x4e, 0x10, 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, + 0x54, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x41, + 0x53, 0x54, 0x5f, 0x54, 0x52, 0x41, 0x44, 0x45, 0x10, 0x03, 0x42, 0x27, 0x5a, 0x25, 0x63, 0x6f, + 0x64, 0x65, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, + 0x69, 0x6f, 0x2f, 0x76, 0x65, 0x67, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x76, + 0x65, 0x67, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From 93fe7a6064675612641541226aee4a570b639721 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 12:01:07 +0100 Subject: [PATCH 02/14] test: fix integration tests to work with new disposal slippage parameter Signed-off-by: Elias Van Ootegem --- core/execution/liquidation/snapshot.go | 6 +++++- .../features/closeouts/0012-POSR-009.feature | 4 ++-- .../features/closeouts/0012-POSR-010.feature | 4 ++-- .../closeout-long-with-custom-strategy.feature | 6 +++--- .../features/liquidation/0012-POSR-012.feature | 15 ++++++++++----- ...work_disposing_position_outside_bounds.feature | 8 ++++---- .../features/verified/network_pnl.feature | 6 +++--- .../features/verified/network_pnl_funding.feature | 4 ++-- .../verified/network_position_disposal.feature | 7 ++++--- .../defaults/liquidation-config/AC-013-strat.json | 3 ++- .../default-liquidation-strat.json | 3 ++- .../legacy-liquidation-strategy.json | 3 ++- .../slow-liquidation-strat.json | 3 ++- .../integration/steps/the_liquidation_strategy.go | 6 ++++++ core/types/liquidation.go | 13 +++++++++---- 15 files changed, 58 insertions(+), 33 deletions(-) diff --git a/core/execution/liquidation/snapshot.go b/core/execution/liquidation/snapshot.go index 22391889686..a8b1767cd67 100644 --- a/core/execution/liquidation/snapshot.go +++ b/core/execution/liquidation/snapshot.go @@ -57,9 +57,13 @@ func (e *Engine) LoadState(ctx context.Context, pl *types.Payload) ([]types.Stat if d.Config != nil { e.cfg = d.Config.DeepClone() } else { - // @NOTE this can be removed after protocol upgrade has completed + // this can probably be removed now e.cfg = GetLegacyStrat() } + // @NOTE this should have a protocol upgrade guard around it + if e.cfg.DisposalFraction.IsZero() { + e.cfg.DisposalFraction = defaultStrat.DisposalSlippage + } default: return nil, types.ErrUnknownSnapshotType } diff --git a/core/integration/features/closeouts/0012-POSR-009.feature b/core/integration/features/closeouts/0012-POSR-009.feature index 72cf1b3ce68..c4ebdf8a504 100644 --- a/core/integration/features/closeouts/0012-POSR-009.feature +++ b/core/integration/features/closeouts/0012-POSR-009.feature @@ -2,8 +2,8 @@ Feature: When a party is distressed and gets closed out the network's position g Background: Given the liquidation strategies: - | name | disposal step | disposal fraction | full disposal size | max fraction consumed | - | disposal-strat | 10 | 1.0 | 1000 | 1.0 | + | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | + | disposal-strat | 10 | 1.0 | 1000 | 1.0 | 0.1 | Given the markets: | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | liquidation strategy | diff --git a/core/integration/features/closeouts/0012-POSR-010.feature b/core/integration/features/closeouts/0012-POSR-010.feature index 57a39148ef5..f0da894e5c2 100644 --- a/core/integration/features/closeouts/0012-POSR-010.feature +++ b/core/integration/features/closeouts/0012-POSR-010.feature @@ -3,8 +3,8 @@ Feature: When the network party holds a non-zero position and there are not enou Background: Given the liquidation strategies: - | name | disposal step | disposal fraction | full disposal size | max fraction consumed | - | disposal-strat | 1000 | 1.0 | 1000 | 1.0 | + | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | + | disposal-strat | 1000 | 1.0 | 1000 | 1.0 | 0.1 | Given the markets: | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | liquidation strategy | diff --git a/core/integration/features/closeouts/closeout-long-with-custom-strategy.feature b/core/integration/features/closeouts/closeout-long-with-custom-strategy.feature index 4b7a7e9a6b1..a90744bd720 100644 --- a/core/integration/features/closeouts/closeout-long-with-custom-strategy.feature +++ b/core/integration/features/closeouts/closeout-long-with-custom-strategy.feature @@ -1,10 +1,10 @@ Feature: Same as 1847-closeout-long test, but with a custom liquidation strategy in place Background: - # disposal strategy every 5 seconds, 20% until 10 or less, max 10% of the book used + # disposal strategy every 5 seconds, 20% until 10 or less, max 10% of the book used, slippage is set to 10 so price range is always wide enough Given the liquidation strategies: - | name | disposal step | disposal fraction | full disposal size | max fraction consumed | - | disposal-strat-1 | 5 | 0.2 | 10 | 0.1 | + | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | + | disposal-strat-1 | 5 | 0.2 | 10 | 0.1 | 10 | And the markets: | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | liquidation strategy | | ETH/DEC19 | BTC | BTC | default-simple-risk-model-4 | default-margin-calculator | 1 | default-none | default-none | default-eth-for-future | 0.25 | 0 | default-futures | disposal-strat-1 | diff --git a/core/integration/features/liquidation/0012-POSR-012.feature b/core/integration/features/liquidation/0012-POSR-012.feature index a671211c734..137aba24788 100644 --- a/core/integration/features/liquidation/0012-POSR-012.feature +++ b/core/integration/features/liquidation/0012-POSR-012.feature @@ -1,9 +1,14 @@ Feature: 0012-POSR-012 Update the liquidation strategy through market update Background: - Given the markets: - | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | liquidation strategy | - | ETH/DEC19 | BTC | BTC | default-simple-risk-model-4 | default-margin-calculator | 1 | default-none | default-none | default-eth-for-future | 0.25 | 0 | default-futures | slow-liquidation-strat | + Given the liquidation strategies: + | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | + | slow-liquidation | 100 | 0.2 | 1 | 0.2 | 0.5 | + | fast-liquidation | 10 | 0.1 | 20 | 0.05 | 0.5 | + + And the markets: + | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | liquidation strategy | + | ETH/DEC19 | BTC | BTC | default-simple-risk-model-4 | default-margin-calculator | 1 | default-none | default-none | default-eth-for-future | 0.25 | 0 | default-futures | slow-liquidation | And the following network parameters are set: | name | value | | market.auction.minimumDuration | 1 | @@ -97,8 +102,8 @@ Feature: 0012-POSR-012 Update the liquidation strategy through market update | tt_10 | 100 | 1 | network | # Now update the market When the markets are updated: - | id | linear slippage factor | quadratic slippage factor | liquidation strategy | - | ETH/DEC19 | 0.25 | 0 | default-liquidation-strat | + | id | linear slippage factor | quadratic slippage factor | liquidation strategy | + | ETH/DEC19 | 0.25 | 0 | fast-liquidation | # Now the network should dispose of its entire position When the network moves ahead "11" blocks Then the parties should have the following profit and loss: diff --git a/core/integration/features/verified/network_disposing_position_outside_bounds.feature b/core/integration/features/verified/network_disposing_position_outside_bounds.feature index dd716b083da..5a30951f1ac 100644 --- a/core/integration/features/verified/network_disposing_position_outside_bounds.feature +++ b/core/integration/features/verified/network_disposing_position_outside_bounds.feature @@ -16,9 +16,9 @@ Feature: Disposing position outside bounds # Configure the markets Given the liquidation strategies: - | name | disposal step | disposal fraction | full disposal size | max fraction consumed | - | liquidation-strat-1 | 1 | 0.5 | 0 | 1 | - | liquidation-strat-2 | 1 | 1 | 0 | 0.5 | + | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | + | liquidation-strat-1 | 1 | 0.5 | 0 | 1 | 0.1 | + | liquidation-strat-2 | 1 | 1 | 0 | 0.5 | 0.1 | And the price monitoring named "price-monitoring": | horizon | probability | auction extension | | 6200 | 0.99 | 5 | @@ -357,4 +357,4 @@ Feature: Disposing position outside bounds When the network moves ahead "1" blocks Then the parties should have the following profit and loss: | party | volume | unrealised pnl | realised pnl | - | network | 1 | 0 | 0 | \ No newline at end of file + | network | 1 | 0 | 0 | diff --git a/core/integration/features/verified/network_pnl.feature b/core/integration/features/verified/network_pnl.feature index f83815733c8..8e74f30cb3e 100644 --- a/core/integration/features/verified/network_pnl.feature +++ b/core/integration/features/verified/network_pnl.feature @@ -13,9 +13,9 @@ Feature: Profit and loss for network a running liquidation strategy # Configure the markets Given the liquidation strategies: - | name | disposal step | disposal fraction | full disposal size | max fraction consumed | - | liquidation-strat-1 | 3600 | 0.5 | 0 | 1 | - | liquidation-strat-2 | 5 | 0.5 | 0 | 1 | + | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | + | liquidation-strat-1 | 3600 | 0.5 | 0 | 1 | 0.1 | + | liquidation-strat-2 | 5 | 0.5 | 0 | 1 | 0.1 | And the markets: | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | liquidation strategy | sla params | diff --git a/core/integration/features/verified/network_pnl_funding.feature b/core/integration/features/verified/network_pnl_funding.feature index 1268913c3c1..f7df129a0b0 100644 --- a/core/integration/features/verified/network_pnl_funding.feature +++ b/core/integration/features/verified/network_pnl_funding.feature @@ -15,8 +15,8 @@ Feature: Network profit and loss consideres funding | name | asset | settlement property | settlement type | schedule property | schedule type | margin funding factor | interest rate | clamp lower bound | clamp upper bound | quote name | settlement decimals | | perp-oracle | ETH | perp.ETH.value | TYPE_INTEGER | perp.funding.cue | TYPE_TIMESTAMP | 0.9 | 0.1 | 0 | 0 | ETH | 0 | Given the liquidation strategies: - | name | disposal step | disposal fraction | full disposal size | max fraction consumed | - | liquidation-strat-1 | 3600 | 0.5 | 0 | 1 | + | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | + | liquidation-strat-1 | 3600 | 0.5 | 0 | 1 | 0.1 | And the markets: | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | liquidation strategy | sla params | market type | | ETH/MAR22 | ETH | ETH | default-log-normal-risk-model | default-margin-calculator | 1 | default-none | default-none | perp-oracle | 0.001 | 0 | liquidation-strat-1 | default-basic | perp | diff --git a/core/integration/features/verified/network_position_disposal.feature b/core/integration/features/verified/network_position_disposal.feature index 753e29ffb4c..8fdb500de8b 100644 --- a/core/integration/features/verified/network_position_disposal.feature +++ b/core/integration/features/verified/network_position_disposal.feature @@ -12,10 +12,11 @@ Feature: Network disposing position | id | decimal places | quantum | | USD.0.10 | 0 | 10 | + # disposal strat 1 set to 0.5 for scenario at line 244 Given the liquidation strategies: - | name | disposal step | disposal fraction | full disposal size | max fraction consumed | - | disposal-strat-1 | 5 | 0.2 | 0 | 0.1 | - | disposal-strat-2 | 5 | 0.2 | 5 | 0.1 | + | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | + | disposal-strat-1 | 5 | 0.2 | 0 | 0.1 | 0.5 | + | disposal-strat-2 | 5 | 0.2 | 5 | 0.1 | 0.1 | And the log normal risk model named "log-normal-risk-model-1": | risk aversion | tau | mu | r | sigma | diff --git a/core/integration/steps/market/defaults/liquidation-config/AC-013-strat.json b/core/integration/steps/market/defaults/liquidation-config/AC-013-strat.json index bf1aa2d901e..6d93f5c820f 100644 --- a/core/integration/steps/market/defaults/liquidation-config/AC-013-strat.json +++ b/core/integration/steps/market/defaults/liquidation-config/AC-013-strat.json @@ -2,5 +2,6 @@ "disposal_time_step": 10, "disposal_fraction": "0.5", "full_disposal_size": 50, - "max_fraction_consumed": "0.01" + "max_fraction_consumed": "0.01", + "disposal_slippage_range": "0.1" } diff --git a/core/integration/steps/market/defaults/liquidation-config/default-liquidation-strat.json b/core/integration/steps/market/defaults/liquidation-config/default-liquidation-strat.json index f3479abb0d8..b588c887942 100644 --- a/core/integration/steps/market/defaults/liquidation-config/default-liquidation-strat.json +++ b/core/integration/steps/market/defaults/liquidation-config/default-liquidation-strat.json @@ -2,5 +2,6 @@ "disposal_time_step": 10, "disposal_fraction": "0.1", "full_disposal_size": 20, - "max_fraction_consumed": "0.05" + "max_fraction_consumed": "0.05", + "disposal_slippage_range": "0.1" } diff --git a/core/integration/steps/market/defaults/liquidation-config/legacy-liquidation-strategy.json b/core/integration/steps/market/defaults/liquidation-config/legacy-liquidation-strategy.json index d1e1b7ff1a2..c4326c07d37 100644 --- a/core/integration/steps/market/defaults/liquidation-config/legacy-liquidation-strategy.json +++ b/core/integration/steps/market/defaults/liquidation-config/legacy-liquidation-strategy.json @@ -2,5 +2,6 @@ "disposal_time_step": 0, "disposal_fraction": "1", "full_disposal_size": 18446744073709551615, - "max_fraction_consumed": "1" + "max_fraction_consumed": "1", + "disposal_slippage_range": "10" } diff --git a/core/integration/steps/market/defaults/liquidation-config/slow-liquidation-strat.json b/core/integration/steps/market/defaults/liquidation-config/slow-liquidation-strat.json index ca90c3c07e4..b8728b29785 100644 --- a/core/integration/steps/market/defaults/liquidation-config/slow-liquidation-strat.json +++ b/core/integration/steps/market/defaults/liquidation-config/slow-liquidation-strat.json @@ -2,6 +2,7 @@ "disposal_time_step": 100, "disposal_fraction": "0.2", "full_disposal_size": 1, - "max_fraction_consumed": "0.2" + "max_fraction_consumed": "0.2", + "disposal_slippage_range": "0.1" } diff --git a/core/integration/steps/the_liquidation_strategy.go b/core/integration/steps/the_liquidation_strategy.go index 1f9dce0ef23..4ce274dafde 100644 --- a/core/integration/steps/the_liquidation_strategy.go +++ b/core/integration/steps/the_liquidation_strategy.go @@ -43,6 +43,7 @@ func parseLiquidationStrategyTable(table *godog.Table) []RowWrapper { "disposal fraction", "full disposal size", "max fraction consumed", + "disposal slippage range", }, nil) } @@ -56,6 +57,7 @@ func (l lsRow) liquidationStrategy() *types.LiquidationStrategy { DisposalFraction: l.disposalFraction(), FullDisposalSize: l.fullDisposalSize(), MaxFractionConsumed: l.maxFraction(), + DisposalSlippage: l.disposalSlippage(), } } @@ -79,3 +81,7 @@ func (l lsRow) fullDisposalSize() uint64 { func (l lsRow) maxFraction() num.Decimal { return l.r.MustDecimal("max fraction consumed") } + +func (l lsRow) disposalSlippage() num.Decimal { + return l.r.MustDecimal("disposal slippage range") +} diff --git a/core/types/liquidation.go b/core/types/liquidation.go index 429f0d64ec3..5d8959a6886 100644 --- a/core/types/liquidation.go +++ b/core/types/liquidation.go @@ -124,11 +124,16 @@ func LiquidationStrategyFromProto(p *vegapb.LiquidationStrategy) (*LiquidationSt } func (l *LiquidationStrategy) IntoProto() *vegapb.LiquidationStrategy { + slip := "" + if !l.DisposalSlippage.IsZero() { + slip = l.DisposalSlippage.String() + } return &vegapb.LiquidationStrategy{ - DisposalTimeStep: int64(l.DisposalTimeStep / time.Second), - DisposalFraction: l.DisposalFraction.String(), - FullDisposalSize: l.FullDisposalSize, - MaxFractionConsumed: l.MaxFractionConsumed.String(), + DisposalTimeStep: int64(l.DisposalTimeStep / time.Second), + DisposalFraction: l.DisposalFraction.String(), + FullDisposalSize: l.FullDisposalSize, + MaxFractionConsumed: l.MaxFractionConsumed.String(), + DisposalSlippageRange: slip, } } From 790eba68bc4c1ffbfbb98d9bf543fdfeab25af79 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 12:36:32 +0100 Subject: [PATCH 03/14] test: tweak integration, fix unit tests after adding validation to the new parameter Signed-off-by: Elias Van Ootegem --- commands/proposal_submission.go | 4 + .../proposal_submission_new_market_test.go | 94 ++++++++++++------- core/governance/engine_new_market_test.go | 7 ++ core/governance/engine_test.go | 3 + core/governance/market.go | 3 + .../liquidation/0012-POSR-012.feature | 5 +- .../slow-liquidation-strat.json | 2 +- 7 files changed, 82 insertions(+), 36 deletions(-) diff --git a/commands/proposal_submission.go b/commands/proposal_submission.go index 7b08a896cab..05c1b510ce7 100644 --- a/commands/proposal_submission.go +++ b/commands/proposal_submission.go @@ -1084,6 +1084,10 @@ func checkLiquidationStrategy(params *protoTypes.LiquidationStrategy, parent str } else if params.DisposalTimeStep > 3600 { errs.AddForProperty(fmt.Sprintf("%s.liquidation_strategy.disposal_time_step", parent), ErrMustBeAtMost3600) } + slippage, err := num.DecimalFromString(params.DisposalSlippageRange) + if err != nil || slippage.IsNegative() || slippage.IsZero() { + errs.AddForProperty(fmt.Sprintf("%s.liquidation_strategy.disposal_slippage_range", parent), ErrMustBePositive) + } return errs } diff --git a/commands/proposal_submission_new_market_test.go b/commands/proposal_submission_new_market_test.go index bc46a9bf7d9..461e5e9a102 100644 --- a/commands/proposal_submission_new_market_test.go +++ b/commands/proposal_submission_new_market_test.go @@ -6152,10 +6152,11 @@ func testFutureMarketSubmissionWithValidLiquidationStrategySucceeds(t *testing.T }, }, LiquidationStrategy: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 20, - DisposalFraction: "0.05", - FullDisposalSize: 20, - MaxFractionConsumed: "0.01", + DisposalTimeStep: 20, + DisposalFraction: "0.05", + FullDisposalSize: 20, + MaxFractionConsumed: "0.01", + DisposalSlippageRange: "0.1", }, }, }, @@ -6166,6 +6167,7 @@ func testFutureMarketSubmissionWithValidLiquidationStrategySucceeds(t *testing.T assert.Empty(t, err.Get("proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_fraction")) assert.Empty(t, err.Get("proposal_submission.terms.change.new_market.changes.liquidation_strategy.max_fraction_consumed")) assert.Empty(t, err.Get("proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_time_step")) + assert.Empty(t, err.Get("proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_slippage_range")) } type compositePriceConfigCase struct { @@ -6587,10 +6589,11 @@ func testFutureMarketSubmissionWithInvalidLiquidationStrategyFails(t *testing.T) }, }, LiquidationStrategy: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 20, - DisposalFraction: "0.05", - FullDisposalSize: 20, - MaxFractionConsumed: "0.01", + DisposalTimeStep: 20, + DisposalFraction: "0.05", + FullDisposalSize: 20, + MaxFractionConsumed: "0.01", + DisposalSlippageRange: "0.1", }, }, }, @@ -6604,28 +6607,41 @@ func testFutureMarketSubmissionWithInvalidLiquidationStrategyFails(t *testing.T) }{ "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_fraction": { ls: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 20, - DisposalFraction: "123", - FullDisposalSize: 20, - MaxFractionConsumed: "0.01", + DisposalTimeStep: 20, + DisposalFraction: "123", + FullDisposalSize: 20, + MaxFractionConsumed: "0.01", + DisposalSlippageRange: "0.1", }, err: commands.ErrMustBeBetween01, }, "proposal_submission.terms.change.new_market.changes.liquidation_strategy.max_fraction_consumed": { ls: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 20, - DisposalFraction: "0.1", - FullDisposalSize: 20, - MaxFractionConsumed: "-0.1", + DisposalTimeStep: 20, + DisposalFraction: "0.1", + FullDisposalSize: 20, + MaxFractionConsumed: "-0.1", + DisposalSlippageRange: "100", // large values are fine }, err: commands.ErrMustBeBetween01, }, "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_time_step": { ls: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 0, - DisposalFraction: "0.1", - FullDisposalSize: 20, - MaxFractionConsumed: "0.1", + DisposalTimeStep: 0, + DisposalFraction: "0.1", + FullDisposalSize: 20, + MaxFractionConsumed: "0.1", + DisposalSlippageRange: "0.5", + }, + err: commands.ErrMustBePositive, + }, + "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_slippage_range": { + ls: &vegapb.LiquidationStrategy{ + DisposalTimeStep: 5, + DisposalFraction: "0.1", + FullDisposalSize: 20, + MaxFractionConsumed: "0.1", + DisposalSlippageRange: "-0.5", }, err: commands.ErrMustBePositive, }, @@ -6634,6 +6650,7 @@ func testFutureMarketSubmissionWithInvalidLiquidationStrategyFails(t *testing.T) "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_fraction", "proposal_submission.terms.change.new_market.changes.liquidation_strategy.max_fraction_consumed", "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_time_step", + "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_slippage_range", } for ec, exp := range data { nm := submission.Terms.GetNewMarket() @@ -6659,31 +6676,44 @@ func testFutureMarketSubmissionWithInvalidLiquidationStrategyFails(t *testing.T) }{ "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_fraction": { ls: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 20, - DisposalFraction: "-2", - FullDisposalSize: 20, - MaxFractionConsumed: "0.01", + DisposalTimeStep: 20, + DisposalFraction: "-2", + FullDisposalSize: 20, + MaxFractionConsumed: "0.01", + DisposalSlippageRange: "0.5", }, err: commands.ErrMustBeBetween01, }, "proposal_submission.terms.change.new_market.changes.liquidation_strategy.max_fraction_consumed": { ls: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 20, - DisposalFraction: "0.1", - FullDisposalSize: 20, - MaxFractionConsumed: "2", + DisposalTimeStep: 20, + DisposalFraction: "0.1", + FullDisposalSize: 20, + MaxFractionConsumed: "2", + DisposalSlippageRange: "0.5", }, err: commands.ErrMustBeBetween01, }, "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_time_step": { ls: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 3601, - DisposalFraction: "0.1", - FullDisposalSize: 20, - MaxFractionConsumed: "0.1", + DisposalTimeStep: 3601, + DisposalFraction: "0.1", + FullDisposalSize: 20, + MaxFractionConsumed: "0.1", + DisposalSlippageRange: "0.5", }, err: commands.ErrMustBeAtMost3600, }, + "proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_slippage_range": { + ls: &vegapb.LiquidationStrategy{ + DisposalTimeStep: 5, + DisposalFraction: "0.1", + FullDisposalSize: 20, + MaxFractionConsumed: "0.1", + DisposalSlippageRange: "0", // zero or missing values fail, too + }, + err: commands.ErrMustBePositive, + }, } for ec, exp := range data { nm := submission.Terms.GetNewMarket() diff --git a/core/governance/engine_new_market_test.go b/core/governance/engine_new_market_test.go index 5698a86a175..6fbd9c5c415 100644 --- a/core/governance/engine_new_market_test.go +++ b/core/governance/engine_new_market_test.go @@ -474,6 +474,7 @@ func testSubmittingProposalWithInternalTimeSettlingForNewMarketFails(t *testing. DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, @@ -555,6 +556,7 @@ func testSubmittingProposalWithEmptySettlingDataForNewMarketFails(t *testing.T) DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, @@ -645,6 +647,7 @@ func testSubmittingProposalWithEmptyTerminationDataForNewMarketFails(t *testing. DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, @@ -760,6 +763,7 @@ func testSubmittingProposalWithInternalTimeTerminationWithLessThanEqualCondition DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, @@ -830,6 +834,7 @@ func testSubmittingProposalWithInternalTimeTerminationWithLessThanEqualCondition DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, @@ -965,6 +970,7 @@ func testSubmittingProposalWithInternalTimeTriggerTerminationFails(t *testing.T) DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, @@ -1066,6 +1072,7 @@ func testSubmittingProposalWithInternalTimeTriggerSettlementFails(t *testing.T) DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, diff --git a/core/governance/engine_test.go b/core/governance/engine_test.go index 873992cafa8..6e35d338a6b 100644 --- a/core/governance/engine_test.go +++ b/core/governance/engine_test.go @@ -1628,6 +1628,7 @@ func newMarketTerms(termFilter *dstypes.SpecFilter, termBinding *datasource.Spec DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.NewUint(1), }, @@ -1718,6 +1719,7 @@ func newPerpsMarketTerms(termFilter *dstypes.SpecFilter, binding *datasource.Spe DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, @@ -1911,6 +1913,7 @@ func updateMarketTerms(termFilter *dstypes.SpecFilter, termBinding *datasource.S DisposalFraction: num.DecimalFromFloat(0.1), FullDisposalSize: 20, MaxFractionConsumed: num.DecimalFromFloat(0.01), + DisposalSlippage: num.DecimalFromFloat(0.1), }, TickSize: num.UintOne(), }, diff --git a/core/governance/market.go b/core/governance/market.go index 5b32d961a8b..a3509939ff5 100644 --- a/core/governance/market.go +++ b/core/governance/market.go @@ -705,6 +705,9 @@ func validateLiquidationStrategy(ls *types.LiquidationStrategy) (types.ProposalE } else if ls.DisposalTimeStep > time.Hour { return types.ProposalErrorInvalidMarket, fmt.Errorf("liquidation strategy time step can't be more than 1h") } + if ls.DisposalSlippage.IsZero() || ls.DisposalSlippage.IsNegative() { + return types.ProposalErrorInvalidMarket, fmt.Errorf("liquidation strategy must specify a disposal slippage range > 1") + } return types.ProposalErrorUnspecified, nil } diff --git a/core/integration/features/liquidation/0012-POSR-012.feature b/core/integration/features/liquidation/0012-POSR-012.feature index 137aba24788..45b002f8e6d 100644 --- a/core/integration/features/liquidation/0012-POSR-012.feature +++ b/core/integration/features/liquidation/0012-POSR-012.feature @@ -3,12 +3,11 @@ Feature: 0012-POSR-012 Update the liquidation strategy through market update Background: Given the liquidation strategies: | name | disposal step | disposal fraction | full disposal size | max fraction consumed | disposal slippage range | - | slow-liquidation | 100 | 0.2 | 1 | 0.2 | 0.5 | | fast-liquidation | 10 | 0.1 | 20 | 0.05 | 0.5 | And the markets: - | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | liquidation strategy | - | ETH/DEC19 | BTC | BTC | default-simple-risk-model-4 | default-margin-calculator | 1 | default-none | default-none | default-eth-for-future | 0.25 | 0 | default-futures | slow-liquidation | + | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | liquidation strategy | + | ETH/DEC19 | BTC | BTC | default-simple-risk-model-4 | default-margin-calculator | 1 | default-none | default-none | default-eth-for-future | 0.25 | 0 | default-futures | slow-liquidation-strat | And the following network parameters are set: | name | value | | market.auction.minimumDuration | 1 | diff --git a/core/integration/steps/market/defaults/liquidation-config/slow-liquidation-strat.json b/core/integration/steps/market/defaults/liquidation-config/slow-liquidation-strat.json index b8728b29785..7b158419faf 100644 --- a/core/integration/steps/market/defaults/liquidation-config/slow-liquidation-strat.json +++ b/core/integration/steps/market/defaults/liquidation-config/slow-liquidation-strat.json @@ -3,6 +3,6 @@ "disposal_fraction": "0.2", "full_disposal_size": 1, "max_fraction_consumed": "0.2", - "disposal_slippage_range": "0.1" + "disposal_slippage_range": "0.5" } From 9b0a8de3f55797cd3d08bf1159d1db53efe15c93 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 12:46:22 +0100 Subject: [PATCH 04/14] test: add coverage for proposal disposal slippage validation Signed-off-by: Elias Van Ootegem --- core/governance/engine_new_market_test.go | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/governance/engine_new_market_test.go b/core/governance/engine_new_market_test.go index 6fbd9c5c415..f1098922354 100644 --- a/core/governance/engine_new_market_test.go +++ b/core/governance/engine_new_market_test.go @@ -57,6 +57,7 @@ func TestProposalForNewMarket(t *testing.T) { t.Run("Submitting a duplicated proposal with internal time termination for new market fails", testSubmittingDuplicatedProposalWithInternalTimeTerminationForNewMarketFails) t.Run("Submitting a proposal for new market with bad risk parameter fails", testSubmittingProposalForNewMarketWithBadRiskParameterFails) t.Run("Submitting a proposal for new market with internal time termination with bad risk parameter fails", testSubmittingProposalForNewMarketWithInternalTimeTerminationWithBadRiskParameterFails) + t.Run("Submitting a proposal for a ne market without disposal slippage range fails", testSubmittingProposalWithoutDisposalSlippageFails) t.Run("Rejecting a proposal for new market succeeds", testRejectingProposalForNewMarketSucceeds) @@ -1233,6 +1234,31 @@ func testSubmittingProposalForNewMarketWithInternalTimeTerminationWithBadRiskPar assert.Contains(t, err.Error(), "invalid risk parameter") } +func testSubmittingProposalWithoutDisposalSlippageFails(t *testing.T) { + eng := getTestEngine(t, time.Now()) + // given + party := eng.newValidParty("a-valid-party", 1) + eng.ensureAllAssetEnabled(t) + + proposal := eng.newProposalForNewMarket(party.Id, eng.tsvc.GetTimeNow().Add(2*time.Hour), nil, nil, false) + proposal.Terms.GetNewMarket().Changes.LiquidationStrategy = &types.LiquidationStrategy{ + DisposalTimeStep: 10, + DisposalFraction: num.DecimalFromFloat(0.2), + FullDisposalSize: 10, + MaxFractionConsumed: num.DecimalFromFloat(0.5), + } + + // setup + eng.broker.EXPECT().Send(gomock.Any()).Times(1) + + // when + _, err := eng.submitProposal(t, proposal) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "liquidation strategy time step has to be 1s or more") +} + func testOutOfRangeRiskParamFail(t *testing.T, lnm *types.LogNormalRiskModel) { t.Helper() eng := getTestEngine(t, time.Now()) From 0555e41aa3dcd206f428c0da287c17c53de93850 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 12:56:42 +0100 Subject: [PATCH 05/14] chore: comment out scenario without implementation Signed-off-by: Elias Van Ootegem --- core/integration/features/orders/stoporders_spot.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/integration/features/orders/stoporders_spot.feature b/core/integration/features/orders/stoporders_spot.feature index b63d303dbe6..0053bcbb910 100644 --- a/core/integration/features/orders/stoporders_spot.feature +++ b/core/integration/features/orders/stoporders_spot.feature @@ -1258,7 +1258,7 @@ Feature: stop orders | party1 | USDT/ETH | STATUS_PENDING | stop3 | | party1 | USDT/ETH | STATUS_PENDING | stop4 | - Scenario: An OCO stop order with expiration time T with both sides set to execute at that time will be rejected on submission (0014-ORDT-176) + #Scenario: An OCO stop order with expiration time T with both sides set to execute at that time will be rejected on submission (0014-ORDT-176) # # setup accounts # Given time is updated to "2019-11-30T00:00:00Z" # Given the parties deposit on asset's general account the following amount: @@ -1395,4 +1395,4 @@ Feature: stop orders # | party2 | BTC/ETH | STATUS_STOPPED | stop2-2 | # | party2 | BTC/ETH | STATUS_EXPIRED | stop2-1 | - \ No newline at end of file + From 6e8ab48e36b316494c29b12f4bd7d6437557ad32 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 13:20:46 +0100 Subject: [PATCH 06/14] chore: review Signed-off-by: Elias Van Ootegem --- core/execution/liquidation/engine.go | 11 ++++------- core/governance/engine_new_market_test.go | 4 ++-- core/governance/market.go | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/core/execution/liquidation/engine.go b/core/execution/liquidation/engine.go index 3448e7db939..a9479ee1dde 100644 --- a/core/execution/liquidation/engine.go +++ b/core/execution/liquidation/engine.go @@ -85,9 +85,6 @@ var ( MaxFractionConsumed: num.DecimalOne(), DisposalSlippage: num.DecimalFromFloat(10.0), } - - // just because we use it for min-max - dOne = num.DecimalFromFloat(1.0) ) // GetDefaultStrat is exporeted, expected to be used to update existing proposals on protocol upgrade @@ -136,14 +133,14 @@ func (e *Engine) OnTick(ctx context.Context, now time.Time, midPrice *num.Uint) return nil, nil } - // get the min/max price from the range based on slippage paramter + // get the min/max price from the range based on slippage parameter mpDec := num.DecimalFromUint(midPrice) minP := num.UintZero() - if e.cfg.DisposalSlippage.LessThan(dOne) { - minD := mpDec.Mul(dOne.Sub(e.cfg.DisposalSlippage)) + if e.cfg.DisposalSlippage.LessThan(num.DecimalZero()) { + minD := mpDec.Mul(num.DecimalZero().Sub(e.cfg.DisposalSlippage)) minP, _ = num.UintFromDecimal(minD) } - maxD := mpDec.Mul(dOne.Add(e.cfg.DisposalSlippage)) + maxD := mpDec.Mul(num.DecimalZero().Add(e.cfg.DisposalSlippage)) maxP, _ := num.UintFromDecimal(maxD) minB, maxB := e.pmon.GetValidPriceRange() diff --git a/core/governance/engine_new_market_test.go b/core/governance/engine_new_market_test.go index f1098922354..60f41707749 100644 --- a/core/governance/engine_new_market_test.go +++ b/core/governance/engine_new_market_test.go @@ -1242,7 +1242,7 @@ func testSubmittingProposalWithoutDisposalSlippageFails(t *testing.T) { proposal := eng.newProposalForNewMarket(party.Id, eng.tsvc.GetTimeNow().Add(2*time.Hour), nil, nil, false) proposal.Terms.GetNewMarket().Changes.LiquidationStrategy = &types.LiquidationStrategy{ - DisposalTimeStep: 10, + DisposalTimeStep: time.Second * 10, DisposalFraction: num.DecimalFromFloat(0.2), FullDisposalSize: 10, MaxFractionConsumed: num.DecimalFromFloat(0.5), @@ -1256,7 +1256,7 @@ func testSubmittingProposalWithoutDisposalSlippageFails(t *testing.T) { // then require.Error(t, err) - assert.Contains(t, err.Error(), "liquidation strategy time step has to be 1s or more") + assert.Contains(t, err.Error(), "liquidation strategy must specify a disposal slippage range > 0") } func testOutOfRangeRiskParamFail(t *testing.T, lnm *types.LogNormalRiskModel) { diff --git a/core/governance/market.go b/core/governance/market.go index a3509939ff5..8f9378a3b28 100644 --- a/core/governance/market.go +++ b/core/governance/market.go @@ -706,7 +706,7 @@ func validateLiquidationStrategy(ls *types.LiquidationStrategy) (types.ProposalE return types.ProposalErrorInvalidMarket, fmt.Errorf("liquidation strategy time step can't be more than 1h") } if ls.DisposalSlippage.IsZero() || ls.DisposalSlippage.IsNegative() { - return types.ProposalErrorInvalidMarket, fmt.Errorf("liquidation strategy must specify a disposal slippage range > 1") + return types.ProposalErrorInvalidMarket, fmt.Errorf("liquidation strategy must specify a disposal slippage range > 0") } return types.ProposalErrorUnspecified, nil } From 0b7819fe2faab15304ee99d5689efc222bc370ce Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 13:41:17 +0100 Subject: [PATCH 07/14] test: fix type unit tests Signed-off-by: Elias Van Ootegem --- core/types/governance_new_market_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/types/governance_new_market_test.go b/core/types/governance_new_market_test.go index ecd8510b40c..e8fc923325a 100644 --- a/core/types/governance_new_market_test.go +++ b/core/types/governance_new_market_test.go @@ -160,10 +160,11 @@ func TestNewMarketProposalMapping(t *testing.T) { SlaCompetitionFactor: "0.5", }, LiquidationStrategy: &vegapb.LiquidationStrategy{ - DisposalTimeStep: 300, - DisposalFraction: "0.1", - FullDisposalSize: 20, - MaxFractionConsumed: "0.01", + DisposalTimeStep: 300, + DisposalFraction: "0.1", + FullDisposalSize: 20, + MaxFractionConsumed: "0.01", + DisposalSlippageRange: "0.1", }, TickSize: "1", }, From b93764a0c8df68057d6ea5aa830843d4d68dd147 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 14:22:05 +0100 Subject: [PATCH 08/14] chore: review fixes Signed-off-by: Elias Van Ootegem --- core/execution/liquidation/engine.go | 9 +++++---- protos/sources/vega/markets.proto | 2 +- protos/vega/markets.pb.go | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/execution/liquidation/engine.go b/core/execution/liquidation/engine.go index a9479ee1dde..71884c8c4e0 100644 --- a/core/execution/liquidation/engine.go +++ b/core/execution/liquidation/engine.go @@ -129,18 +129,19 @@ func (e *Engine) Update(cfg *types.LiquidationStrategy) { } func (e *Engine) OnTick(ctx context.Context, now time.Time, midPrice *num.Uint) (*types.Order, error) { - if e.pos.open == 0 || e.as.InAuction() || e.nextStep.After(now) { + if e.pos.open == 0 || e.as.InAuction() || e.nextStep.After(now) || midPrice.IsZero() { return nil, nil } + one := num.DecimalOne() // get the min/max price from the range based on slippage parameter mpDec := num.DecimalFromUint(midPrice) minP := num.UintZero() - if e.cfg.DisposalSlippage.LessThan(num.DecimalZero()) { - minD := mpDec.Mul(num.DecimalZero().Sub(e.cfg.DisposalSlippage)) + if e.cfg.DisposalSlippage.LessThan(one) { + minD := mpDec.Mul(one.Sub(e.cfg.DisposalSlippage)) minP, _ = num.UintFromDecimal(minD) } - maxD := mpDec.Mul(num.DecimalZero().Add(e.cfg.DisposalSlippage)) + maxD := mpDec.Mul(one.Add(e.cfg.DisposalSlippage)) maxP, _ := num.UintFromDecimal(maxD) minB, maxB := e.pmon.GetValidPriceRange() diff --git a/protos/sources/vega/markets.proto b/protos/sources/vega/markets.proto index 4a801fb3727..5335d966c70 100644 --- a/protos/sources/vega/markets.proto +++ b/protos/sources/vega/markets.proto @@ -414,7 +414,7 @@ message LiquidationStrategy { // Max fraction of the total volume of the orderbook, within liquidity bounds, that the network can use to close its position; range 0 through 1. string max_fraction_consumed = 4; // Decimal > 0 defining the price range within which the network will trade to dispose of its position. Retroactively defaults to 0.1 (10%). - // The value can be > 1, if set to 1.5, the minimum price will be 0 (min(0, mid_price * (1 - 1.))), the maximum price will be mid_price * (1 + 1.5). + // The value can be > 1, if set to 1.5, the minimum price will be 0 (max(0, mid_price * (1 - 1.5))), the maximum price will be mid_price * (1 + 1.5). string disposal_slippage_range = 5; } diff --git a/protos/vega/markets.pb.go b/protos/vega/markets.pb.go index 99e0f6c93c6..f5ffbcfc665 100644 --- a/protos/vega/markets.pb.go +++ b/protos/vega/markets.pb.go @@ -2334,7 +2334,7 @@ type LiquidationStrategy struct { // Max fraction of the total volume of the orderbook, within liquidity bounds, that the network can use to close its position; range 0 through 1. MaxFractionConsumed string `protobuf:"bytes,4,opt,name=max_fraction_consumed,json=maxFractionConsumed,proto3" json:"max_fraction_consumed,omitempty"` // Decimal > 0 defining the price range within which the network will trade to dispose of its position. Retroactively defaults to 0.1 (10%). - // The value can be > 1, if set to 1.5, the minimum price will be 0 (min(0, mid_price * (1 - 1.))), the maximum price will be mid_price * (1 + 1.5). + // The value can be > 1, if set to 1.5, the minimum price will be 0 (max(0, mid_price * (1 - 1.5))), the maximum price will be mid_price * (1 + 1.5). DisposalSlippageRange string `protobuf:"bytes,5,opt,name=disposal_slippage_range,json=disposalSlippageRange,proto3" json:"disposal_slippage_range,omitempty"` } From e7827146e36c2d441cdc2529d20d7bd0904e6cd2 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 14:24:58 +0100 Subject: [PATCH 09/14] fix: remove duplicate mid-price func Signed-off-by: Elias Van Ootegem --- core/execution/future/liquidation.go | 2 +- core/execution/future/market.go | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/core/execution/future/liquidation.go b/core/execution/future/liquidation.go index b430083db9d..2f3cb0b3733 100644 --- a/core/execution/future/liquidation.go +++ b/core/execution/future/liquidation.go @@ -30,7 +30,7 @@ func (m *Market) checkNetwork(ctx context.Context, now time.Time) error { return nil } // this only returns an error if we couldn't get the price range, incidating no orders on book - order, _ := m.liquidation.OnTick(ctx, now, m.getMarketMidPrice()) + order, _ := m.liquidation.OnTick(ctx, now, m.midPrice()) if order == nil { return nil } diff --git a/core/execution/future/market.go b/core/execution/future/market.go index ed3c8611b52..302d486c90e 100644 --- a/core/execution/future/market.go +++ b/core/execution/future/market.go @@ -709,21 +709,6 @@ func (m *Market) midPrice() *num.Uint { return midPrice } -func (m *Market) getMarketMidPrice() *num.Uint { - mp := num.UintZero() - if m.as.InAuction() { - return mp - } - bb, _, _ := m.matching.BestBidPriceAndVolume() - if bb.IsZero() { - return mp - } - if bo, _, _ := m.matching.BestOfferPriceAndVolume(); !bo.IsZero() { - return mp.Div(num.Sum(bb, bo), num.NewUint(2)) - } - return mp -} - func (m *Market) GetMarketData() types.MarketData { bestBidPrice, bestBidVolume, _ := m.matching.BestBidPriceAndVolume() bestOfferPrice, bestOfferVolume, _ := m.matching.BestOfferPriceAndVolume() From a587816267cc1c025d448d21a695b7104abf1585 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 15:43:42 +0100 Subject: [PATCH 10/14] feat: add migration checks and events to governance engine Signed-off-by: Elias Van Ootegem --- core/execution/future/market_snapshot.go | 3 ++ core/governance/snapshot.go | 51 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/core/execution/future/market_snapshot.go b/core/execution/future/market_snapshot.go index 30fbb7314af..7d5209c5f51 100644 --- a/core/execution/future/market_snapshot.go +++ b/core/execution/future/market_snapshot.go @@ -186,6 +186,9 @@ func NewMarketFromSnapshot( // this can be removed once this parameter is no longer optional if mkt.LiquidationStrategy == nil { mkt.LiquidationStrategy = liquidation.GetLegacyStrat() + } else if mkt.LiquidationStrategy.DisposalSlippage.IsZero() { + // @TODO check for migration from v0.75.8, strictly speaking, not doing so should have the same effect, though... + mkt.LiquidationStrategy.DisposalSlippage = mkt.LiquiditySLAParams.PriceRange } le := liquidation.New(log, mkt.LiquidationStrategy, mkt.GetID(), broker, book, as, timeService, positionEngine, pMonitor) diff --git a/core/governance/snapshot.go b/core/governance/snapshot.go index 7f7d5532016..706d5c42415 100644 --- a/core/governance/snapshot.go +++ b/core/governance/snapshot.go @@ -21,6 +21,7 @@ import ( "code.vegaprotocol.io/vega/core/events" "code.vegaprotocol.io/vega/core/types" + vgcontext "code.vegaprotocol.io/vega/libs/context" "code.vegaprotocol.io/vega/libs/num" "code.vegaprotocol.io/vega/libs/proto" "code.vegaprotocol.io/vega/logging" @@ -74,6 +75,30 @@ func (e *Engine) OnStateLoaded(ctx context.Context) error { } } + // update market events may require updating to set the liquidation strategy slippage + if vgcontext.InProgressUpgradeFrom(ctx, "v0.75.8") { + evts := make([]events.Event, 0, len(e.activeProposals)/2) + for _, p := range e.activeProposals { + if !p.Proposal.IsMarketUpdate() { + continue + } + mID := p.Proposal.MarketUpdate().MarketID + changes := p.Proposal.MarketUpdate().Changes + if changes.LiquidationStrategy != nil && changes.LiquidationStrategy.DisposalSlippage.IsZero() { + existingMarket, ok := e.markets.GetMarket(mID, false) + if !ok { + continue + } + // execution engine has already been restored at this point, so we can get the current slippage value from the market itself. + changes.LiquidationStrategy.DisposalSlippage = existingMarket.LiquidationStrategy.DisposalSlippage + evts = append(evts, events.NewProposalEvent(ctx, *p.Proposal)) + } + } + if len(evts) > 0 { + e.broker.SendBatch(evts) + } + } + return nil } @@ -252,6 +277,11 @@ func (e *Engine) restoreActiveProposals(ctx context.Context, active *types.Gover vevts := []events.Event{} e.log.Debug("restoring active proposals snapshot", logging.Int("nproposals", len(active.Proposals))) for _, p := range active.Proposals { + if vgcontext.InProgressUpgradeFrom(ctx, "v0.75.8") { + if p.Proposal.IsNewMarket() || p.Proposal.IsMarketUpdate() { + setLiquidationSlippage(p.Proposal) + } + } pp := &proposal{ Proposal: p.Proposal, yes: votesAsMap(p.Yes), @@ -287,6 +317,22 @@ func (e *Engine) restoreActiveProposals(ctx context.Context, active *types.Gover return err } +func setLiquidationSlippage(p *types.Proposal) { + if p.IsNewMarket() { + if !p.NewMarket().Changes.LiquidationStrategy.DisposalSlippage.IsZero() { + return + } + changes := p.NewMarket().Changes + changes.LiquidationStrategy.DisposalSlippage = changes.LiquiditySLAParameters.PriceRange + return + } + // this must be a market update + changes := p.MarketUpdate().Changes + if changes.LiquidationStrategy != nil && changes.LiquiditySLAParameters != nil && changes.LiquidationStrategy.DisposalSlippage.IsZero() { + changes.LiquidationStrategy.DisposalSlippage = changes.LiquiditySLAParameters.PriceRange + } +} + func (e *Engine) restoreBatchActiveProposals(ctx context.Context, active *types.GovernanceBatchActive, p *types.Payload) error { e.activeBatchProposals = make(map[string]*batchProposal, len(active.BatchProposals)) @@ -304,6 +350,11 @@ func (e *Engine) restoreBatchActiveProposals(ctx context.Context, active *types. evts = append(evts, events.NewProposalEventFromProto(ctx, bp.BatchProposal.ToProto())) for _, p := range bp.BatchProposal.Proposals { + if vgcontext.InProgressUpgradeFrom(ctx, "v0.75.8") { + if p.IsMarketUpdate() || p.IsNewMarket() { + setLiquidationSlippage(p) + } + } evts = append(evts, events.NewProposalEvent(ctx, *p)) } From 7ebde91ac08543f85aacee43aead7b906645e0d2 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 15:53:58 +0100 Subject: [PATCH 11/14] feat: emit update events on upgrade, add types to datanode and GQL Signed-off-by: Elias Van Ootegem --- core/execution/future/market.go | 5 ++ datanode/entities/market.go | 15 +++-- datanode/gateway/graphql/generated.go | 73 +++++++++++++++++++++++-- datanode/gateway/graphql/schema.graphql | 2 + 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/core/execution/future/market.go b/core/execution/future/market.go index 302d486c90e..8bc3fdd715e 100644 --- a/core/execution/future/market.go +++ b/core/execution/future/market.go @@ -963,6 +963,11 @@ func (m *Market) PostRestore(ctx context.Context) error { } } + // Disposal slippage was set as part of this upgrade, send event to ensure datanode is updated. + if vegacontext.InProgressUpgradeFrom(ctx, "v0.75.8") { + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) + } + return nil } diff --git a/datanode/entities/market.go b/datanode/entities/market.go index 20fb0d3f76f..dba19da6030 100644 --- a/datanode/entities/market.go +++ b/datanode/entities/market.go @@ -363,6 +363,7 @@ type LiquidationStrategy struct { DisposalFraction num.Decimal `json:"disposalFraction"` FullDisposalSize uint64 `json:"fullDisposalSize"` MaxFractionConsumed num.Decimal `json:"maxFractionConsumed"` + DisposalSlippage num.Decimal `json:"disposalSlippageRange"` } func LiquidationStrategyFromProto(ls *vega.LiquidationStrategy) LiquidationStrategy { @@ -371,20 +372,26 @@ func LiquidationStrategyFromProto(ls *vega.LiquidationStrategy) LiquidationStrat } df, _ := num.DecimalFromString(ls.DisposalFraction) mfc, _ := num.DecimalFromString(ls.MaxFractionConsumed) + slip := num.DecimalZero() + if len(ls.DisposalSlippageRange) > 0 { + slip, _ = num.DecimalFromString(ls.DisposalSlippageRange) + } return LiquidationStrategy{ DisposalTimeStep: time.Duration(ls.DisposalTimeStep) * time.Second, FullDisposalSize: ls.FullDisposalSize, DisposalFraction: df, MaxFractionConsumed: mfc, + DisposalSlippage: slip, } } func (l LiquidationStrategy) IntoProto() *vega.LiquidationStrategy { return &vega.LiquidationStrategy{ - DisposalTimeStep: int64(l.DisposalTimeStep / time.Second), - DisposalFraction: l.DisposalFraction.String(), - FullDisposalSize: l.FullDisposalSize, - MaxFractionConsumed: l.MaxFractionConsumed.String(), + DisposalTimeStep: int64(l.DisposalTimeStep / time.Second), + DisposalFraction: l.DisposalFraction.String(), + FullDisposalSize: l.FullDisposalSize, + MaxFractionConsumed: l.MaxFractionConsumed.String(), + DisposalSlippageRange: l.DisposalSlippage.String(), } } diff --git a/datanode/gateway/graphql/generated.go b/datanode/gateway/graphql/generated.go index 4207624718b..48a6cd9e534 100644 --- a/datanode/gateway/graphql/generated.go +++ b/datanode/gateway/graphql/generated.go @@ -1017,10 +1017,11 @@ type ComplexityRoot struct { } LiquidationStrategy struct { - DisposalFraction func(childComplexity int) int - DisposalTimeStep func(childComplexity int) int - FullDisposalSize func(childComplexity int) int - MaxFractionConsumed func(childComplexity int) int + DisposalFraction func(childComplexity int) int + DisposalSlippageRange func(childComplexity int) int + DisposalTimeStep func(childComplexity int) int + FullDisposalSize func(childComplexity int) int + MaxFractionConsumed func(childComplexity int) int } LiquidityFeeSettings struct { @@ -7171,6 +7172,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.LiquidationStrategy.DisposalFraction(childComplexity), true + case "LiquidationStrategy.disposalSlippageRange": + if e.complexity.LiquidationStrategy.DisposalSlippageRange == nil { + break + } + + return e.complexity.LiquidationStrategy.DisposalSlippageRange(childComplexity), true + case "LiquidationStrategy.disposalTimeStep": if e.complexity.LiquidationStrategy.DisposalTimeStep == nil { break @@ -42219,6 +42227,50 @@ func (ec *executionContext) fieldContext_LiquidationStrategy_maxFractionConsumed return fc, nil } +func (ec *executionContext) _LiquidationStrategy_disposalSlippageRange(ctx context.Context, field graphql.CollectedField, obj *vega.LiquidationStrategy) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LiquidationStrategy_disposalSlippageRange(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DisposalSlippageRange, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LiquidationStrategy_disposalSlippageRange(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LiquidationStrategy", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _LiquidityFeeSettings_method(ctx context.Context, field graphql.CollectedField, obj *vega.LiquidityFeeSettings) (ret graphql.Marshaler) { fc, err := ec.fieldContext_LiquidityFeeSettings_method(ctx, field) if err != nil { @@ -49130,6 +49182,8 @@ func (ec *executionContext) fieldContext_Market_liquidationStrategy(ctx context. return ec.fieldContext_LiquidationStrategy_fullDisposalSize(ctx, field) case "maxFractionConsumed": return ec.fieldContext_LiquidationStrategy_maxFractionConsumed(ctx, field) + case "disposalSlippageRange": + return ec.fieldContext_LiquidationStrategy_disposalSlippageRange(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type LiquidationStrategy", field.Name) }, @@ -54161,6 +54215,8 @@ func (ec *executionContext) fieldContext_NewMarket_liquidationStrategy(ctx conte return ec.fieldContext_LiquidationStrategy_fullDisposalSize(ctx, field) case "maxFractionConsumed": return ec.fieldContext_LiquidationStrategy_maxFractionConsumed(ctx, field) + case "disposalSlippageRange": + return ec.fieldContext_LiquidationStrategy_disposalSlippageRange(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type LiquidationStrategy", field.Name) }, @@ -98344,6 +98400,8 @@ func (ec *executionContext) fieldContext_UpdateMarketConfiguration_liquidationSt return ec.fieldContext_LiquidationStrategy_fullDisposalSize(ctx, field) case "maxFractionConsumed": return ec.fieldContext_LiquidationStrategy_maxFractionConsumed(ctx, field) + case "disposalSlippageRange": + return ec.fieldContext_LiquidationStrategy_disposalSlippageRange(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type LiquidationStrategy", field.Name) }, @@ -112046,6 +112104,13 @@ func (ec *executionContext) _LiquidationStrategy(ctx context.Context, sel ast.Se out.Values[i] = ec._LiquidationStrategy_maxFractionConsumed(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "disposalSlippageRange": + + out.Values[i] = ec._LiquidationStrategy_disposalSlippageRange(ctx, field, obj) + if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } diff --git a/datanode/gateway/graphql/schema.graphql b/datanode/gateway/graphql/schema.graphql index fab1e871acb..6b4ee6ac71b 100644 --- a/datanode/gateway/graphql/schema.graphql +++ b/datanode/gateway/graphql/schema.graphql @@ -5052,6 +5052,8 @@ type LiquidationStrategy { fullDisposalSize: Int! "Specifies the maximum size by which the network can reduce its position as a fraction of the volume on the book." maxFractionConsumed: String! + "Specifies the slippage relative to the mid prige within which the network will place orders to dispose of its position." + disposalSlippageRange: String! } "Representation of a network parameter" From fcdbccf4fb9d222462a9eca1c009518e99dc7402 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 16:28:34 +0100 Subject: [PATCH 12/14] test: update datanode unit tests Signed-off-by: Elias Van Ootegem --- datanode/networkhistory/service_test.go | 12 ++++++------ datanode/sqlstore/markets_test.go | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/datanode/networkhistory/service_test.go b/datanode/networkhistory/service_test.go index 70571cbfd83..e41016fe3cc 100644 --- a/datanode/networkhistory/service_test.go +++ b/datanode/networkhistory/service_test.go @@ -379,12 +379,12 @@ func TestMain(t *testing.M) { log.Infof("%s", goldenSourceHistorySegment[4000].HistorySegmentID) log.Infof("%s", goldenSourceHistorySegment[5000].HistorySegmentID) - panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[1000].HistorySegmentID, "QmbpJgGrF77bRNDFfJGvLpnUZ8pNHjyxNrDKsjj1qHRRVT", snapshots) - panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[2000].HistorySegmentID, "QmQEkwKQeLAaEeDjTRzYcSHwfqzHPceVXMsF2w2aBYMJuS", snapshots) - panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[2500].HistorySegmentID, "QmRfbQDZzJKBbaiDZpSvjbVv3NNN5aGkWrcpo3fZTEfYv4", snapshots) - panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[3000].HistorySegmentID, "QmQbtFz37HMrM5rgKmQ2Em4YmbADj7bgCFDBDjj1wy5KQS", snapshots) - panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[4000].HistorySegmentID, "QmUWDmXwPxkyg7ZaXhg6znZUSyndB35u8okMV1MhQYSSz8", snapshots) - panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[5000].HistorySegmentID, "QmYdufkLtNRYDA4zmbFirtkVuNG4z4Hs1hidjpKRhcjsN5", snapshots) + panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[1000].HistorySegmentID, "QmRM4ANS2j6wShYt9SS2njiPT1JWhZFWJCgTqxt27xz2ys", snapshots) + panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[2000].HistorySegmentID, "QmVHoDCYhP8cLqpjZKgGtKVLQdHCuqAkumiWmNNkqjZyqs", snapshots) + panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[2500].HistorySegmentID, "QmYC6vk2VKFjkZ46MhYYPMF3RMqoN2pSMucN3vZHRYWBus", snapshots) + panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[3000].HistorySegmentID, "QmeU5kmTSFUdx5G3QYdN3Wuo9LdDake1T5pb7ui12Em5Bq", snapshots) + panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[4000].HistorySegmentID, "QmQ7ezyBxdQJDRoHgz4ZyFw5iDhzQY5E9hegZrtJocZtXX", snapshots) + panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[5000].HistorySegmentID, "Qmf9KNqAg4XHs8vMozBeCsDLLG1v6MDzKLebonuhK7ksaq", snapshots) }, postgresRuntimePath, sqlFs) if exitCode != 0 { diff --git a/datanode/sqlstore/markets_test.go b/datanode/sqlstore/markets_test.go index c630cce4f89..21728049d3f 100644 --- a/datanode/sqlstore/markets_test.go +++ b/datanode/sqlstore/markets_test.go @@ -552,10 +552,11 @@ func getTestMarket() *vega.Market { func getTestFutureMarketWithLiquidationStrategy(termInt bool) *vega.Market { mkt := getTestFutureMarket(termInt) mkt.LiquidationStrategy = &vega.LiquidationStrategy{ - DisposalTimeStep: 10, - DisposalFraction: "0.1", - FullDisposalSize: 20, - MaxFractionConsumed: "0.01", + DisposalTimeStep: 10, + DisposalFraction: "0.1", + FullDisposalSize: 20, + MaxFractionConsumed: "0.01", + DisposalSlippageRange: "0.1", } return mkt } @@ -1519,10 +1520,11 @@ func setupSuccessorMarkets(t *testing.T, ctx context.Context) (*sqlstore.Markets ts := sqlstore.NewParties(connectionSource) emptyLS := &vega.LiquidationStrategy{ - DisposalTimeStep: 0, - DisposalFraction: "0", - FullDisposalSize: 0, - MaxFractionConsumed: "0", + DisposalTimeStep: 0, + DisposalFraction: "0", + FullDisposalSize: 0, + MaxFractionConsumed: "0", + DisposalSlippageRange: "0", } liquidationStrat := entities.LiquidationStrategyFromProto(emptyLS) parentMarket := entities.Market{ From 979105472ad8f2c6621c8a2bfe656ecce4b8fd37 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 17:15:35 +0100 Subject: [PATCH 13/14] fix: account for both mainnet and fairground versions Signed-off-by: Elias Van Ootegem --- core/execution/future/market.go | 2 +- core/governance/snapshot.go | 6 +++--- libs/context/context.go | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/core/execution/future/market.go b/core/execution/future/market.go index 8bc3fdd715e..f23838e5bc9 100644 --- a/core/execution/future/market.go +++ b/core/execution/future/market.go @@ -964,7 +964,7 @@ func (m *Market) PostRestore(ctx context.Context) error { } // Disposal slippage was set as part of this upgrade, send event to ensure datanode is updated. - if vegacontext.InProgressUpgradeFrom(ctx, "v0.75.8") { + if vegacontext.InProgressUpgradeFromMultiple(ctx, "v0.75.8", "v0.75.7") { m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) } diff --git a/core/governance/snapshot.go b/core/governance/snapshot.go index 706d5c42415..6272e61a66a 100644 --- a/core/governance/snapshot.go +++ b/core/governance/snapshot.go @@ -76,7 +76,7 @@ func (e *Engine) OnStateLoaded(ctx context.Context) error { } // update market events may require updating to set the liquidation strategy slippage - if vgcontext.InProgressUpgradeFrom(ctx, "v0.75.8") { + if vgcontext.InProgressUpgradeFromMultiple(ctx, "v0.75.8", "v0.75.7") { evts := make([]events.Event, 0, len(e.activeProposals)/2) for _, p := range e.activeProposals { if !p.Proposal.IsMarketUpdate() { @@ -277,7 +277,7 @@ func (e *Engine) restoreActiveProposals(ctx context.Context, active *types.Gover vevts := []events.Event{} e.log.Debug("restoring active proposals snapshot", logging.Int("nproposals", len(active.Proposals))) for _, p := range active.Proposals { - if vgcontext.InProgressUpgradeFrom(ctx, "v0.75.8") { + if vgcontext.InProgressUpgradeFromMultiple(ctx, "v0.75.8", "v0.75.7") { if p.Proposal.IsNewMarket() || p.Proposal.IsMarketUpdate() { setLiquidationSlippage(p.Proposal) } @@ -350,7 +350,7 @@ func (e *Engine) restoreBatchActiveProposals(ctx context.Context, active *types. evts = append(evts, events.NewProposalEventFromProto(ctx, bp.BatchProposal.ToProto())) for _, p := range bp.BatchProposal.Proposals { - if vgcontext.InProgressUpgradeFrom(ctx, "v0.75.8") { + if vgcontext.InProgressUpgradeFromMultiple(ctx, "v0.75.8", "v0.75.7") { if p.IsMarketUpdate() || p.IsNewMarket() { setLiquidationSlippage(p) } diff --git a/libs/context/context.go b/libs/context/context.go index 8c7538145d4..9a5033a53ad 100644 --- a/libs/context/context.go +++ b/libs/context/context.go @@ -170,6 +170,26 @@ func InProgressUpgradeFrom(ctx context.Context, from string) bool { return from == si.version && si.upgrade } +// InProgressUpgradeFromMultiple returns whether the data in the contexts tells us that the +// node is restoring from a snapshot taken for a protocol-upgrade by one of the given versions. +// This can be useful in situations where mainnet and fairground are running different versions. +func InProgressUpgradeFromMultiple(ctx context.Context, versions ...string) bool { + v := ctx.Value(snapshotKey) + if v == nil { + return false + } + si, ok := v.(snapshotInfo) + if !ok || !si.upgrade { + return false + } + for _, from := range versions { + if from == si.version { + return true + } + } + return false +} + // InProgressUpgrade returns whether the data in the contexts tells us that the // node is restoring from a snapshot taken for a protocol-upgrade. func InProgressUpgrade(ctx context.Context) bool { From bb0a4d147e4820f29891c76d5429fe6dceb5fdcf Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Thu, 18 Apr 2024 17:24:19 +0100 Subject: [PATCH 14/14] chore: clarify docs in proto Signed-off-by: Elias Van Ootegem --- protos/sources/vega/markets.proto | 4 ++-- protos/vega/markets.pb.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/protos/sources/vega/markets.proto b/protos/sources/vega/markets.proto index 5335d966c70..0e2a322377f 100644 --- a/protos/sources/vega/markets.proto +++ b/protos/sources/vega/markets.proto @@ -413,8 +413,8 @@ message LiquidationStrategy { uint64 full_disposal_size = 3; // Max fraction of the total volume of the orderbook, within liquidity bounds, that the network can use to close its position; range 0 through 1. string max_fraction_consumed = 4; - // Decimal > 0 defining the price range within which the network will trade to dispose of its position. Retroactively defaults to 0.1 (10%). - // The value can be > 1, if set to 1.5, the minimum price will be 0 (max(0, mid_price * (1 - 1.5))), the maximum price will be mid_price * (1 + 1.5). + // Decimal > 0 specifying the range range above and below the mid price within which the network will trade to dispose of its position. + // The value can be > 1. For example, if set to 1.5, the minimum price will be 0, ie max(0, mid_price * (1 - 1.5)), and the maximum price will be mid_price * (1 + 1.5). string disposal_slippage_range = 5; } diff --git a/protos/vega/markets.pb.go b/protos/vega/markets.pb.go index f5ffbcfc665..89664c3f5bc 100644 --- a/protos/vega/markets.pb.go +++ b/protos/vega/markets.pb.go @@ -2333,8 +2333,8 @@ type LiquidationStrategy struct { FullDisposalSize uint64 `protobuf:"varint,3,opt,name=full_disposal_size,json=fullDisposalSize,proto3" json:"full_disposal_size,omitempty"` // Max fraction of the total volume of the orderbook, within liquidity bounds, that the network can use to close its position; range 0 through 1. MaxFractionConsumed string `protobuf:"bytes,4,opt,name=max_fraction_consumed,json=maxFractionConsumed,proto3" json:"max_fraction_consumed,omitempty"` - // Decimal > 0 defining the price range within which the network will trade to dispose of its position. Retroactively defaults to 0.1 (10%). - // The value can be > 1, if set to 1.5, the minimum price will be 0 (max(0, mid_price * (1 - 1.5))), the maximum price will be mid_price * (1 + 1.5). + // Decimal > 0 specifying the range range above and below the mid price within which the network will trade to dispose of its position. + // The value can be > 1. For example, if set to 1.5, the minimum price will be 0, ie max(0, mid_price * (1 - 1.5)), and the maximum price will be mid_price * (1 + 1.5). DisposalSlippageRange string `protobuf:"bytes,5,opt,name=disposal_slippage_range,json=disposalSlippageRange,proto3" json:"disposal_slippage_range,omitempty"` }