Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Liquidation strategy #10006

Merged
merged 11 commits into from
Nov 7, 2023
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### 🚨 Breaking changes

- [](https://github.com/vegaprotocol/vega/issues/xxxx) -
- [9945](https://github.com/vegaprotocol/vega/issues/9945) - Add liquidation strategy.

### 🗑️ Deprecation

Expand Down
1 change: 1 addition & 0 deletions commands/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ var (
ErrMustBeLessThen366 = errors.New("must be less then 366")
ErrMustBeAtMost500 = errors.New("must be at most 500")
ErrMustBeSetTo0IfSizeSet = errors.New("must be set to 0 if the property \"order_amendment.size\" is set")
ErrMustBeAtMost3600 = errors.New("must be at most 3600")
ErrMustBeWithinRangeGT0LT20 = errors.New("price range must be strictly greater than 0 and less than or equal to 20")
)

Expand Down
25 changes: 25 additions & 0 deletions commands/proposal_submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,7 @@ func checkNewMarketChanges(change *protoTypes.ProposalTerms_NewMarket) Errors {
}
}

errs.Merge(checkLiquidationStrategy(changes.LiquidationStrategy, "proposal_submission.terms.change.new_market.changes"))
errs.Merge(checkPriceMonitoring(changes.PriceMonitoringParameters, "proposal_submission.terms.change.new_market.changes"))
errs.Merge(checkLiquidityMonitoring(changes.LiquidityMonitoringParameters, "proposal_submission.terms.change.new_market.changes"))
errs.Merge(checkNewInstrument(changes.Instrument, "proposal_submission.terms.change.new_market.changes.instrument"))
Expand Down Expand Up @@ -904,6 +905,7 @@ func checkUpdateMarketChanges(change *protoTypes.ProposalTerms_UpdateMarket) Err
}
}

errs.Merge(checkLiquidationStrategy(changes.LiquidationStrategy, "proposal_submission.terms.change.update_market.changes"))
errs.Merge(checkPriceMonitoring(changes.PriceMonitoringParameters, "proposal_submission.terms.change.update_market.changes"))
errs.Merge(checkLiquidityMonitoring(changes.LiquidityMonitoringParameters, "proposal_submission.terms.change.update_market.changes"))
errs.Merge(checkUpdateInstrument(changes.Instrument))
Expand Down Expand Up @@ -975,6 +977,29 @@ func checkPriceMonitoring(parameters *protoTypes.PriceMonitoringParameters, pare
return errs
}

func checkLiquidationStrategy(params *protoTypes.LiquidationStrategy, parent string) Errors {
EVODelavega marked this conversation as resolved.
Show resolved Hide resolved
errs := NewErrors()
if params == nil {
// @TODO these will be required, in that case the check for nil should be removed
// or return an error.
return errs
}
dispFrac, err := num.DecimalFromString(params.DisposalFraction)
if err != nil || dispFrac.IsNegative() || dispFrac.IsZero() || dispFrac.GreaterThan(num.DecimalOne()) {
errs.AddForProperty(fmt.Sprintf("%s.liquidation_strategy.disposal_fraction", parent), ErrMustBeBetween01)
}
maxFrac, err := num.DecimalFromString(params.MaxFractionConsumed)
if err != nil || maxFrac.IsNegative() || maxFrac.IsZero() || maxFrac.GreaterThan(num.DecimalOne()) {
errs.AddForProperty(fmt.Sprintf("%s.liquidation_strategy.max_fraction_consumed", parent), ErrMustBeBetween01)
}
if params.DisposalTimeStep < 1 {
errs.AddForProperty(fmt.Sprintf("%s.liquidation_strategy.disposal_time_step", parent), ErrMustBePositive)
} else if params.DisposalTimeStep > 3600 {
errs.AddForProperty(fmt.Sprintf("%s.liquidation_strategy.disposal_time_step", parent), ErrMustBeAtMost3600)
}
return errs
}

func checkLiquidityMonitoring(parameters *protoTypes.LiquidityMonitoringParameters, parentProperty string) Errors {
errs := NewErrors()

Expand Down
257 changes: 257 additions & 0 deletions commands/proposal_submission_new_market_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ func TestCheckProposalSubmissionForNewMarket(t *testing.T) {
t.Run("Submitting a future market with external data sources for settlement and termination with no signers fail", testFutureMarketSubmissionWithExternalSettlementDataAndTerminationNoSignerFails)
t.Run("Submitting a future market with internal time trigger termination data fails", testFutureMarketSubmissionWithInternalTimeTriggerTerminationDataFails)
t.Run("Submitting a future market with internal time trigger settlement data fails", testFutureMarketSubmissionWithInternalTimeTriggerSettlementDataFails)
t.Run("Submitting a future market with valid liquidation strategy succeeds", testFutureMarketSubmissionWithValidLiquidationStrategySucceeds)
t.Run("Submitting a future market with invalid liquidation strategy fails", testFutureMarketSubmissionWithInvalidLiquidationStrategyFails)

t.Run("Submitting a perps market change without perps fails", testNewPerpsMarketChangeSubmissionWithoutPerpsFails)
t.Run("Submitting a perps market change with perps succeeds", testNewPerpsMarketChangeSubmissionWithPerpsSucceeds)
Expand Down Expand Up @@ -5916,3 +5918,258 @@ func testLiquidityFeeSettings(t *testing.T) {
assert.Contains(t, err.Get("proposal_submission.terms.change.new_market.changes.liquidity_fee_settings."+c.field), c.err)
}
}

func testFutureMarketSubmissionWithValidLiquidationStrategySucceeds(t *testing.T) {
pubKey := []*dstypes.Signer{
dstypes.CreateSignerFromString("bd069246503a57271375f1995c46e03db88c4e1a564077b33a9872f905650dc4", dstypes.SignerTypePubKey),
}

err := checkProposalSubmission(&commandspb.ProposalSubmission{
Terms: &vegapb.ProposalTerms{
Change: &vegapb.ProposalTerms_NewMarket{
NewMarket: &vegapb.NewMarket{
Changes: &vegapb.NewMarketConfiguration{
Instrument: &vegapb.InstrumentConfiguration{
Product: &vegapb.InstrumentConfiguration_Future{
Future: &vegapb.FutureProduct{
DataSourceSpecForSettlementData: vegapb.NewDataSourceDefinition(
vegapb.DataSourceContentTypeOracle,
).SetOracleConfig(
&vegapb.DataSourceDefinitionExternal_Oracle{
Oracle: &vegapb.DataSourceSpecConfiguration{
Signers: dstypes.SignersIntoProto(pubKey),
Filters: []*datapb.Filter{
{
Key: &datapb.PropertyKey{
Name: "prices.ETH.value",
Type: datapb.PropertyKey_TYPE_INTEGER,
},
Conditions: []*datapb.Condition{
{
Operator: datapb.Condition_OPERATOR_GREATER_THAN_OR_EQUAL,
},
},
},
},
},
},
),
DataSourceSpecForTradingTermination: vegapb.NewDataSourceDefinition(
vegapb.DataSourceContentTypeOracle,
).SetOracleConfig(
&vegapb.DataSourceDefinitionExternal_Oracle{
Oracle: &vegapb.DataSourceSpecConfiguration{
Signers: dstypes.SignersIntoProto(pubKey),
Filters: []*datapb.Filter{
{
Key: &datapb.PropertyKey{
Name: "vegaprotocol.builtin.timestamp",
Type: datapb.PropertyKey_TYPE_TIMESTAMP,
},
Conditions: []*datapb.Condition{
{
Operator: datapb.Condition_OPERATOR_GREATER_THAN_OR_EQUAL,
Value: fmt.Sprintf("%d", time.Now().Add(time.Hour*24*365).UnixNano()),
},
},
},
},
},
},
),
},
},
},
LiquidationStrategy: &vegapb.LiquidationStrategy{
DisposalTimeStep: 20,
DisposalFraction: "0.05",
FullDisposalSize: 20,
MaxFractionConsumed: "0.01",
},
},
},
},
},
})

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"))
}

func testFutureMarketSubmissionWithInvalidLiquidationStrategyFails(t *testing.T) {
pubKey := []*dstypes.Signer{
dstypes.CreateSignerFromString("bd069246503a57271375f1995c46e03db88c4e1a564077b33a9872f905650dc4", dstypes.SignerTypePubKey),
}

submission := &commandspb.ProposalSubmission{
Terms: &vegapb.ProposalTerms{
Change: &vegapb.ProposalTerms_NewMarket{
NewMarket: &vegapb.NewMarket{
Changes: &vegapb.NewMarketConfiguration{
Instrument: &vegapb.InstrumentConfiguration{
Product: &vegapb.InstrumentConfiguration_Future{
Future: &vegapb.FutureProduct{
DataSourceSpecForSettlementData: vegapb.NewDataSourceDefinition(
vegapb.DataSourceContentTypeOracle,
).SetOracleConfig(
&vegapb.DataSourceDefinitionExternal_Oracle{
Oracle: &vegapb.DataSourceSpecConfiguration{
Signers: dstypes.SignersIntoProto(pubKey),
Filters: []*datapb.Filter{
{
Key: &datapb.PropertyKey{
Name: "prices.ETH.value",
Type: datapb.PropertyKey_TYPE_INTEGER,
},
Conditions: []*datapb.Condition{
{
Operator: datapb.Condition_OPERATOR_GREATER_THAN_OR_EQUAL,
},
},
},
},
},
},
),
DataSourceSpecForTradingTermination: vegapb.NewDataSourceDefinition(
vegapb.DataSourceContentTypeOracle,
).SetOracleConfig(
&vegapb.DataSourceDefinitionExternal_Oracle{
Oracle: &vegapb.DataSourceSpecConfiguration{
Signers: dstypes.SignersIntoProto(pubKey),
Filters: []*datapb.Filter{
{
Key: &datapb.PropertyKey{
Name: "vegaprotocol.builtin.timestamp",
Type: datapb.PropertyKey_TYPE_TIMESTAMP,
},
Conditions: []*datapb.Condition{
{
Operator: datapb.Condition_OPERATOR_GREATER_THAN_OR_EQUAL,
Value: fmt.Sprintf("%d", time.Now().Add(time.Hour*24*365).UnixNano()),
},
},
},
},
},
},
),
},
},
},
LiquidationStrategy: &vegapb.LiquidationStrategy{
DisposalTimeStep: 20,
DisposalFraction: "0.05",
FullDisposalSize: 20,
MaxFractionConsumed: "0.01",
},
},
},
},
},
}

data := map[string]struct {
ls *vegapb.LiquidationStrategy
err error
}{
"proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_fraction": {
ls: &vegapb.LiquidationStrategy{
DisposalTimeStep: 20,
DisposalFraction: "123",
FullDisposalSize: 20,
MaxFractionConsumed: "0.01",
},
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",
},
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",
},
err: commands.ErrMustBePositive,
},
}
checks := []string{
"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",
}
for ec, exp := range data {
nm := submission.Terms.GetNewMarket()
nm.Changes.LiquidationStrategy = exp.ls
submission.Terms.Change = &vegapb.ProposalTerms_NewMarket{
NewMarket: nm,
}
err := checkProposalSubmission(submission)
for _, k := range checks {
if k != ec {
assert.Empty(t, err.Get(k))
} else {
assert.Contains(t, err.Get(k), exp.err)
}
}
}
// pretty much the same as above, only this time set the disposal fraction to a negative value
// and max fraction consumed to a large positive
// finally set the disposal time step to a large int value, this changes the error
data = map[string]struct {
ls *vegapb.LiquidationStrategy
err error
}{
"proposal_submission.terms.change.new_market.changes.liquidation_strategy.disposal_fraction": {
ls: &vegapb.LiquidationStrategy{
DisposalTimeStep: 20,
DisposalFraction: "-2",
FullDisposalSize: 20,
MaxFractionConsumed: "0.01",
},
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",
},
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",
},
err: commands.ErrMustBeAtMost3600,
},
}
for ec, exp := range data {
nm := submission.Terms.GetNewMarket()
nm.Changes.LiquidationStrategy = exp.ls
submission.Terms.Change = &vegapb.ProposalTerms_NewMarket{
NewMarket: nm,
}
err := checkProposalSubmission(submission)
for _, k := range checks {
if k != ec {
assert.Empty(t, err.Get(k))
} else {
assert.Contains(t, err.Get(k), exp.err)
}
}
}
}
Loading
Loading