From 5849c2d7313ef0993bc344f3b1aebbd5b1d8f1d8 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Jul 2024 14:37:06 +0100 Subject: [PATCH] feat: separate fee discount components (#2293) * feat: Adding separate discount factor components * feat: Adding separate discount factor components * feat: Add ACs * feat: Fix AC code * feat: Fix AC code * feat: Review comments * feat: Adding high volume maker rebate * feat: Fix up eqs * feat: Remove duplicative naming * chore: Fix up markdown * refactor: add default value --------- Co-authored-by: Witold --- protocol/0029-FEES-fees.md | 61 ++++++++---- .../0083-RFPR-on_chain_referral_program.md | 96 +++++++++++++++---- protocol/0084-VDPR-volume_discount_program.md | 54 ++++++++--- .../0095-HVMR-high_volume_maker_rebate.md | 26 +++++ protocol/features.json | 14 ++- 5 files changed, 196 insertions(+), 55 deletions(-) create mode 100644 protocol/0095-HVMR-high_volume_maker_rebate.md diff --git a/protocol/0029-FEES-fees.md b/protocol/0029-FEES-fees.md index 6573baea3..31f40d451 100644 --- a/protocol/0029-FEES-fees.md +++ b/protocol/0029-FEES-fees.md @@ -9,7 +9,7 @@ An order may cross with more than one other order, creating multiple trades. Eac The trading fee is: -`total_fee = infrastructure_fee + maker_fee + liquidity_fee` +`total_fee = infrastructure_fee + maker_fee + liquidity_fee + buyback_fee + treasury_fee` `infrastructure_fee = fee_factor[infrastructure] * trade_value_for_fee_purposes` @@ -17,6 +17,10 @@ The trading fee is: `liquidity_fee = fee_factor[liquidity] * trade_value_for_fee_purposes` +`buyback_fee = fee_factor[buyback] * trade_value_for_fee_purposes` + +`treasury_fee = fee_factor[treasury] * trade_value_for_fee_purposes` + Fees are calculated and collected in the settlement currency of the market, collected from the general account. Fees are collected first from the trader's account and then margin from account balance. If the general account doesn't have sufficient balance, then the remaining fee amount is collected from the margin account. If this is still insufficient then different rules apply between continuous trading and auctions (details below). Note that maker_fee = 0 if there is no maker, taker relationship between the trading parties (in particular auctions). @@ -25,14 +29,14 @@ Note that maker_fee = 0 if there is no maker, taker relationship between the tra Before fees are transferred, if there is an [active referral program](./0083-RFPR-on_chain_referral_program.md) or [volume discount program](./0085-VDPR-volume_discount_program.md), each parties fee components must be modified as follows. -Note, discounts are calculated and applied one after the other and **before** rewards are calculated. +Note, discounts are calculated and applied one after the other and **before** rewards are calculated. Additionally, no benefit discounts can be applied to the treasury or buyback fee components as these may be required for the `high volume market maker rebate`. 1. Calculate any referral discounts due to the party. ```pseudo - infrastructure_fee_referral_discount = floor(original_infrastructure_fee * referral_discount_factor) - liquidity_fee_referral_discount = floor(original_liquidity_fee * referral_discount_factor) - maker_fee_referral_discount = floor(original_maker_fee * referral_discount_factor) + infrastructure_fee_referral_discount = floor(original_infrastructure_fee * referral_infrastructure_discount_factor) + liquidity_fee_referral_discount = floor(original_liquidity_fee * referral_liquidity_discount_factor) + maker_fee_referral_discount = floor(original_maker_fee * referral_maker_discount_factor) ``` 1. Apply referral discounts to the original fee. @@ -46,9 +50,9 @@ Note, discounts are calculated and applied one after the other and **before** re 1. Calculate any volume discounts due to the party. ```pseudo - infrastructure_fee_volume_discount = floor(infrastructure_fee_after_referral_discount * volume_discount_factor) - liquidity_fee_volume_discount = floor(liquidity_fee_after_referral_discount * volume_discount_factor) - maker_fee_volume_discount = floor(maker_fee_after_referral_discount * volume_discount_factor) + infrastructure_fee_volume_discount = floor(infrastructure_fee_after_referral_discount * volume_infrastructure_discount_factor) + liquidity_fee_volume_discount = floor(liquidity_fee_after_referral_discount * volume_liquidity_discount_factor) + maker_fee_volume_discount = floor(maker_fee_after_referral_discount * volume_maker_discount_factor) ``` 1. Apply any volume discounts to the fee after referral discounts. @@ -62,9 +66,9 @@ Note, discounts are calculated and applied one after the other and **before** re 1. Calculate any referral rewards due to the parties referrer (Note we are using the updated fee components from step 4 and the `referralProgram.maxReferralRewardProportion` is the network parameter described in the [referral program spec](./0083-RFPR-on_chain_referral_program.md#network-parameters)) ```pseudo - infrastructure_fee_referral_reward = floor(infrastructure_fee_after_volume_discount * min(referral_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion)) - liquidity_fee_referral_reward = floor(liquidity_fee * min(liquidity_fee_after_volume_discount * min(referral_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion)) - maker_fee_referral_reward = floor(maker_fee * min(maker_fee_after_volume_discount * min(referral_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion)) + infrastructure_fee_referral_reward = floor(infrastructure_fee_after_volume_discount * min(referral_infrastructure_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion)) + liquidity_fee_referral_reward = floor(liquidity_fee * min(liquidity_fee_after_volume_discount * min(referral_liquidity_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion))) + maker_fee_referral_reward = floor(maker_fee * min(maker_fee_after_volume_discount * min(referral_maker_reward_factor * referral_reward_multiplier, referralProgram.maxReferralRewardProportion))) ``` 1. Finally, update the fee components by applying the rewards. @@ -82,10 +86,14 @@ Note, discounts are calculated and applied one after the other and **before** re - infrastructure: staking/governance system/engine (network wide) - maker: market framework / market making (network wide) - liquidity: market making system (per market) +- treasury: Fees sent to network treasury for later usage via governance votes (network wide) +- buyback: Fees used to purchase governance tokens to the protocol via regular auctions (network wide) The infrastructure fee factor is set by a network parameter `market.fee.factors.infrastructureFee` and a reasonable default value is `fee_factor[infrastructure] = 0.0005 = 0.05%`. The maker fee factor is set by a network parameter `market.fee.factors.makerFee` and a reasonable default value is `fee_factor[maker] = 0.00025 = 0.025%`. The liquidity fee factor is set by an auction-like mechanism based on the liquidity provisions committed to the market, see [setting LP fees](./0042-LIQF-setting_fees_and_rewarding_lps.md). +The treasury fee factor is set by the network parameter `market.fee.factors.treasuryFee` with a default value should of `0`. +The buyback fee factor is set by the network parameter `market.fee.factors.buybackFee` with a default value should of `0`. trade_value_for_fee_purposes: @@ -107,15 +115,26 @@ If the transfer fails: 1. If we are in continuous trading mode, than trades should be discarded, any orders on the book that would have been hit should remain in place with previous remaining size intact and the incoming order should be rejected (not enough fees error). This functionality requires to match orders and create trades without changing the state of the order book or passing trades downstream so that the execution of the transaction can be discarded with no impact on the order book if needed. Other than the criteria whether to proceed or discard, this is exactly the same functionality required to implement [price monitoring](./0032-PRIM-price_monitoring.md). -2. If we are in auction mode, ignore the shortfall (and see more details below). +1. If we are in auction mode, ignore the shortfall (and see more details below). + +The transfer of fees must be completed before performing the normal post-trade calculations (MTM Settlement, position resolution etc...). The transfers have to be identifiable as fee transfers and separate for the different components. -The transfer of fees must be completed before performing the normal post-trade calculations (MTM Settlement, position resolution etc...). The transfers have to be identifiable as fee transfers and separate for the three components. +Additionally, a `high_volume_market_maker_rebate` may be necessary which will be taken from the `treasury/buyback_fee` components. This will be calculated as: + +1. Determine whether the maker party of the trade (if there is one) qualifies for a `high volume market maker rebate` and, if so, at what rate. +1. Calculate the fee that this corresponds to as `high_volume_maker_fee = high_volume_factor * trade_value_for_fee_purposes`. +1. Take this fee from the `treasury_fee` and `buyback_fee` (protocol restrictions on governance changes ensure that `treasury_fee + buyback_fee >= high_volume_maker_fee` is always true) as a proportion of their relative sizes, i.e.: + 1. `high_volume_maker_fee = high_volume_factor * trade_value_for_fee_purposes` + 1. `treasury_fee = treasury_fee * (1 - high_volume_maker_fee / (treasury_fee + buyback_fee))` + 1. `buyback_fee = buyback_fee * (1 - high_volume_maker_fee / (treasury_fee + buyback_fee))` Now [apply benefit factors](#applying-benefit-factors) and then distribute funds from the "temporary fee bucket" as follows: 1. The `infrastructure_fee` is transferred to infrastructure fee pool for that asset. Its distribution is described in [0061 - Proof of Stake rewards](./0061-REWP-pos_rewards.md). In particular, at the end of each epoch the amount due to each validator and delegator is to be calculated and then distributed subject to validator score and type. -1. The `maker_fee` is transferred to the relevant party (the maker). +1. The `maker_fee` and any `high_volume_maker_fee` are transferred to the relevant party (the maker). 1. The `liquidity_fee` is distributed as described in [this spec](./0042-LIQF-setting_fees_and_rewarding_lps.md). +1. The `treasury_fee` is transferred to the treasury fee pool for that asset, where it will remain until community governance votes for transfers. +1. The `buyback_fee` is transferred to the buyback fee pool for that asset, where it will remain until community governance votes for transfers or a regular purchase program is set up. 1. The referral fee components (if any) can then be individually transferred to the relevant party (the referee). ### During Continuous Trading @@ -126,9 +145,9 @@ The "aggressor or price taker" pays the fee. The "passive or price maker" party ### Normal Auctions (including market protection and opening auctions) -During normal auctions there is no "price maker" both parties are "takers". Each side in a matched trade should contribute 1/2 of the infrastructure_fee + liquidity_fee. Note that this does not include a maker fee. +During normal auctions there is no "price maker" both parties are "takers". Each side in a matched trade should contribute 1/2 of the infrastructure_fee + liquidity_fee + treasury_fee + buyback_fee. Note that this does not include a maker fee. -Fees calculated and collected from general + margin as in continuous trading *but* if a party has insufficient capital to cover the trading fee then in auction the trade *still* *goes* *ahead* as long as the margin account should have enough left after paying the fees to cover maintenance level of margin for the orders and then converted trades. The fee is distributed so that the infrastructure_fee is paid first and only then the liquidity_fee. +Fees calculated and collected from general + margin as in continuous trading *but* if a party has insufficient capital to cover the trading fee then in auction the trade *still* *goes* *ahead* as long as the margin account should have enough left after paying the fees to cover maintenance level of margin for the orders and then converted trades. The fee is distributed so that the infrastructure_fee is paid first and only then the liquidity_fee/treasury_fee/buyback_fee. During an opening auction of a market, no fees are collected. @@ -168,7 +187,11 @@ For example, Ether is 18 decimals (wei). The smallest unit, non divisible is 1 w ### Applying benefit factors 1. Referee discounts are correctly calculated and applied for each taker fee component during continuous trading (assuming no volume discounts due to party) (0029-FEES-023) - - `infrastructure_referral_fee_discount` + - `infrastructure_fee_referral_discount` + - `liquidity_fee_referral_discount` + - `maker_fee_referral_discount` +1. Referee discounts with differing discounts across the three factors are correctly calculated and applied for each taker fee component during continuous trading (assuming no volume discounts due to party) (0029-FEES-034) + - `infrastructure_fee_referral_discount` - `liquidity_fee_referral_discount` - `maker_fee_referral_discount` 1. Referee discounts are correctly calculated and applied for each fee component when exiting an auction (assuming no volume discounts due to party) (0029-FEES-024) @@ -178,6 +201,10 @@ For example, Ether is 18 decimals (wei). The smallest unit, non divisible is 1 w - `infrastructure_fee_referral_reward` - `liquidity_fee_referral_reward` - `maker_fee_referral_reward` +1. Referrer rewards with differing reward factors are correctly calculated and transferred for each fee component during continuous trading (assuming no volume discounts due to party) (0029-FEES-035) + - `infrastructure_fee_referral_reward` + - `liquidity_fee_referral_reward` + - `maker_fee_referral_reward` 1. Referrer rewards are correctly calculated and transferred for each fee component when exiting an auction (assuming no volume discounts due to party) (0029-FEES-026) - `infrastructure_fee_referral_reward` - `liquidity_fee_referral_reward` diff --git a/protocol/0083-RFPR-on_chain_referral_program.md b/protocol/0083-RFPR-on_chain_referral_program.md index 39846dbe4..74da638d7 100644 --- a/protocol/0083-RFPR-on_chain_referral_program.md +++ b/protocol/0083-RFPR-on_chain_referral_program.md @@ -46,8 +46,14 @@ Enabling or changing the terms of the on-chain referral program can be proposed - `benefit_tiers`: a list of dictionaries (with the below fields) defining the reward and discount factors from referrals - `minimum_running_notional_taker_volume`: the required [`referral_set_running_notional_taker_volume`](#referral-set-volumes) in quantum units for parties to access this tier - `minimum_epochs`: the required number of epochs a referee must have been in a referral set to access this tier - - `referral_reward_factor`: the proportion of the referee's taker fees to be rewarded to the referrer - - `referral_discount_factor`: the proportion of the referee's taker fees to be discounted + - `referral_reward_factors`: the proportions of the referee's taker fees to be rewarded to the referrer + - `referral_maker_reward_factor`: the factor to reward the referrer from the maker fee component of any fees paid + - `referral_infrastructure_reward_factor`: the factor to reward the referrer from the infrastructure fee component of any fees paid + - `referral_liquidity_reward_factor`: the factor to reward the referrer from the liquidity provider fee component of any fees paid + - `referral_discount_factors`: the proportions of the referee's taker fees to be discounted + - `referral_maker_discount_factor`: the factor to discount the maker fee component of any fees paid + - `referral_infrastructure_discount_factor`: the factor to discount the infrastructure fee component of any fees paid + - `referral_liquidity_discount_factor`: the factor to discount the liquidity provider fee component of any fees paid - `staking_tiers`: a list of dictionaries (with the below fields) defining the multipliers from staking - `minimum_staked_tokens`: the required number of governance tokens ($VEGA) a referrer must be staking to receive the multiplier - `referral_reward_multiplier`: the multiplier applied to the referral_reward_factor when calculating referral rewards due to the referrer. @@ -61,20 +67,44 @@ message UpdateReferralProgram{ { "minimum_running_notional_taker_volume": 10000, "minimum_epochs": 1, - "referral_reward_factor": 0.001, - "referral_discount_factor": 0.001, + "referral_reward_factors": { + "referral_maker_reward_factor": 0.001, + "referral_infrastructure_reward_factor": 0.001, + "referral_liquidity_reward_factor": 0.001 + }, + "referral_discount_factors": { + "referral_maker_discount_factor": 0.001, + "referral_infrastructure_discount_factor": 0.001, + "referral_liquidity_discount_factor": 0.001 + } }, { "minimum_running_notional_taker_volume": 20000, "minimum_epochs": 7, - "referral_reward_factor": 0.005, - "referral_discount_factor": 0.005, + "referral_reward_factors": { + "referral_maker_reward_factor": 0.005, + "referral_infrastructure_reward_factor": 0.005, + "referral_liquidity_reward_factor": 0.005 + }, + "referral_discount_factors": { + "referral_maker_discount_factor": 0.005, + "referral_infrastructure_discount_factor": 0.005, + "referral_liquidity_discount_factor": 0.005 + }, }, { "minimum_running_notional_taker_volume": 30000, "minimum_epochs": 31, - "referral_reward_factor": 0.010, - "referral_discount_factor": 0.010, + "referral_reward_factors": { + "referral_maker_reward_factor": 0.01, + "referral_infrastructure_reward_factor": 0.01, + "referral_liquidity_reward_factor": 0.01 + }, + "referral_discount_factors": { + "referral_maker_discount_factor": 0.01, + "referral_infrastructure_discount_factor": 0.01, + "referral_liquidity_discount_factor": 0.01 + }, }, ], staking_tiers: [ @@ -103,11 +133,11 @@ When submitting a referral program proposal through governance the following con - the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `referralProgram.maxReferralTiers`. - all `minimum_running_notional_taker_volume` values must be an integer value strictly greater than `0`. - all `minimum_epochs` values must be an integer strictly greater than 0 -- all `referral_reward_factor` values must be greater than `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor`. +- all `referral_reward_factors` values must be greater than `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor`. - the number of tiers in `staking_tiers` must be less than or equal to the network parameter `referralProgram.maxReferralTiers`. - all `minimum_staked_tokens` values must be an integer value strictly greater than `0`. - all `referral_reward_multiplier` values must be a float value greater than or equal to `1`. -- all `referral_discount_factor` values must be greater than `0` and be less than or equal to the network parameter `referralProgram.maxReferralDiscountFactor`. +- all `referral_discount_factors` values must be greater than `0` and be less than or equal to the network parameter `referralProgram.maxReferralDiscountFactor`. - `window_length` must be an integer strictly greater than zero. The referral program will start the epoch after the `enactment_timestamp` is reached. @@ -286,28 +316,28 @@ The network can then calculate the set's `referral_set_running_notional_taker_vo ### Setting benefit factors and reward multipliers -Whilst a referral program is active, at the start of an epoch (after pending `ApplyReferralCode` transactions have been processed) the network must set the `referral_reward_factor` and `referral_discount_factor` for each referee. +Whilst a referral program is active, at the start of an epoch (after pending `ApplyReferralCode` transactions have been processed) the network must set the `referral_reward_factors` and `referral_discount_factors` for each referee. Note, when setting a referee's benefit factors we compare a sets `referral_set_running_notional_taker_volume` to a `minimum_running_notional_taker_volume` value. To prevent parties self-referring and moving teams, this `referral_set_running_notional_taker_volume` is always the value of the referee's original referral set. #### Setting the referral reward factor -The `referral_reward_factor` should be set by identifying the "highest" benefit tier where the following conditions are fulfilled. +The `referral_reward_factors` should be set by identifying the "highest" benefit tier where the following conditions are fulfilled. - `referral_set_running_notional_taker_volume` of the referee's **original** referral set is greater than or equal to the tier's `minimum_running_notional_taker_volume`. -The referee's `referral_reward_factor` is then set to the `referral_reward_factor` defined in the selected benefit tier. +The referee's `referral_reward_factors` are then set to the `referral_reward_factors` defined in the selected benefit tier. Note the **original** referrer is defined as the team of the referrer associated with the referee. See section [applying a referral code](#applying-a-referral-code) for more detail. #### Setting the referral discount factor -The `referral_discount_factor` should be set by identifying the "highest" benefit tier where **BOTH** the following conditions are fulfilled. +The `referral_discount_factors` should be set by identifying the "highest" benefit tier where **BOTH** the following conditions are fulfilled. - `referral_set_running_notional_taker_volume` of the referee's **original** referral set is greater than or equal to the tier's `minimum_running_notional_taker_volume`. - the referee has been a associated with the referral set for at least the tier's `minimum_epochs`. -The referee's `referral_discount_factor` is then set to the `referral_discount_factor` defined in the selected benefit tier. +The referee's `referral_discount_factors` are then set to the `referral_discount_factors` defined in the selected benefit tier. Note the **original** referrer is defined as the team of the referrer associated with the referee. See section [applying a referral code](#applying-a-referral-code) for more detail. @@ -329,20 +359,44 @@ Given: { "minimum_running_notional_taker_volume": 10000, "minimum_epochs": 0, - "referral_reward_factor": 0.001, - "referral_discount_factor": 0.001, + "referral_reward_factor": { + "maker_reward_factor": 0.001, + "infrastructure_reward_factor": 0.001, + "liquidity_reward_factor": 0.001 + }, + "referral_discount_factor": { + "maker_discount_factor": 0.001, + "infrastructure_discount_factor": 0.001, + "liquidity_discount_factor": 0.001 + }, }, { "minimum_running_notional_taker_volume": 20000, "minimum_epochs": 7, - "referral_reward_factor": 0.005, - "referral_discount_factor": 0.005, + "referral_reward_factor": { + "maker_reward_factor": 0.005, + "infrastructure_reward_factor": 0.005, + "liquidity_reward_factor": 0.005 + }, + "referral_discount_factor": { + "maker_discount_factor": 0.005, + "infrastructure_discount_factor": 0.005, + "liquidity_discount_factor": 0.005 + }, }, { "minimum_running_notional_taker_volume": 30000, "minimum_epochs": 31, - "referral_reward_factor": 0.010, - "referral_discount_factor": 0.010, + "referral_reward_factor": { + "maker_reward_factor": 0.01, + "infrastructure_reward_factor": 0.01, + "liquidity_reward_factor": 0. 01 + }, + "referral_discount_factor": { + "maker_discount_factor": 0.01, + "infrastructure_discount_factor": 0.01, + "liquidity_discount_factor": 0.01 + }, }, ] staking_tiers: [ diff --git a/protocol/0084-VDPR-volume_discount_program.md b/protocol/0084-VDPR-volume_discount_program.md index efbd1ef26..861e9811f 100644 --- a/protocol/0084-VDPR-volume_discount_program.md +++ b/protocol/0084-VDPR-volume_discount_program.md @@ -17,7 +17,10 @@ Enabling or changing the terms of the volume discount program can be proposed vi - `benefit_tiers`: a list of dictionaries with the following fields - `minimum_party_running_notional_taker_volume`: the required `party_running_notional_taker_volume` in quantum units for a party to access this tier - - `volume_discount_factor`: the proportion of the referees taker fees to be rewarded to the referrer + - `volume_discount_factors`: a dictionary with the following fields + - `maker_discount_factor`: the factor to discount the maker fee component of any fees paid + - `infrastructure_discount_factor`: the factor to discount the infrastructure fee component of any fees paid + - `lp_discount_factor`: the factor to discount the liquidity provider fee component of any fees paid - `end_of_program_timestamp`: the timestamp after which when the current epoch ends, the program will become inactive and benefits will be disabled. If this field is empty, the program runs indefinitely. - `window_length`: the number of epochs over which to evaluate a parties notional running volume @@ -27,15 +30,27 @@ message UpdateVolumeDiscountProgram{ benefit_tiers: [ { "minimum_party_running_notional_taker_volume": 1000, - "volume_discount_factor": 0.001, + "volume_discount_factors": { + "maker_discount_factor": 0.001, + "infrastructure_discount_factor": 0.001, + "lp_discount_factor": 0.001 + } }, { "minimum_party_running_notional_taker_volume": 20000, - "volume_discount_factor": 0.002, + "volume_discount_factors": { + "maker_discount_factor": 0.002, + "infrastructure_discount_factor": 0.002, + "lp_discount_factor": 0.002 + } }, { "minimum_party_running_notional_taker_volume": 30000, - "volume_discount_factor": 0.003, + "volume_discount_factors": { + "maker_discount_factor": 0.003, + "infrastructure_discount_factor": 0.003, + "lp_discount_factor": 0.003 + } }, ], end_of_program_timestamp: 123456789, @@ -49,7 +64,7 @@ When submitting a volume discount program proposal through governance the follow - a proposer cannot set an `end_of_program_timestamp` less than the proposals `enactment_time`. - the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `volumeDiscountProgram.maxBenefitTiers`. - all `minimum_party_running_notional_taker_volume` values must be an integer value strictly greater than `0`. -- all `volume_discount_factor` values must be greater than or equal to `0` and less than or equal to the network parameter `volumeDiscountProgram.maxVolumeDiscountFactor`. +- all `volume_discount_factors` values must be greater than or equal to `0` and less than or equal to the network parameter `volumeDiscountProgram.maxVolumeDiscountFactor`. - `window_length` must be an integer strictly greater than zero. The volume discount program will start the epoch after the `enactment_timestamp` is reached. @@ -78,15 +93,27 @@ Given: benefit_tiers=[ { "minimum_party_running_notional_taker_volume": 10000, - "volume_discount_factor": 0.001, + "volume_discount_factors": { + "maker_discount_factor": 0.001, + "infrastructure_discount_factor": 0.001, + "lp_discount_factor": 0.001 + } }, { "minimum_party_running_notional_taker_volume": 20000, - "volume_discount_factor": 0.005, + "volume_discount_factors": { + "maker_discount_factor": 0.005, + "infrastructure_discount_factor": 0.005, + "lp_discount_factor": 0.005 + } }, { "minimum_party_running_notional_taker_volume": 30000, - "volume_discount_factor": 0.010, + "volume_discount_factors": { + "maker_discount_factor": 0.05, + "infrastructure_discount_factor": 0.05, + "lp_discount_factor": 0.05 + } }, ] @@ -94,7 +121,9 @@ And: party_running_notional_taker_volume=22353 Then: - volume_discount_factor=0.005 + maker_volume_discount_factor=0.005 + infrastructure_volume_discount_factor=0.005 + lp_volume_discount_factor=0.005 ``` This benefit factor is then fixed for the duration of the next epoch. @@ -109,7 +138,7 @@ The Parties API should expose the following information: - a list of all **parties** (by `id`) and the following metrics: - current `party_running_notional_taker_volume` (value at the start of the epoch) - - current `volume_discount_factor` applied to fees + - current `volume_discount_factors` applied to fees - the total amount discounted for the party The Trades API should now also expose the following additional information for every trade: @@ -127,7 +156,7 @@ The Trades API should now also expose the following additional information for e - the `end_of_program_timestamp` must be less than or equal to the proposals `enactment_time` (0084-VDPR-001). - the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `volumeDiscountProgram.maxBenefitTiers` (0084-VDPR-002). - all `minimum_party_running_notional_taker_volume` values must be an integer strictly greater than 0 (0084-VDPR-017). - - all `volume_discount_factor` values must be greater than or equal to `0` and less than or equal to the network parameter `volumeDiscountProgram.maxVolumeDiscountFactor` (0084-VDPR-003). + - all `volume_discount_factors` values must be greater than or equal to `0` and less than or equal to the network parameter `volumeDiscountProgram.maxVolumeDiscountFactor` (0084-VDPR-003). - the `window_length` must be an integer strictly greater than zero (0084-VDPR-004). 1. A volume discount program should be started the first epoch change after the `enactment_datetime` is reached (0084-VDPR-005). 1. A volume discount program should be closed the first epoch change after the `end_of_program_timestamp` is reached (0084-VDPR-006). @@ -143,6 +172,7 @@ The Trades API should now also expose the following additional information for e ### Setting benefit factors -1. At the start of an epoch, each parties `volume_discount_factor` is reevaluated and fixed for the epoch (0084-VDPR-012). +1. At the start of an epoch, each parties `volume_discount_factors` are reevaluated and fixed for the epoch (0084-VDPR-012). 1. A parties `volume_discount_factor` is set equal to the factors in the highest benefit tier they qualify for (0084-VDPR-013). 1. If a party does not qualify for the lowest tier, their `volume_discount_factor`is set to `0` (0084-VDPR-014). +1. A `volume_discount_factors` tier with differing factors across the three options has each factor set correctly (0084-VDPR-018). diff --git a/protocol/0095-HVMR-high_volume_maker_rebate.md b/protocol/0095-HVMR-high_volume_maker_rebate.md new file mode 100644 index 000000000..21e43dbb2 --- /dev/null +++ b/protocol/0095-HVMR-high_volume_maker_rebate.md @@ -0,0 +1,26 @@ +# High Volume Maker Rebate + +The high volume maker rebate program is a network-wide community governed set of parameters to provide an additional reward to market makers on the network who are involved in a significant fraction of all trading on the network. When enabled, eligible market makers receive an additional fraction of trading fees from trades in which they are involved on top of any standard received maker fee (and would receive this even were the default maker fee removed). + +## Configuration + +The configuration of the High Volume Maker rebate is performed very similarly to that of the [volume discount program](./0084-VDPR-volume_discount_program.md): +Enabling or changing the terms of the program can be proposed via governance. As part of the proposal, the proposer specifies the following fields: + +- `benefit_tiers`: a list of dictionaries with the following fields + - `minimum_party_maker_volume_fraction`: the required `party_maker_volume_fraction` for a party to access this tier + - `additional_maker_rebate`: the additional rebate factor (in percentage of `trade_value_for_fee_purposes`) a party at this tier will receive when they are the maker side of a trade +- `end_of_program_timestamp`: the timestamp after which when the current epoch ends, the program will become inactive and benefits will be disabled. If this field is empty, the program runs indefinitely. +- `window_length`: the number of epochs over which to average a party's `maker_volume_fraction` + +## Application + +As variable fees for the taker depending upon with whom they are trading would not be a good experience, the additional maker rebate should be taken from a weighted combination of the network treasury and network buyback components of the total fee. The exact calculations are laid out in [0029-FEES](./0029-FEES-fees.md) but are broadly: + + 1. `high_volume_maker_fee = high_volume_factor * trade_value_for_fee_purposes` + 1. `treasury_fee = treasury_fee * (1 - high_volume_maker_fee / (treasury_fee + buyback_fee))` + 1. `buyback_fee = treasury_fee * (1 - buyback_fee / (treasury_fee + buyback_fee))` + +## Governance Requirements + +As the rebate possible level interacts with other fee settings there must be a restriction on it's possible values in governance change proposals. However, as both the rebate and the relevant fees could be changed at once the failure should occur at enactment of the proposal rather than initial validation. The criterion `max(additional_maker_rebate) <= market.fee.factors.treasuryFee + market.fee.factors.buybackFee` should be checked at changes of both the maker rebate program and the two fee factor values to ensure this constraint remains true. diff --git a/protocol/features.json b/protocol/features.json index 25ed39720..e3597fcf5 100644 --- a/protocol/features.json +++ b/protocol/features.json @@ -481,7 +481,6 @@ "0009-MRKP-037" ] }, - "Explicit liquidation range": { "milestone": "colosseo", "acs": [ @@ -523,8 +522,6 @@ "0004-AMND-061" ] }, - - "Reward Improvements": { "milestone": "colosseo", "acs": [ @@ -575,7 +572,6 @@ "0057-TRAN-079" ] }, - "Capped Futures": { "milestone": "colosseo_II", "acs": [ @@ -779,6 +775,14 @@ "0028-GOVE-191" ] }, + "Volume Discount Factor Customisation": { + "milestone": "colosseo_II", + "acs": [ + "0084-VDPR-015", + "0029-FEES-034", + "0029-FEES-035" + ] + }, "Perpetual funding rates": { "milestone": "historic_distillery", "acs": [ @@ -853,4 +857,4 @@ "milestone": "unknown", "acs": [] } -} +} \ No newline at end of file