Skip to content

Commit

Permalink
feat: add funding payment modifiers to perpetual definition
Browse files Browse the repository at this point in the history
  • Loading branch information
wwestgarth committed Nov 27, 2023
1 parent 1727b48 commit 0e10bb9
Show file tree
Hide file tree
Showing 19 changed files with 2,651 additions and 1,152 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- [9516](https://github.com/vegaprotocol/vega/issues/9516) - Add filter by transfer ID for ledger entries API.
- [9943](https://github.com/vegaprotocol/vega/issues/9943) - Support amending the order size by defining the target size.
- [9231](https://github.com/vegaprotocol/vega/issues/9231) - Add a `JoinTeam API`
- [10097](https://github.com/vegaprotocol/vega/issues/10097) - Add funding rate modifiers to perpetual product definition.
- [9981](https://github.com/vegaprotocol/vega/issues/9981) - Support filtering on epoch range on transfers.
- [9981](https://github.com/vegaprotocol/vega/issues/9981) - Support filtering on status on transfers.
- [10104](https://github.com/vegaprotocol/vega/issues/10104) - Add network position tracking.
Expand Down
165 changes: 140 additions & 25 deletions commands/proposal_submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -1075,7 +1075,7 @@ func checkNewInstrument(instrument *protoTypes.InstrumentConfiguration, parent s
case *protoTypes.InstrumentConfiguration_Future:
errs.Merge(checkNewFuture(product.Future))
case *protoTypes.InstrumentConfiguration_Perpetual:
errs.Merge(checkNewPerps(product.Perpetual))
errs.Merge(checkNewPerps(product.Perpetual, fmt.Sprintf("%s.product", parent)))
case *protoTypes.InstrumentConfiguration_Spot:
errs.Merge(checkNewSpot(product.Spot))
default:
Expand Down Expand Up @@ -1104,7 +1104,7 @@ func checkUpdateInstrument(instrument *protoTypes.UpdateInstrumentConfiguration)
case *protoTypes.UpdateInstrumentConfiguration_Future:
errs.Merge(checkUpdateFuture(product.Future))
case *protoTypes.UpdateInstrumentConfiguration_Perpetual:
errs.Merge(checkUpdatePerps(product.Perpetual))
errs.Merge(checkUpdatePerps(product.Perpetual, "proposal_submission.terms.change.update_market.changes.instrument.product"))
default:
return errs.FinalAddForProperty("proposal_submission.terms.change.update_market.changes.instrument.product", ErrIsNotValid)
}
Expand Down Expand Up @@ -1133,39 +1133,39 @@ func checkNewFuture(future *protoTypes.FutureProduct) Errors {
return errs
}

func checkNewPerps(perps *protoTypes.PerpetualProduct) Errors {
func checkNewPerps(perps *protoTypes.PerpetualProduct, parentProperty string) Errors {
errs := NewErrors()

if perps == nil {
return errs.FinalAddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps", ErrIsRequired)
return errs.FinalAddForProperty(fmt.Sprintf("%s.perps", parentProperty), ErrIsRequired)
}

if len(perps.SettlementAsset) == 0 {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.settlement_asset", ErrIsRequired)
errs.AddForProperty(fmt.Sprintf("%s.perps.settlement_asset", parentProperty), ErrIsRequired)
}
if len(perps.QuoteName) == 0 {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.quote_name", ErrIsRequired)
errs.AddForProperty(fmt.Sprintf("%s.perps.quote_name", parentProperty), ErrIsRequired)
}

if len(perps.MarginFundingFactor) <= 0 {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.margin_funding_factor", ErrIsRequired)
errs.AddForProperty(fmt.Sprintf("%s.perps.margin_funding_factor", parentProperty), ErrIsRequired)
} else {
mff, err := num.DecimalFromString(perps.MarginFundingFactor)
if err != nil {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.margin_funding_factor", ErrIsNotValidNumber)
errs.AddForProperty(fmt.Sprintf("%s.perps.margin_funding_factor", parentProperty), ErrIsNotValidNumber)
} else if mff.IsNegative() || mff.GreaterThan(num.DecimalOne()) {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.margin_funding_factor", ErrMustBeWithinRange01)
errs.AddForProperty(fmt.Sprintf("%s.perps.margin_funding_factor", parentProperty), ErrMustBeWithinRange01)
}
}

if len(perps.InterestRate) <= 0 {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.interest_rate", ErrIsRequired)
errs.AddForProperty(fmt.Sprintf("%s.perps.interest_rate", parentProperty), ErrIsRequired)
} else {
mff, err := num.DecimalFromString(perps.InterestRate)
if err != nil {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.interest_rate", ErrIsNotValidNumber)
errs.AddForProperty(fmt.Sprintf("%s.perps.interest_rate", parentProperty), ErrIsNotValidNumber)
} else if mff.LessThan(num.MustDecimalFromString("-1")) || mff.GreaterThan(num.DecimalOne()) {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.interest_rate", ErrMustBeWithinRange11)
errs.AddForProperty(fmt.Sprintf("%s.perps.interest_rate", parentProperty), ErrMustBeWithinRange11)
}
}

Expand All @@ -1176,39 +1176,67 @@ func checkNewPerps(perps *protoTypes.PerpetualProduct) Errors {
)

if len(perps.ClampLowerBound) <= 0 {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.clamp_lower_bound", ErrIsRequired)
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_lower_bound", parentProperty), ErrIsRequired)
} else {
clampLowerBound, err = num.DecimalFromString(perps.ClampLowerBound)
if err != nil {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.clamp_lower_bound", ErrIsNotValidNumber)
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_lower_bound", parentProperty), ErrIsNotValidNumber)
} else if clampLowerBound.LessThan(num.MustDecimalFromString("-1")) || clampLowerBound.GreaterThan(num.DecimalOne()) {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.clamp_lower_bound", ErrMustBeWithinRange11)
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_lower_bound", parentProperty), ErrMustBeWithinRange11)
} else {
okClampLowerBound = true
}
}

if len(perps.ClampUpperBound) <= 0 {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.clamp_upper_bound", ErrIsRequired)
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_upper_bound", parentProperty), ErrIsRequired)
} else {
clampUpperBound, err = num.DecimalFromString(perps.ClampUpperBound)
if err != nil {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.clamp_upper_bound", ErrIsNotValidNumber)
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_upper_bound", parentProperty), ErrIsNotValidNumber)
} else if clampUpperBound.LessThan(num.MustDecimalFromString("-1")) || clampUpperBound.GreaterThan(num.DecimalOne()) {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.clamp_upper_bound", ErrMustBeWithinRange11)
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_upper_bound", parentProperty), ErrMustBeWithinRange11)
} else {
okClampUpperBound = true
}
}

if okClampLowerBound && okClampUpperBound && clampUpperBound.LessThan(clampLowerBound) {
errs.AddForProperty("proposal_submission.terms.change.new_market.changes.instrument.product.perps.clamp_upper_bound", ErrMustBeGTEClampLowerBound)
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_upper_bound", parentProperty), ErrMustBeGTEClampLowerBound)
}

errs.Merge(checkDataSourceSpec(perps.DataSourceSpecForSettlementData, "data_source_spec_for_settlement_data", "proposal_submission.terms.change.new_market.changes.instrument.product.perps", true))
errs.Merge(checkDataSourceSpec(perps.DataSourceSpecForSettlementSchedule, "data_source_spec_for_settlement_schedule", "proposal_submission.terms.change.new_market.changes.instrument.product.perps", true))
errs.Merge(checkNewPerpsOracleBinding(perps))
if perps.FundingRateScalingFactor != nil {
sf, err := num.DecimalFromString(*perps.FundingRateScalingFactor)
if err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_scaling_factor", parentProperty), ErrIsNotValidNumber)
}
if !sf.IsPositive() {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_scaling_factor", parentProperty), ErrMustBePositive)
}
}

var lowerBound, upperBound num.Decimal
if perps.FundingRateLowerBound != nil {
if lowerBound, err = num.DecimalFromString(*perps.FundingRateLowerBound); err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_lower_bound", parentProperty), ErrIsNotValidNumber)
}
}

if perps.FundingRateUpperBound != nil {
if upperBound, err = num.DecimalFromString(*perps.FundingRateUpperBound); err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_upper_bound", parentProperty), ErrIsNotValidNumber)
}
}

if perps.FundingRateLowerBound != nil && perps.FundingRateUpperBound != nil {
if lowerBound.GreaterThan(upperBound) {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_lower_bound", parentProperty), ErrIsNotValid)
}
}

errs.Merge(checkDataSourceSpec(perps.DataSourceSpecForSettlementData, "data_source_spec_for_settlement_data", fmt.Sprintf("%s.perps", parentProperty), true))
errs.Merge(checkDataSourceSpec(perps.DataSourceSpecForSettlementSchedule, "data_source_spec_for_settlement_schedule", fmt.Sprintf("%s.perps", parentProperty), true))
errs.Merge(checkNewPerpsOracleBinding(perps))
return errs
}

Expand Down Expand Up @@ -1252,15 +1280,102 @@ func checkUpdateFuture(future *protoTypes.UpdateFutureProduct) Errors {
return errs
}

func checkUpdatePerps(perps *protoTypes.UpdatePerpetualProduct) Errors {
func checkUpdatePerps(perps *protoTypes.UpdatePerpetualProduct, parentProperty string) Errors {
errs := NewErrors()

if perps == nil {
return errs.FinalAddForProperty("proposal_submission.terms.change.update_market.changes.instrument.product.future", ErrIsRequired)
return errs.FinalAddForProperty(fmt.Sprintf("%s.perps", parentProperty), ErrIsRequired)
}

if len(perps.QuoteName) == 0 {
errs.AddForProperty("proposal_submission.terms.change.update_market.changes.instrument.product.future.quote_name", ErrIsRequired)
errs.AddForProperty(fmt.Sprintf("%s.perps.quote_name", parentProperty), ErrIsRequired)
}

if len(perps.MarginFundingFactor) <= 0 {
errs.AddForProperty(fmt.Sprintf("%s.perps.margin_funding_factor", parentProperty), ErrIsRequired)
} else {
mff, err := num.DecimalFromString(perps.MarginFundingFactor)
if err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.margin_funding_factor", parentProperty), ErrIsNotValidNumber)
} else if mff.IsNegative() || mff.GreaterThan(num.DecimalOne()) {
errs.AddForProperty(fmt.Sprintf("%s.perps.margin_funding_factor", parentProperty), ErrMustBeWithinRange01)
}
}

if len(perps.InterestRate) <= 0 {
errs.AddForProperty(fmt.Sprintf("%s.perps.interest_rate", parentProperty), ErrIsRequired)
} else {
mff, err := num.DecimalFromString(perps.InterestRate)
if err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.interest_rate", parentProperty), ErrIsNotValidNumber)
} else if mff.LessThan(num.MustDecimalFromString("-1")) || mff.GreaterThan(num.DecimalOne()) {
errs.AddForProperty(fmt.Sprintf("%s.perps.interest_rate", parentProperty), ErrMustBeWithinRange11)
}
}

var (
okClampLowerBound, okClampUpperBound bool
clampLowerBound, clampUpperBound num.Decimal
err error
)

if len(perps.ClampLowerBound) <= 0 {
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_lower_bound", parentProperty), ErrIsRequired)
} else {
clampLowerBound, err = num.DecimalFromString(perps.ClampLowerBound)
if err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_lower_bound", parentProperty), ErrIsNotValidNumber)
} else if clampLowerBound.LessThan(num.MustDecimalFromString("-1")) || clampLowerBound.GreaterThan(num.DecimalOne()) {
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_lower_bound", parentProperty), ErrMustBeWithinRange11)
} else {
okClampLowerBound = true
}
}

if len(perps.ClampUpperBound) <= 0 {
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_upper_bound", parentProperty), ErrIsRequired)
} else {
clampUpperBound, err = num.DecimalFromString(perps.ClampUpperBound)
if err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_upper_bound", parentProperty), ErrIsNotValidNumber)
} else if clampUpperBound.LessThan(num.MustDecimalFromString("-1")) || clampUpperBound.GreaterThan(num.DecimalOne()) {
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_upper_bound", parentProperty), ErrMustBeWithinRange11)
} else {
okClampUpperBound = true
}
}

if okClampLowerBound && okClampUpperBound && clampUpperBound.LessThan(clampLowerBound) {
errs.AddForProperty(fmt.Sprintf("%s.perps.clamp_upper_bound", parentProperty), ErrMustBeGTEClampLowerBound)
}

if perps.FundingRateScalingFactor != nil {
sf, err := num.DecimalFromString(*perps.FundingRateScalingFactor)
if err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_scaling_factor", parentProperty), ErrIsNotValidNumber)
}
if !sf.IsPositive() {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_scaling_factor", parentProperty), ErrMustBePositive)
}
}

var lowerBound, upperBound num.Decimal
if perps.FundingRateLowerBound != nil {
if lowerBound, err = num.DecimalFromString(*perps.FundingRateLowerBound); err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_lower_bound", parentProperty), ErrIsNotValidNumber)
}
}

if perps.FundingRateUpperBound != nil {
if upperBound, err = num.DecimalFromString(*perps.FundingRateUpperBound); err != nil {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_upper_bound", parentProperty), ErrIsNotValidNumber)
}
}

if perps.FundingRateLowerBound != nil && perps.FundingRateUpperBound != nil {
if lowerBound.GreaterThan(upperBound) {
errs.AddForProperty(fmt.Sprintf("%s.perps.funding_rate_lower_bound", parentProperty), ErrIsNotValid)
}
}

errs.Merge(checkDataSourceSpec(perps.DataSourceSpecForSettlementData, "data_source_spec_for_settlement_data", "proposal_submission.terms.change.update_market.changes.instrument.product.future", true))
Expand Down
100 changes: 100 additions & 0 deletions commands/proposal_submission_new_market_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func TestCheckProposalSubmissionForNewMarket(t *testing.T) {
t.Run("Submitting a perps market change with filter with condition value succeeds", testNewPerpsMarketChangeSubmissionWithFilterWithConditionValueSucceeds)
t.Run("Submitting a perps market change without oracle spec bindings fails", testNewPerpsMarketChangeSubmissionWithoutDataSourceSpecBindingFails)
t.Run("Submitting a perps market change with oracle spec binding succeeds", testNewPerpsMarketChangeSubmissionWithDataSourceSpecBindingSucceeds)
t.Run("Submitting a perps market with funding rate modifiers", testNewPerpsMarketWithFundingRateModifiers)
t.Run("Submitting a perps market change with a mismatch between binding property name and filter fails", testNewPerpsMarketChangeSubmissionWithMismatchBetweenFilterAndBindingFails)
t.Run("Submitting a perps market change with match between binding property name and filter succeeds", testNewPerpsMarketChangeSubmissionWithNoMismatchBetweenFilterAndBindingSucceeds)
t.Run("Submitting a perps market change with settlement data and trading termination properties succeeds", testNewPerpsMarketChangeSubmissionWithSettlementDataPropertySucceeds)
Expand Down Expand Up @@ -5214,6 +5215,105 @@ func TestNewPerpsMarketChangeSubmissionProductParameters(t *testing.T) {
}
}

func testNewPerpsMarketWithFundingRateModifiers(t *testing.T) {
cases := []struct {
product vegapb.PerpetualProduct
err error
path string
desc string
}{
{
product: vegapb.PerpetualProduct{
FundingRateScalingFactor: ptr.From("hello"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_scaling_factor",
err: commands.ErrIsNotValidNumber,
},
{
product: vegapb.PerpetualProduct{
FundingRateScalingFactor: ptr.From("-10"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_scaling_factor",
err: commands.ErrMustBePositive,
},
{
product: vegapb.PerpetualProduct{
FundingRateScalingFactor: ptr.From("0"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_scaling_factor",
err: commands.ErrMustBePositive,
},
{
product: vegapb.PerpetualProduct{
FundingRateScalingFactor: ptr.From("0.1"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_scaling_factor",
},
{
product: vegapb.PerpetualProduct{
FundingRateLowerBound: ptr.From("hello"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_lower_bound",
err: commands.ErrIsNotValidNumber,
},
{
product: vegapb.PerpetualProduct{
FundingRateLowerBound: ptr.From("-100"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_lower_bound",
},
{
product: vegapb.PerpetualProduct{
FundingRateUpperBound: ptr.From("hello"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_upper_bound",
err: commands.ErrIsNotValidNumber,
},
{
product: vegapb.PerpetualProduct{
FundingRateUpperBound: ptr.From("100"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_upper_bound",
},
{
product: vegapb.PerpetualProduct{
FundingRateUpperBound: ptr.From("100"),
FundingRateLowerBound: ptr.From("200"),
},
path: "proposal_submission.terms.change.new_market.changes.instrument.product.perps.funding_rate_lower_bound",
err: commands.ErrIsNotValid,
},
}

for _, v := range cases {
t.Run(v.desc, func(t *testing.T) {
err := checkProposalSubmission(&commandspb.ProposalSubmission{
Terms: &vegapb.ProposalTerms{
Change: &vegapb.ProposalTerms_NewMarket{
NewMarket: &vegapb.NewMarket{
Changes: &vegapb.NewMarketConfiguration{
Instrument: &vegapb.InstrumentConfiguration{
Product: &vegapb.InstrumentConfiguration_Perpetual{
Perpetual: &v.product,
},
},
},
},
},
},
})
errs := err.Get(v.path)

// no errors expected
if v.err == nil {
assert.Len(t, errs, 0, v.desc)
return
}
assert.Contains(t, errs, v.err, v.desc)
})
}
}

func TestNewPerpsMarketChangeSubmissionSettlementSchedule(t *testing.T) {
cases := []struct {
product vegapb.PerpetualProduct
Expand Down
Loading

0 comments on commit 0e10bb9

Please sign in to comment.