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

feat: add support for market tick size #10762

Merged
merged 7 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/xxx)
- [10635](https://github.com/vegaprotocol/vega/issues/10635) - Add support for tick size

### 🗑️ Deprecation

Expand Down
23 changes: 23 additions & 0 deletions commands/proposal_submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,7 @@ func checkNewSpotMarketConfiguration(changes *vegapb.NewSpotMarketConfiguration)
errs.Merge(checkNewInstrument(changes.Instrument, "new_spot_market.changes.instrument"))
errs.Merge(checkNewSpotRiskParameters(changes))
errs.Merge(checkSLAParams(changes.SlaParams, "new_spot_market.changes.sla_params"))
errs.Merge(checkTickSize(changes.TickSize, "new_spot_market.changes"))

return errs
}
Expand Down Expand Up @@ -918,6 +919,26 @@ func checkNewMarketChangesConfiguration(changes *vegapb.NewMarketConfiguration)
errs.Merge(checkSLAParams(changes.LiquiditySlaParameters, "new_market.changes.sla_params"))
errs.Merge(checkLiquidityFeeSettings(changes.LiquidityFeeSettings, "new_market.changes.liquidity_fee_settings"))
errs.Merge(checkCompositePriceConfiguration(changes.MarkPriceConfiguration, "new_market.changes.mark_price_configuration"))
errs.Merge(checkTickSize(changes.TickSize, "new_market.changes"))

return errs
}

func checkTickSize(tickSize string, parent string) Errors {
errs := NewErrors()

if len(tickSize) == 0 {
errs.AddForProperty(fmt.Sprintf("%s.tick_size", parent), ErrIsRequired)
return errs
}

tickSizeU, overflow := num.UintFromString(tickSize, 10)
if overflow {
errs.AddForProperty(fmt.Sprintf("%s.tick_size", parent), ErrNotAValidInteger)
} else if tickSizeU.IsZero() || tickSizeU.IsNegative() {
errs.AddForProperty(fmt.Sprintf("%s.tick_size", parent), ErrMustBePositive)
}

return errs
}

Expand Down Expand Up @@ -965,6 +986,7 @@ func checkUpdateMarket(updateMarket *vegapb.UpdateMarket) Errors {
errs.Merge(checkSLAParams(changes.LiquiditySlaParameters, "update_market.changes.sla_params"))
errs.Merge(checkLiquidityFeeSettings(changes.LiquidityFeeSettings, "update_market.changes.liquidity_fee_settings"))
errs.Merge(checkCompositePriceConfiguration(changes.MarkPriceConfiguration, "update_market.changes.mark_price_configuration"))
errs.Merge(checkTickSize(changes.TickSize, "update_market.changes"))
return errs
}

Expand Down Expand Up @@ -995,6 +1017,7 @@ func checkUpdateSpotMarket(updateSpotMarket *vegapb.UpdateSpotMarket) Errors {
errs.Merge(checkTargetStakeParams(changes.TargetStakeParameters, "update_spot_market.changes"))
errs.Merge(checkUpdateSpotRiskParameters(changes))
errs.Merge(checkSLAParams(changes.SlaParams, "update_spot_market.changes.sla_params"))
errs.Merge(checkTickSize(changes.TickSize, "update_spot_market.changes"))
return errs
}

Expand Down
39 changes: 39 additions & 0 deletions commands/proposal_submission_new_market_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,45 @@ func TestCheckProposalSubmissionForNewMarket(t *testing.T) {
t.Run("Submitting a new market with valid hysteresis epochs succeeds", testNewMarketChangeSubmissionWithValidPerformanceHysteresisEpochsSucceeds)
t.Run("Submitting a new market with invalid liquidity fee settings", testLiquidityFeeSettings)
t.Run("Submitting a new market with invalid mark price configuration ", testCompositePriceConfiguration)
t.Run("Submitting a new market with invalid tick size fails and with valid tick size succeeds", testNewMarketTickSize)
}

type tickSizeCase struct {
tickSize string
err error
}

func getTickSizeCases() []tickSizeCase {
return []tickSizeCase{
{tickSize: "", err: commands.ErrIsRequired},
{tickSize: "banana", err: commands.ErrNotAValidInteger},
{tickSize: "-1", err: commands.ErrMustBePositive},
{tickSize: "0", err: commands.ErrMustBePositive},
{tickSize: "1", err: nil},
{tickSize: "123", err: nil},
}
}

func testNewMarketTickSize(t *testing.T) {
cases := getTickSizeCases()
for _, tsc := range cases {
err := checkProposalSubmission(&commandspb.ProposalSubmission{
Terms: &vegapb.ProposalTerms{
Change: &vegapb.ProposalTerms_NewMarket{
NewMarket: &vegapb.NewMarket{
Changes: &vegapb.NewMarketConfiguration{
TickSize: tsc.tickSize,
},
},
},
},
})
if tsc.err != nil {
assert.Contains(t, err.Get("proposal_submission.terms.change.new_market.changes.tick_size"), tsc.err)
} else {
assert.Empty(t, err.Get("proposal_submission.terms.change.new_market.changes.tick_size"))
}
}
}

func testNewMarketChangeSubmissionWithoutNewMarketFails(t *testing.T) {
Expand Down
27 changes: 27 additions & 0 deletions commands/proposal_submission_new_spot_market_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,33 @@ func TestCheckProposalSubmissionForNewSpotMarket(t *testing.T) {
t.Run("Submitting a new spot market with valid competition factor succeeds", testNewSpotMarketChangeSubmissionWithValidCompetitionFactorSucceeds)
t.Run("Submitting a new spot market with invalid hysteresis epochs fails", testNewSpotMarketChangeSubmissionWithInvalidPerformanceHysteresisEpochsFails)
t.Run("Submitting a new spot market with valid hysteresis epochs succeeds", testNewSpotMarketChangeSubmissionWithValidPerformanceHysteresisEpochsSucceeds)

t.Run("Submitting a new spot market with invalid tick size fails and valid tick size succeeds", testNewSpotMarketTickSize)
}

func testNewSpotMarketTickSize(t *testing.T) {
cases := getTickSizeCases()
for _, tsc := range cases {
err := checkProposalSubmission(&commandspb.ProposalSubmission{
Terms: &vegapb.ProposalTerms{
Change: &vegapb.ProposalTerms_NewSpotMarket{
NewSpotMarket: &vegapb.NewSpotMarket{
Changes: &vegapb.NewSpotMarketConfiguration{
Instrument: &protoTypes.InstrumentConfiguration{
Product: &protoTypes.InstrumentConfiguration_Spot{},
},
TickSize: tsc.tickSize,
},
},
},
},
})
if tsc.err != nil {
assert.Contains(t, err.Get("proposal_submission.terms.change.new_spot_market.changes.tick_size"), tsc.err)
} else {
assert.Empty(t, err.Get("proposal_submission.terms.change.new_spot_market.changes.tick_size"))
}
}
}

func testNewSpotMarketChangeSubmissionWithoutNewSpotMarketFails(t *testing.T) {
Expand Down
25 changes: 24 additions & 1 deletion commands/proposal_submission_update_market_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,30 @@ func TestCheckProposalSubmissionForUpdateMarket(t *testing.T) {
t.Run("Submitting a perps market product parameters", testUpdatePerpsMarketChangeSubmissionProductParameters)
t.Run("Submitting a perps market with funding rate modifiers", testUpdatePerpetualMarketWithFundingRateModifiers)
t.Run("Submitting a market update with invalid mark price configuration ", testUpdateMarketCompositePriceConfiguration)
t.Run("Submitting a market update with invalid intenal composite price configuration ", testUpdatePerpsMarketChangeSubmissionWithInternalCompositePriceConfig)
t.Run("Submitting a market update with invalid intenal composite price configuration", testUpdatePerpsMarketChangeSubmissionWithInternalCompositePriceConfig)
t.Run("Submitting a market update with invalid tick size fails and valid tick size succeeds", testUpdateMarketTickSize)
}

func testUpdateMarketTickSize(t *testing.T) {
cases := getTickSizeCases()
for _, tsc := range cases {
err := checkProposalSubmission(&commandspb.ProposalSubmission{
Terms: &vegapb.ProposalTerms{
Change: &vegapb.ProposalTerms_UpdateMarket{
UpdateMarket: &vegapb.UpdateMarket{
Changes: &vegapb.UpdateMarketConfiguration{
TickSize: tsc.tickSize,
},
},
},
},
})
if tsc.err != nil {
assert.Contains(t, err.Get("proposal_submission.terms.change.update_market.changes.tick_size"), tsc.err)
} else {
assert.Empty(t, err.Get("proposal_submission.terms.change.update_market.changes.tick_size"))
}
}
}

func testUpdatePerpsMarketChangeSubmissionWithInternalCompositePriceConfig(t *testing.T) {
Expand Down
23 changes: 23 additions & 0 deletions commands/proposal_submission_update_spot_market_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,29 @@ func TestCheckProposalSubmissionForUpdateSpotMarket(t *testing.T) {
t.Run("Submitting a spot market update with valid competition factor succeeds", testUpdateSpotMarketChangeSubmissionWithValidCompetitionFactorSucceeds)
t.Run("Submitting a spot market update with invalid hysteresis epochs fails", testUpdateSpotMarketChangeSubmissionWithInvalidPerformanceHysteresisEpochsFails)
t.Run("Submitting a spot market update with valid hysteresis epochs succeeds", testUpdateSpotMarketChangeSubmissionWithValidPerformanceHysteresisEpochsSucceeds)
t.Run("Submitting a spot market update with invalid tick size fails and valid tick size succeeds", testUpdateSpotMarketTickSize)
}

func testUpdateSpotMarketTickSize(t *testing.T) {
cases := getTickSizeCases()
for _, tsc := range cases {
err := checkProposalSubmission(&commandspb.ProposalSubmission{
Terms: &protoTypes.ProposalTerms{
Change: &protoTypes.ProposalTerms_UpdateSpotMarket{
UpdateSpotMarket: &protoTypes.UpdateSpotMarket{
Changes: &protoTypes.UpdateSpotMarketConfiguration{
TickSize: tsc.tickSize,
},
},
},
},
})
if tsc.err != nil {
assert.Contains(t, err.Get("proposal_submission.terms.change.update_spot_market.changes.tick_size"), tsc.err)
} else {
assert.Empty(t, err.Get("proposal_submission.terms.change.update_spot_market.changes.tick_size"))
}
}
}

func testUpdateSpotMarketChangeSubmissionWithoutUpdateMarketFails(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions core/execution/engine_snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ func getSpotMarketConfig() *types.Market {
SourceStalenessTolerance: []time.Duration{0, 0, 0, 0},
CompositePriceType: types.CompositePriceTypeByLastTrade,
},
TickSize: num.UintOne(),
}
}

Expand Down Expand Up @@ -387,6 +388,7 @@ func getMarketConfig() *types.Market {
SourceStalenessTolerance: []time.Duration{0, 0, 0, 0},
CompositePriceType: types.CompositePriceTypeByLastTrade,
},
TickSize: num.UintOne(),
}
}

Expand Down
37 changes: 35 additions & 2 deletions core/execution/future/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -1325,14 +1325,28 @@ func (m *Market) getNewPeggedPrice(order *types.Order) (*num.Uint, error) {

offset := num.UintZero().Mul(order.PeggedOrder.Offset, m.priceFactor)
if order.Side == types.SideSell {
return price.AddSum(offset), nil
price = price.AddSum(offset)
if !num.UintZero().Mod(price, m.mkt.TickSize).IsZero() {
price = price.Div(price.AddSum(m.mkt.TickSize), m.mkt.TickSize)
price = price.Mul(price, m.mkt.TickSize)
}
return price, nil
}

if price.LTE(offset) {
return num.UintZero(), common.ErrUnableToReprice
}

return num.UintZero().Sub(price, offset), nil
price.Sub(price, offset)
if !num.UintZero().Mod(price, m.mkt.TickSize).IsZero() {
price.Div(price, m.mkt.TickSize)
price.Mul(price, m.mkt.TickSize)
if price.LTE(offset) {
return num.UintZero(), common.ErrUnableToReprice
}
}

return price, nil
}

// Reprice a pegged order. This only updates the price on the order.
Expand Down Expand Up @@ -1700,8 +1714,21 @@ func (m *Market) validateOrder(ctx context.Context, order *types.Order) (err err
}
return reason
}
return m.validateTickSize(order.PeggedOrder.Offset)
}

if order.OriginalPrice != nil {
wwestgarth marked this conversation as resolved.
Show resolved Hide resolved
return m.validateTickSize(order.OriginalPrice)
}

return nil
}

func (m *Market) validateTickSize(price *num.Uint) error {
d := num.UintZero().Mod(price, m.mkt.TickSize)
if !d.IsZero() {
return types.ErrOrderNotInTickSize
}
return nil
}

Expand Down Expand Up @@ -3520,6 +3547,12 @@ func (m *Market) amendOrder(
return nil, nil, err
}

if orderAmendment.Price != nil && amendedOrder.OriginalPrice != nil {
if err = m.validateTickSize(amendedOrder.OriginalPrice); err != nil {
return nil, nil, err
}
}

if err := m.position.ValidateAmendOrder(existingOrder, amendedOrder); err != nil {
return nil, nil, err
}
Expand Down
Loading
Loading