Skip to content

Commit

Permalink
Merge pull request #11152 from vegaprotocol/10995-liquidation-range
Browse files Browse the repository at this point in the history
10995 liquidation range
  • Loading branch information
EVODelavega authored Apr 18, 2024
2 parents 00e9795 + bb0a4d1 commit ad4121e
Show file tree
Hide file tree
Showing 38 changed files with 460 additions and 244 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions commands/proposal_submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
94 changes: 62 additions & 32 deletions commands/proposal_submission_new_market_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
},
Expand All @@ -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 {
Expand Down Expand Up @@ -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",
},
},
},
Expand All @@ -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,
},
Expand All @@ -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()
Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion core/execution/future/liquidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.midPrice())
if order == nil {
return nil
}
Expand Down
7 changes: 6 additions & 1 deletion core/execution/future/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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.InProgressUpgradeFromMultiple(ctx, "v0.75.8", "v0.75.7") {
m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt))
}

return nil
}

Expand Down
5 changes: 4 additions & 1 deletion core/execution/future/market_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,11 @@ 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, 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 {
Expand Down
29 changes: 16 additions & 13 deletions core/execution/liquidation/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
}
Expand All @@ -66,7 +62,6 @@ type Engine struct {
as common.AuctionState
nextStep time.Time
tSvc common.TimeService
ml MarketLiquidity
position Positions
stopped bool
pmon PriceMonitor
Expand All @@ -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).
Expand All @@ -87,6 +83,7 @@ var (
DisposalFraction: num.DecimalOne(),
FullDisposalSize: math.MaxUint64,
MaxFractionConsumed: num.DecimalOne(),
DisposalSlippage: num.DecimalFromFloat(10.0),
}
)

Expand All @@ -103,7 +100,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()
Expand All @@ -116,7 +113,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,
Expand All @@ -132,14 +128,21 @@ func (e *Engine) Update(cfg *types.LiquidationStrategy) {
e.cfg = cfg
}

func (e *Engine) OnTick(ctx context.Context, now time.Time) (*types.Order, error) {
if e.pos.open == 0 || e.as.InAuction() || e.nextStep.After(now) {
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) || midPrice.IsZero() {
return nil, nil
}
minP, maxP, err := e.ml.ValidOrdersPriceRange()
if err != nil {
return nil, err

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(one) {
minD := mpDec.Mul(one.Sub(e.cfg.DisposalSlippage))
minP, _ = num.UintFromDecimal(minD)
}
maxD := mpDec.Mul(one.Add(e.cfg.DisposalSlippage))
maxP, _ := num.UintFromDecimal(maxD)

minB, maxB := e.pmon.GetValidPriceRange()

Expand Down
Loading

0 comments on commit ad4121e

Please sign in to comment.