From 694061d4284801e23d9a79ac3adc5742a05a57be Mon Sep 17 00:00:00 2001 From: Charlie Date: Wed, 22 May 2024 10:01:55 +0100 Subject: [PATCH 1/3] feat: add full coverage test --- .../features/verified/pegged_orders.feature | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 core/integration/features/verified/pegged_orders.feature diff --git a/core/integration/features/verified/pegged_orders.feature b/core/integration/features/verified/pegged_orders.feature new file mode 100644 index 00000000000..bb45437d19c --- /dev/null +++ b/core/integration/features/verified/pegged_orders.feature @@ -0,0 +1,96 @@ + +Feature: Pegged orders do not cross + + Aiming for full coverage of edge-cases, check the following: + + - Market decimals > asset decimals + - Market decimals < asset decimals + + - For each of the above + - tick size cannot be expressed in asset decimals + - tick size can just be expressed in asset decimals + - tick size can be expressed in asset decimals + + - For each of the above + - offset cannot be expressed in asset decimals + - offset can just be expressed in asset decimals + - offset can be expressed in asset decimals + + Background: + + Given the average block duration is "1" + And the following assets are registered: + | id | decimal places | quantum | + | ETH.1.10 | 1 | 1 | + + And the following network parameters are set: + | name | value | + | limits.markets.maxPeggedOrders | 6 | + + And the parties deposit on asset's general account the following amount: + | party | asset | amount | + | aux1 | ETH.1.10 | 1000000000000 | + | aux2 | ETH.1.10 | 1000000000000 | + | party1 | ETH.1.10 | 1000000000000 | + | party2 | ETH.1.10 | 1000000000000 | + + Scenario Outline: # Market decimals > asset decimals + + Given the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | tick size | + | ETH.1.10/DEC21 | ETH.1.10 | ETH.1.10 | default-parameters | default-simple-risk-model | default-margin-calculator | 1 | default-none | default-none | default-eth-for-future | 0.5 | 0 | default-basic | 2 | | + Given the parties place the following pegged orders: + | party | market id | side | pegged reference | volume | offset | reference | error | + | party1 | ETH.1.10/DEC21 | buy | MID | 10 | | peg-buy | | + | party2 | ETH.1.10/DEC21 | sell | MID | 10 | | peg-sell | | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | aux1 | ETH.1.10/DEC21 | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b1-1 | + | aux1 | ETH.1.10/DEC21 | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b1-1 | + | aux2 | ETH.1.10/DEC21 | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b2-1 | + | aux2 | ETH.1.10/DEC21 | sell | 1 | | 0 | TYPE_LIMIT | TIF_GTC | p3b2-1 | + When the opening auction period ends for market "ETH.1.10/DEC21" + Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH.1.10/DEC21" + + Examples: + | bo | tick size | offset | error | + | 1010 | 1 | 1 | | + | 1010 | 1 | 10 | | + | 1010 | 1 | 100 | | + | 1010 | 10 | 1 | OrderError: price not in tick size | + | 1010 | 10 | 10 | | + | 1010 | 10 | 100 | | + | 1100 | 100 | 1 | OrderError: price not in tick size | + | 1100 | 100 | 10 | OrderError: price not in tick size | + | 1100 | 100 | 100 | | + + + Scenario Outline: # Market decimals < asset decimals + + Given the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | tick size | + | ETH.1.10/DEC21 | ETH.1.10 | ETH.1.10 | default-parameters | default-simple-risk-model | default-margin-calculator | 1 | default-none | default-none | default-eth-for-future | 0.5 | 0 | default-basic | 0 | | + Given the parties place the following pegged orders: + | party | market id | side | pegged reference | volume | offset | reference | error | + | party1 | ETH.1.10/DEC21 | buy | MID | 10 | | peg-buy | | + | party2 | ETH.1.10/DEC21 | sell | MID | 10 | | peg-sell | | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | aux1 | ETH.1.10/DEC21 | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b1-1 | + | aux1 | ETH.1.10/DEC21 | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b1-1 | + | aux2 | ETH.1.10/DEC21 | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b2-1 | + | aux2 | ETH.1.10/DEC21 | sell | 1 | | 0 | TYPE_LIMIT | TIF_GTC | p3b2-1 | + When the opening auction period ends for market "ETH.1.10/DEC21" + Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH.1.10/DEC21" + + Examples: + | bo | tick size | offset | error | + | 1001 | 1 | 1 | | + | 1001 | 1 | 10 | | + | 1001 | 1 | 100 | | + | 1010 | 10 | 1 | OrderError: price not in tick size | + | 1010 | 10 | 10 | | + | 1010 | 10 | 100 | | + | 1100 | 100 | 1 | OrderError: price not in tick size | + | 1100 | 100 | 10 | OrderError: price not in tick size | + | 1100 | 100 | 100 | | From 552ce80d58370299fcd5cca7bdfc52a623a43283 Mon Sep 17 00:00:00 2001 From: ze97286 Date: Wed, 22 May 2024 10:48:09 +0100 Subject: [PATCH 2/3] fix: prevent a pegged order submission that would cross mid --- core/execution/future/market.go | 7 ++++ core/execution/spot/market.go | 7 ++++ .../features/verified/pegged_orders.feature | 40 +++++++++---------- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/core/execution/future/market.go b/core/execution/future/market.go index 69ab4ed57bb..bc59879eec8 100644 --- a/core/execution/future/market.go +++ b/core/execution/future/market.go @@ -1830,6 +1830,13 @@ func (m *Market) validateOrder(ctx context.Context, order *types.Order) (err err } return reason } + if order.PeggedOrder.Reference == types.PeggedReferenceMid { + offsetInAsset, _ := num.UintFromDecimal(order.PeggedOrder.Offset.ToDecimal().Mul(m.priceFactor)) + tickSizeInAsset, _ := num.UintFromDecimal(m.mkt.TickSize.ToDecimal().Mul(m.priceFactor)) + if offsetInAsset.IsZero() && tickSizeInAsset.IsZero() { + return fmt.Errorf("invalid offset - pegged mid will cross") + } + } return m.validateTickSize(order.PeggedOrder.Offset) } diff --git a/core/execution/spot/market.go b/core/execution/spot/market.go index 13e88b3b515..33442260d29 100644 --- a/core/execution/spot/market.go +++ b/core/execution/spot/market.go @@ -1046,6 +1046,13 @@ func (m *Market) validateOrder(ctx context.Context, order *types.Order) (err err } return reason } + if order.PeggedOrder.Reference == types.PeggedReferenceMid { + offsetInAsset, _ := num.UintFromDecimal(order.PeggedOrder.Offset.ToDecimal().Mul(m.priceFactor)) + tickSizeInAsset, _ := num.UintFromDecimal(m.mkt.TickSize.ToDecimal().Mul(m.priceFactor)) + if offsetInAsset.IsZero() && tickSizeInAsset.IsZero() { + return fmt.Errorf("invalid offset - pegged mid will cross") + } + } return m.validateTickSize(order.PeggedOrder.Offset) } diff --git a/core/integration/features/verified/pegged_orders.feature b/core/integration/features/verified/pegged_orders.feature index bb45437d19c..9e8fc1f7508 100644 --- a/core/integration/features/verified/pegged_orders.feature +++ b/core/integration/features/verified/pegged_orders.feature @@ -1,20 +1,20 @@ Feature: Pegged orders do not cross - Aiming for full coverage of edge-cases, check the following: + Aiming for full coverage of edge-cases, check the following: - Market decimals > asset decimals - Market decimals < asset decimals - For each of the above - - tick size cannot be expressed in asset decimals - - tick size can just be expressed in asset decimals - - tick size can be expressed in asset decimals + - tick size cannot be expressed in asset decimals + - tick size can just be expressed in asset decimals + - tick size can be expressed in asset decimals - - For each of the above - - offset cannot be expressed in asset decimals - - offset can just be expressed in asset decimals - - offset can be expressed in asset decimals + - For each of the above + - offset cannot be expressed in asset decimals + - offset can just be expressed in asset decimals + - offset can be expressed in asset decimals Background: @@ -52,17 +52,17 @@ Feature: Pegged orders do not cross When the opening auction period ends for market "ETH.1.10/DEC21" Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH.1.10/DEC21" - Examples: - | bo | tick size | offset | error | - | 1010 | 1 | 1 | | - | 1010 | 1 | 10 | | - | 1010 | 1 | 100 | | - | 1010 | 10 | 1 | OrderError: price not in tick size | - | 1010 | 10 | 10 | | - | 1010 | 10 | 100 | | - | 1100 | 100 | 1 | OrderError: price not in tick size | - | 1100 | 100 | 10 | OrderError: price not in tick size | - | 1100 | 100 | 100 | | + Examples: + | bo | tick size | offset | error | + | 1010 | 1 | 1 | invalid offset - pegged mid will cross | + | 1010 | 1 | 10 | | + | 1010 | 1 | 100 | | + | 1010 | 10 | 1 | OrderError: price not in tick size | + | 1010 | 10 | 10 | | + | 1010 | 10 | 100 | | + | 1100 | 100 | 1 | OrderError: price not in tick size | + | 1100 | 100 | 10 | OrderError: price not in tick size | + | 1100 | 100 | 100 | | Scenario Outline: # Market decimals < asset decimals @@ -83,7 +83,7 @@ Feature: Pegged orders do not cross When the opening auction period ends for market "ETH.1.10/DEC21" Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH.1.10/DEC21" - Examples: + Examples: | bo | tick size | offset | error | | 1001 | 1 | 1 | | | 1001 | 1 | 10 | | From e6cbccf4decbd1c8e779eea17b5956c9f6e24711 Mon Sep 17 00:00:00 2001 From: Charlie Date: Wed, 22 May 2024 11:20:55 +0100 Subject: [PATCH 3/3] feat: add test cases for spot markets --- .../features/verified/pegged_orders.feature | 91 ++++++++++++++++--- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/core/integration/features/verified/pegged_orders.feature b/core/integration/features/verified/pegged_orders.feature index 9e8fc1f7508..a99abf2d611 100644 --- a/core/integration/features/verified/pegged_orders.feature +++ b/core/integration/features/verified/pegged_orders.feature @@ -1,7 +1,8 @@ Feature: Pegged orders do not cross - Aiming for full coverage of edge-cases, check the following: + Aiming for full coverage of edge-cases, check the following for both + derivative and spot markets: - Market decimals > asset decimals - Market decimals < asset decimals @@ -22,19 +23,24 @@ Feature: Pegged orders do not cross And the following assets are registered: | id | decimal places | quantum | | ETH.1.10 | 1 | 1 | + | BTC.1.10 | 1 | 1 | And the following network parameters are set: | name | value | | limits.markets.maxPeggedOrders | 6 | And the parties deposit on asset's general account the following amount: - | party | asset | amount | - | aux1 | ETH.1.10 | 1000000000000 | - | aux2 | ETH.1.10 | 1000000000000 | - | party1 | ETH.1.10 | 1000000000000 | - | party2 | ETH.1.10 | 1000000000000 | - - Scenario Outline: # Market decimals > asset decimals + | party | asset | amount | + | aux1 | ETH.1.10 | 1000000000000000 | + | aux2 | ETH.1.10 | 1000000000000000 | + | party1 | ETH.1.10 | 1000000000000000 | + | party2 | ETH.1.10 | 1000000000000000 | + | aux1 | BTC.1.10 | 1000000000000000 | + | aux2 | BTC.1.10 | 1000000000000000 | + | party1 | BTC.1.10 | 1000000000000000 | + | party2 | BTC.1.10 | 1000000000000000 | + + Scenario Outline: Derivative markets - market decimals > asset decimals Given the markets: | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | tick size | @@ -52,7 +58,7 @@ Feature: Pegged orders do not cross When the opening auction period ends for market "ETH.1.10/DEC21" Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH.1.10/DEC21" - Examples: + Examples: | bo | tick size | offset | error | | 1010 | 1 | 1 | invalid offset - pegged mid will cross | | 1010 | 1 | 10 | | @@ -65,7 +71,7 @@ Feature: Pegged orders do not cross | 1100 | 100 | 100 | | - Scenario Outline: # Market decimals < asset decimals + Scenario Outline: Derivative markets - market decimals < asset decimals Given the markets: | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | tick size | @@ -83,7 +89,69 @@ Feature: Pegged orders do not cross When the opening auction period ends for market "ETH.1.10/DEC21" Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH.1.10/DEC21" - Examples: + Examples: + | bo | tick size | offset | error | + | 1001 | 1 | 1 | | + | 1001 | 1 | 10 | | + | 1001 | 1 | 100 | | + | 1010 | 10 | 1 | OrderError: price not in tick size | + | 1010 | 10 | 10 | | + | 1010 | 10 | 100 | | + | 1100 | 100 | 1 | OrderError: price not in tick size | + | 1100 | 100 | 10 | OrderError: price not in tick size | + | 1100 | 100 | 100 | | + + + Scenario Outline: Spot market - market decimals > asset decimals + + Given the spot markets: + | id | name | base asset | quote asset | liquidity monitoring | risk model | auction duration | fees | price monitoring | sla params | decimal places | tick size | + | BTC/ETH | BTC/ETH | BTC.1.10 | ETH.1.10 | default-parameters | default-simple-risk-model | 1 | default-none | default-none | default-basic | 2 | | + Given the parties place the following pegged orders: + | party | market id | side | pegged reference | volume | offset | reference | error | + | party1 | BTC/ETH | buy | MID | 10 | | peg-buy | | + | party2 | BTC/ETH | sell | MID | 10 | | peg-sell | | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | aux1 | BTC/ETH | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b1-1 | + | aux1 | BTC/ETH | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b1-1 | + | aux2 | BTC/ETH | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b2-1 | + | aux2 | BTC/ETH | sell | 1 | | 0 | TYPE_LIMIT | TIF_GTC | p3b2-1 | + When the opening auction period ends for market "BTC/ETH" + Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "BTC/ETH" + + Examples: + | bo | tick size | offset | error | + | 1010 | 1 | 1 | invalid offset - pegged mid will cross | + | 1010 | 1 | 10 | | + | 1010 | 1 | 100 | | + | 1010 | 10 | 1 | OrderError: price not in tick size | + | 1010 | 10 | 10 | | + | 1010 | 10 | 100 | | + | 1100 | 100 | 1 | OrderError: price not in tick size | + | 1100 | 100 | 10 | OrderError: price not in tick size | + | 1100 | 100 | 100 | | + + + Scenario Outline: Spot market - market decimals < asset decimals + + Given the spot markets: + | id | name | base asset | quote asset | liquidity monitoring | risk model | auction duration | fees | price monitoring | sla params | decimal places | tick size | + | BTC/ETH | BTC/ETH | BTC.1.10 | ETH.1.10 | default-parameters | default-simple-risk-model | 1 | default-none | default-none | default-basic | 0 | | + Given the parties place the following pegged orders: + | party | market id | side | pegged reference | volume | offset | reference | error | + | party1 | BTC/ETH | buy | MID | 10 | | peg-buy | | + | party2 | BTC/ETH | sell | MID | 10 | | peg-sell | | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | aux1 | BTC/ETH | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b1-1 | + | aux1 | BTC/ETH | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b1-1 | + | aux2 | BTC/ETH | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | p3b2-1 | + | aux2 | BTC/ETH | sell | 1 | | 0 | TYPE_LIMIT | TIF_GTC | p3b2-1 | + When the opening auction period ends for market "BTC/ETH" + Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "BTC/ETH" + + Examples: | bo | tick size | offset | error | | 1001 | 1 | 1 | | | 1001 | 1 | 10 | | @@ -94,3 +162,4 @@ Feature: Pegged orders do not cross | 1100 | 100 | 1 | OrderError: price not in tick size | | 1100 | 100 | 10 | OrderError: price not in tick size | | 1100 | 100 | 100 | | +