diff --git a/core/integration/features/volume-rebate/0095-HVMR.feature b/core/integration/features/volume-rebate/0095-HVMR.feature new file mode 100644 index 0000000000..ff4d646654 --- /dev/null +++ b/core/integration/features/volume-rebate/0095-HVMR.feature @@ -0,0 +1,205 @@ +Feature: Volume rebate program - contributions from trades + + Volume rebate program rewards parties who comprise above a specified + fraction of the maker volume on the network in a window with an + extra rebate factor. + + Tests check trades contribute towards a party's maker volume fraction + correctly and that volume across windows and markets is correctly + counted and scaled where necessary. + + Background: + + # Initialise the network and register the assets + Given the average block duration is "1" + And the following network parameters are set: + | name | value | + | market.fee.factors.makerFee | 0.01 | + | market.fee.factors.infrastructureFee | 0.01 | + | market.fee.factors.treasuryFee | 0.1 | + | market.fee.factors.buybackFee | 0.1 | + | network.markPriceUpdateMaximumFrequency | 0s | + | validators.epoch.length | 20s | + | market.auction.minimumDuration | 1 | + + And the following assets are registered: + | id | decimal places | quantum | + | USD-0-1 | 0 | 1 | + | MXN-0-10 | 0 | 10 | + + # Initialise the parties and deposit assets + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | aux1 | USD-0-1 | 1000000 | + | aux2 | USD-0-1 | 1000000 | + | aux1 | MXN-0-10 | 10000000 | + | aux2 | MXN-0-10 | 10000000 | + + # Setup the USD and VMD markets in continuous trading + Given the price monitoring named "price-monitoring": + | horizon | probability | auction extension | + | 3600 | 0.99 | 1 | + And the markets: + | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | position decimal places | + | BTC/USD-0-1 | USD | USD-0-1 | default-log-normal-risk-model | default-margin-calculator | 1 | default-none | price-monitoring | default-eth-for-future | 1e-3 | 0 | default-futures | 0 | 0 | + | BTC/MXN-0-10 | VND | MXN-0-10 | default-log-normal-risk-model | default-margin-calculator | 1 | default-none | price-monitoring | default-eth-for-future | 1e-3 | 0 | default-futures | 0 | 0 | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | aux1 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | + | aux2 | BTC/USD-0-1 | sell | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | aux1 | BTC/MXN-0-10 | buy | 1 | 500000 | 0 | TYPE_LIMIT | TIF_GTC | + | aux2 | BTC/MXN-0-10 | sell | 1 | 500000 | 0 | TYPE_LIMIT | TIF_GTC | + When the network moves ahead "2" blocks + And the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "BTC/USD-0-1" + And the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "BTC/MXN-0-10" + + + Scenario: Maker / taker volume does / does not contribute towards the maker volume fraction respectively (0095-HVMR-013)(0095-HVMR-015) + + Given the volume rebate program tiers named "vrt": + | fraction | rebate | + | 0.0001 | 0.001 | + And the volume rebate program: + | id | tiers | closing timestamp | window length | + | id | vrt | 0 | 1 | + And the network moves ahead "1" epochs + + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | party1 | USD-0-1 | 1000000 | + | party2 | USD-0-1 | 1000000 | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | error | + | party1 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | BTC/USD-0-1 | sell | 1 | 50000 | 1 | TYPE_LIMIT | TIF_GTC | | + When the network moves ahead "1" blocks + Then the following trades should be executed: + | buyer | seller | size | price | aggressor side | buyer maker fee | seller maker fee | + | party1 | party2 | 1 | 50000 | sell | 0 | 500 | + + # In the following epoch, party1 and party2 are both the maker of a + # trade but only party1 recevieves a rebate. + Given the network moves ahead "1" epochs + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | error | + | party1 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | aux1 | BTC/USD-0-1 | sell | 2 | 50000 | 2 | TYPE_LIMIT | TIF_GTC | | + When the network moves ahead "1" blocks + Then the following trades should be executed: + | buyer | seller | size | price | aggressor side | buyer high volume maker fee | seller high volume maker fee | + | party1 | aux1 | 1 | 50000 | sell | 0 | 50 | + | party2 | aux1 | 1 | 50000 | sell | 0 | 0 | + Then the following transfers should happen: + | from | to | from account | to account | market id | amount | asset | type | + | | party1 | ACCOUNT_TYPE_FEES_MAKER | ACCOUNT_TYPE_GENERAL | BTC/USD-0-1 | 50 | USD-0-1 | TRANSFER_TYPE_HIGH_MAKER_FEE_REBATE_RECEIVE | + + + Scenario: Trades on auction uncrossing do not contribute towards the maker volume fraction (0095-HVMR-017) + + Given the volume rebate program tiers named "vrt": + | fraction | rebate | + | 0.0001 | 0.001 | + And the volume rebate program: + | id | tiers | closing timestamp | window length | + | id | vrt | 0 | 1 | + And the network moves ahead "1" epochs + + # Trigger a PM auction + Given the market data for the market "BTC/USD-0-1" should be: + | mark price | trading mode | horizon | min bound | max bound | + | 50000 | TRADING_MODE_CONTINUOUS | 3600 | 47313 | 52816 | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | aux1 | BTC/USD-0-1 | buy | 1 | 60 | 0 | TYPE_LIMIT | TIF_GTC | auction-order-aux1 | + | aux2 | BTC/USD-0-1 | sell | 1 | 60 | 0 | TYPE_LIMIT | TIF_GTC | auction-order-aux2 | + When the network moves ahead "1" blocks + And the parties cancel the following orders: + | party | reference | + | aux1 | auction-order-aux1 | + | aux2 | auction-order-aux2 | + Then the trading mode should be "TRADING_MODE_MONITORING_AUCTION" for the market "BTC/USD-0-1" + + # Exit the PM auction - volume should not contribute towards maker + # volume fraction of either party1 or party2 + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | party1 | USD-0-1 | 1000000 | + | party2 | USD-0-1 | 1000000 | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | error | + | party1 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | BTC/USD-0-1 | sell | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + When the network moves ahead "1" blocks + Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "BTC/USD-0-1" + Then the following trades should be executed: + | buyer | seller | size | price | aggressor side | buyer maker fee | seller maker fee | + | party1 | party2 | 1 | 50000 | | 0 | 0 | + + # In the following epoch, party1 and party2 are both the maker of a + # trade but neither receive a rebate. + Given the network moves ahead "1" epochs + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | error | + | party1 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | aux1 | BTC/USD-0-1 | sell | 2 | 50000 | 2 | TYPE_LIMIT | TIF_GTC | | + When the network moves ahead "1" blocks + Then the following trades should be executed: + | buyer | seller | size | price | aggressor side | buyer high volume maker fee | seller high volume maker fee | + | party1 | aux1 | 1 | 50000 | sell | 0 | 0 | + | party2 | aux1 | 1 | 50000 | sell | 0 | 0 | + + + Scenario Outline: Volume made in previous window correctly contributes towards maker volume fraction (0095-HVMR-019)(0095-HVMR-021) + + Given the volume rebate program tiers named "vrt": + | fraction | rebate | + | 0.0001 | 0.001 | + And the volume rebate program: + | id | tiers | closing timestamp | window length | + | id | vrt | 0 | | + And the network moves ahead "1" epochs + + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | party1 | USD-0-1 | 1000000 | + | party2 | USD-0-1 | 1000000 | + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | error | + | party1 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | aux1 | BTC/USD-0-1 | sell | 1 | 50000 | 1 | TYPE_LIMIT | TIF_GTC | | + When the network moves ahead "1" blocks + Then the following trades should be executed: + | buyer | seller | size | price | aggressor side | buyer maker fee | seller maker fee | + | party1 | aux1 | 1 | 50000 | sell | 0 | 500 | + + Given the network moves ahead epochs + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | error | + | party2 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | aux1 | BTC/USD-0-1 | sell | 1 | 50000 | 1 | TYPE_LIMIT | TIF_GTC | | + When the network moves ahead "1" blocks + Then the following trades should be executed: + | buyer | seller | size | price | aggressor side | buyer maker fee | seller maker fee | + | party2 | aux1 | 1 | 50000 | sell | 0 | 500 | + + When the network moves ahead "1" epochs + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | error | + | party1 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | BTC/USD-0-1 | buy | 1 | 50000 | 0 | TYPE_LIMIT | TIF_GTC | | + | aux1 | BTC/USD-0-1 | sell | 2 | 50000 | 2 | TYPE_LIMIT | TIF_GTC | | + When the network moves ahead "1" blocks + Then the following trades should be executed: + | buyer | seller | size | price | aggressor side | buyer high volume maker fee | seller high volume maker fee | + | party1 | aux1 | 1 | 50000 | sell | 0 | | + | party2 | aux1 | 1 | 50000 | sell | 0 | | + + Examples: + | window length | epochs between trades | party1 rebate | party2 rebate | + | 10 | "1" | 50 | 50 | + + diff --git a/core/integration/steps/the_following_trades_happened.go b/core/integration/steps/the_following_trades_happened.go index 08316da06e..5386f2a8a1 100644 --- a/core/integration/steps/the_following_trades_happened.go +++ b/core/integration/steps/the_following_trades_happened.go @@ -65,6 +65,7 @@ func TheFollowingTradesShouldBeExecuted( buyerInfraFeeReferrerDiscount, hasBuyerInfraFeeReferrerDiscount := row.DecimalB("buyer infrastructure fee referrer discount") buyerMakerFeeReferrerDiscount, hasBuyerMakerFeeReferrerDiscount := row.DecimalB("buyer maker fee referrer discount") buyerLiqFeeReferrerDiscount, hasBuyerLiqFeeReferrerDiscount := row.DecimalB("buyer liquidity fee referrer discount") + buyerHighVolumeMakerFee, hasBuyerHighVolumeMakerFee := row.DecimalB("buyer high volume maker fee") sellerFee, hasSellerFee := row.U64B("seller fee") sellerInfraFee, hasSellerInfraFee := row.U64B("seller infrastructure fee") @@ -76,6 +77,7 @@ func TheFollowingTradesShouldBeExecuted( sellerInfraFeeReferrerDiscount, hasSellerInfraFeeReferrerDiscount := row.DecimalB("seller infrastructure fee referrer discount") sellerMakerFeeReferrerDiscount, hasSellerMakerFeeReferrerDiscount := row.DecimalB("seller maker fee referrer discount") sellerLiqFeeReferrerDiscount, hasSellerLiqFeeReferrerDiscount := row.DecimalB("seller liquidity fee referrer discount") + sellerHighVolumeMakerFee, hasSellerHighVolumeMakerFee := row.DecimalB("seller high volume maker fee") data := broker.GetTrades() var found bool @@ -89,22 +91,24 @@ func TheFollowingTradesShouldBeExecuted( (!hasBuyerInfraFee || buyerInfraFee == stringToU64(v.BuyerFee.InfrastructureFee)) && (!hasBuyerMakerFee || buyerMakerFee == stringToU64(v.BuyerFee.MakerFee)) && (!hasBuyerLiqFee || buyerLiqFee == stringToU64(v.BuyerFee.LiquidityFee)) && - (!hasBuyerInfraFeeVolumeDiscount || buyerInfraFeeVolumeDiscount == num.MustDecimalFromString(v.BuyerFee.InfrastructureFeeVolumeDiscount)) && - (!hasBuyerMakerFeeVolumeDiscount || buyerMakerFeeVolumeDiscount == num.MustDecimalFromString(v.BuyerFee.MakerFeeVolumeDiscount)) && - (!hasBuyerLiqFeeVolumeDiscount || buyerLiqFeeVolumeDiscount == num.MustDecimalFromString(v.BuyerFee.LiquidityFeeVolumeDiscount)) && - (!hasBuyerInfraFeeReferrerDiscount || buyerInfraFeeReferrerDiscount == num.MustDecimalFromString(v.BuyerFee.InfrastructureFeeReferrerDiscount)) && - (!hasBuyerMakerFeeReferrerDiscount || buyerMakerFeeReferrerDiscount == num.MustDecimalFromString(v.BuyerFee.MakerFeeReferrerDiscount)) && - (!hasBuyerLiqFeeReferrerDiscount || buyerLiqFeeReferrerDiscount == num.MustDecimalFromString(v.BuyerFee.LiquidityFeeReferrerDiscount)) && + (!hasBuyerInfraFeeVolumeDiscount || buyerInfraFeeVolumeDiscount.Equal(num.MustDecimalFromString(v.BuyerFee.InfrastructureFeeVolumeDiscount))) && + (!hasBuyerMakerFeeVolumeDiscount || buyerMakerFeeVolumeDiscount.Equal(num.MustDecimalFromString(v.BuyerFee.MakerFeeVolumeDiscount))) && + (!hasBuyerLiqFeeVolumeDiscount || buyerLiqFeeVolumeDiscount.Equal(num.MustDecimalFromString(v.BuyerFee.LiquidityFeeVolumeDiscount))) && + (!hasBuyerInfraFeeReferrerDiscount || buyerInfraFeeReferrerDiscount.Equal(num.MustDecimalFromString(v.BuyerFee.InfrastructureFeeReferrerDiscount))) && + (!hasBuyerMakerFeeReferrerDiscount || buyerMakerFeeReferrerDiscount.Equal(num.MustDecimalFromString(v.BuyerFee.MakerFeeReferrerDiscount))) && + (!hasBuyerLiqFeeReferrerDiscount || buyerLiqFeeReferrerDiscount.Equal(num.MustDecimalFromString(v.BuyerFee.LiquidityFeeReferrerDiscount))) && + (!hasBuyerHighVolumeMakerFee || buyerHighVolumeMakerFee.Equal(num.MustDecimalFromString(v.BuyerFee.HighVolumeMakerFee))) && (!hasSellerFee || sellerFee == feeToU64(v.SellerFee)) && (!hasSellerInfraFee || sellerInfraFee == stringToU64(v.SellerFee.InfrastructureFee)) && (!hasSellerMakerFee || sellerMakerFee == stringToU64(v.SellerFee.MakerFee)) && (!hasSellerLiqFee || sellerLiqFee == stringToU64(v.SellerFee.LiquidityFee)) && - (!hasSellerInfraFeeVolumeDiscount || sellerInfraFeeVolumeDiscount == num.MustDecimalFromString(v.SellerFee.InfrastructureFeeVolumeDiscount)) && - (!hasSellerMakerFeeVolumeDiscount || sellerMakerFeeVolumeDiscount == num.MustDecimalFromString(v.SellerFee.MakerFeeVolumeDiscount)) && - (!hasSellerLiqFeeVolumeDiscount || sellerLiqFeeVolumeDiscount == num.MustDecimalFromString(v.SellerFee.LiquidityFeeVolumeDiscount)) && - (!hasSellerInfraFeeReferrerDiscount || sellerInfraFeeReferrerDiscount == num.MustDecimalFromString(v.SellerFee.InfrastructureFeeReferrerDiscount)) && - (!hasSellerMakerFeeReferrerDiscount || sellerMakerFeeReferrerDiscount == num.MustDecimalFromString(v.SellerFee.MakerFeeReferrerDiscount)) && - (!hasSellerLiqFeeReferrerDiscount || sellerLiqFeeReferrerDiscount == num.MustDecimalFromString(v.SellerFee.LiquidityFeeReferrerDiscount)) { + (!hasSellerInfraFeeVolumeDiscount || sellerInfraFeeVolumeDiscount.Equal(num.MustDecimalFromString(v.SellerFee.InfrastructureFeeVolumeDiscount))) && + (!hasSellerMakerFeeVolumeDiscount || sellerMakerFeeVolumeDiscount.Equal(num.MustDecimalFromString(v.SellerFee.MakerFeeVolumeDiscount))) && + (!hasSellerLiqFeeVolumeDiscount || sellerLiqFeeVolumeDiscount.Equal(num.MustDecimalFromString(v.SellerFee.LiquidityFeeVolumeDiscount))) && + (!hasSellerInfraFeeReferrerDiscount || sellerInfraFeeReferrerDiscount.Equal(num.MustDecimalFromString(v.SellerFee.InfrastructureFeeReferrerDiscount))) && + (!hasSellerMakerFeeReferrerDiscount || sellerMakerFeeReferrerDiscount.Equal(num.MustDecimalFromString(v.SellerFee.MakerFeeReferrerDiscount))) && + (!hasSellerLiqFeeReferrerDiscount || sellerLiqFeeReferrerDiscount.Equal(num.MustDecimalFromString(v.SellerFee.LiquidityFeeReferrerDiscount))) && + (!hasSellerHighVolumeMakerFee || sellerHighVolumeMakerFee.Equal(num.MustDecimalFromString(v.SellerFee.HighVolumeMakerFee))) { found = true } } @@ -141,6 +145,7 @@ func parseExecutedTradesTable(table *godog.Table) []RowWrapper { "buyer infrastructure fee referrer discount", "buyer liquidity fee referrer discount", "buyer maker fee referrer discount", + "buyer high volume maker fee", "seller fee", "seller infrastructure fee", @@ -152,6 +157,7 @@ func parseExecutedTradesTable(table *godog.Table) []RowWrapper { "seller infrastructure fee referrer discount", "seller liquidity fee referrer discount", "seller maker fee referrer discount", + "seller high volume maker fee", "is amm", }) }