diff --git a/core/integration/features/0084-VDPR-012.feature b/core/integration/features/0084-VDPR-012.feature index 47ddde01a1..2eebd9dcce 100644 --- a/core/integration/features/0084-VDPR-012.feature +++ b/core/integration/features/0084-VDPR-012.feature @@ -10,28 +10,28 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval | 0.000001 | 0.1 | 0 | 0 | 1.0 | Given the liquidity monitoring parameters: - | name | triggering ratio | time window | scaling factor | - | lqm-params | 1.0 | 20s | 1.0 | + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.0 | 20s | 1.0 | And the following network parameters are set: - | name | value | - | market.value.windowLength | 60s | - | network.markPriceUpdateMaximumFrequency | 0s | - | limits.markets.maxPeggedOrders | 6 | - | market.auction.minimumDuration | 1 | - | market.fee.factors.infrastructureFee | 0.001 | - | market.fee.factors.makerFee | 0.004 | + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | #risk factor short:3.5569036 #risk factor long:0.801225765 And the volume discount program tiers named "VDP-01": - | volume | factor | - | 1000 | 0.001 | - | 2000 | 0.005 | - | 3000 | 0.010 | + | volume | factor | + | 1000 | 0.001 | + | 2000 | 0.005 | + | 3000 | 0.010 | And the volume discount program: - | id | tiers | closing timestamp | window length | - | id1 | VDP-01 | 0 | 4 | + | id | tiers | closing timestamp | window length | + | id1 | VDP-01 | 0 | 4 | And the following assets are registered: | id | decimal places | @@ -52,29 +52,29 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval | ETH/MAR24 | ETH | ETH | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | price-monitoring | default-eth-for-future | 1e0 | 0 | SLA-22 | And the following network parameters are set: - | name | value | - | market.liquidity.bondPenaltyParameter | 0.2 | - | validators.epoch.length | 5s | - | market.liquidity.stakeToCcyVolume | 1 | - | market.liquidity.successorLaunchWindowLength | 1h | - | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.7 | - | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | - | validators.epoch.length | 10s | - | market.liquidity.earlyExitPenalty | 0.25 | + | name | value | + | market.liquidity.bondPenaltyParameter | 0.2 | + | validators.epoch.length | 5s | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.7 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | Given the average block duration is "1" @Now Scenario: 001: Check that the volume discount factor is updated after each epoch Given the parties deposit on asset's general account the following amount: - | party | asset | amount | + | party | asset | amount | | lp1 | ETH | 10000000 | | party1 | ETH | 10000000 | | party2 | ETH | 10000000 | | party3 | ETH | 10000000 | And the parties submit the following liquidity provision: - | id | party | market id | commitment amount | fee | lp type | - | lp_1 | lp1 | ETH/MAR24 | 100000 | 0.02 | submission | + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR24 | 100000 | 0.02 | submission | Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | @@ -95,25 +95,15 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval And the party "party3" has the following discount factor "0" Then the parties place the following orders: - | party | market id | side | volume | price | resulting trades | type | tif | - | party3 | ETH/MAR24 | buy | 2 | 0 | 1 | TYPE_MARKET | TIF_IOC | - | party3 | ETH/MAR24 | sell | 2 | 0 | 1 | TYPE_MARKET | TIF_IOC | - And the market data for the market "ETH/MAR24" should be: - | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | - | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 10670 | 100000 | 1 | - When the network moves ahead "1" epochs - And the party "party3" has the following discount factor "0.001" - - Then the parties place the following orders: - | party | market id | side | volume | price | resulting trades | type | tif | - | party3 | ETH/MAR24 | buy | 2 | 0 | 1 | TYPE_MARKET | TIF_IOC | - | party3 | ETH/MAR24 | sell | 2 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs And the party "party3" has the following discount factor "0.005" Then the parties place the following orders: - | party | market id | side | volume | price | resulting trades | type | tif | - | party3 | ETH/MAR24 | buy | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | - | party3 | ETH/MAR24 | sell | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | buy | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs And the party "party3" has the following discount factor "0.01" diff --git a/core/integration/features/0084-VDPR-013.feature b/core/integration/features/0084-VDPR-013.feature index 100447fc94..1388f491bf 100644 --- a/core/integration/features/0084-VDPR-013.feature +++ b/core/integration/features/0084-VDPR-013.feature @@ -98,29 +98,18 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | - | party3 | ETH/MAR24 | buy | 3 | 0 | 1 | TYPE_MARKET | TIF_IOC | - | party3 | ETH/MAR24 | sell | 3 | 0 | 1 | TYPE_MARKET | TIF_IOC | - And the market data for the market "ETH/MAR24" should be: - | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | - | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 14227 | 100000 | 1 | + | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs - And the party "party3" has the following taker notional "1500" - And the party "party3" has the following discount factor "0.01" - - Then the parties place the following orders: - | party | market id | side | volume | price | resulting trades | type | tif | - | party3 | ETH/MAR24 | buy | 2 | 0 | 1 | TYPE_MARKET | TIF_IOC | - | party3 | ETH/MAR24 | sell | 2 | 0 | 1 | TYPE_MARKET | TIF_IOC | - When the network moves ahead "1" epochs - And the party "party3" has the following taker notional "2500" + And the party "party3" has the following taker notional "2000" And the party "party3" has the following discount factor "0.02" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | - | party3 | ETH/MAR24 | buy | 5 | 0 | 1 | TYPE_MARKET | TIF_IOC | - | party3 | ETH/MAR24 | sell | 5 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs - And the party "party3" has the following taker notional "5000" + And the party "party3" has the following taker notional "4000" And the party "party3" has the following discount factor "0.03" # now that party3 has a discount, lets do a trade with fees @@ -152,7 +141,7 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig | party3 | ETH/MAR24 | sell | 100 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | And the parties submit the following liquidity provision: - | id | party | market id | commitment amount | fee | lp type | + | id | party | market id | commitment amount | fee | lp type | | lp1 | lp1 | ETH/MAR24 | 1000000 | 0.02 | submission | When the network moves ahead "1" epochs Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH/MAR24" @@ -161,6 +150,6 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig # infra fee discount - floor(50 *0.03) = 1 # no lp fee Then the following transfers should happen: - | from | to | from account | to account | market id | amount | asset | - | party3 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_FEES_INFRASTRUCTURE | | 49 | ETH | - | party1 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_FEES_INFRASTRUCTURE | | 50 | ETH | + | from | to | from account | to account | market id | amount | asset | + | party3 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_FEES_INFRASTRUCTURE | | 49 | ETH | + | party1 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_FEES_INFRASTRUCTURE | | 50 | ETH | diff --git a/core/integration/features/0084-VDPR-014.feature b/core/integration/features/0084-VDPR-014.feature index fd66c4cb34..08263740bb 100644 --- a/core/integration/features/0084-VDPR-014.feature +++ b/core/integration/features/0084-VDPR-014.feature @@ -9,27 +9,27 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ | risk aversion | tau | mu | r | sigma | | 0.000001 | 0.1 | 0 | 0 | 1.0 | Given the liquidity monitoring parameters: - | name | triggering ratio | time window | scaling factor | - | lqm-params | 1.0 | 20s | 1.0 | + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.0 | 20s | 1.0 | And the following network parameters are set: - | name | value | - | market.value.windowLength | 60s | - | network.markPriceUpdateMaximumFrequency | 0s | - | limits.markets.maxPeggedOrders | 6 | - | market.auction.minimumDuration | 1 | - | market.fee.factors.infrastructureFee | 0.001 | - | market.fee.factors.makerFee | 0.004 | + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | #risk factor short:3.5569036 #risk factor long:0.801225765 And the volume discount program tiers named "VDP-01": - | volume | factor | - | 1000 | 0.01 | - | 2000 | 0.02 | - | 3000 | 0.03 | + | volume | factor | + | 3000 | 0.01 | + | 4000 | 0.02 | + | 5000 | 0.03 | And the volume discount program: - | id | tiers | closing timestamp | window length | - | id1 | VDP-01 | 0 | 4 | + | id | tiers | closing timestamp | window length | + | id1 | VDP-01 | 0 | 4 | And the following assets are registered: | id | decimal places | @@ -50,29 +50,29 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ | ETH/MAR24 | ETH | ETH | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | price-monitoring | default-eth-for-future | 1e0 | 0 | SLA-22 | And the following network parameters are set: - | name | value | - | market.liquidity.bondPenaltyParameter | 0.2 | - | validators.epoch.length | 5s | - | market.liquidity.stakeToCcyVolume | 1 | - | market.liquidity.successorLaunchWindowLength | 1h | - | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.7 | - | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | - | validators.epoch.length | 10s | - | market.liquidity.earlyExitPenalty | 0.25 | + | name | value | + | market.liquidity.bondPenaltyParameter | 0.2 | + | validators.epoch.length | 5s | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.7 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | Given the average block duration is "1" @Now Scenario: 001: If a trader doesn't touch or pass the first tier, they get no discount Given the parties deposit on asset's general account the following amount: - | party | asset | amount | + | party | asset | amount | | lp1 | ETH | 10000000 | | party1 | ETH | 10000000 | | party2 | ETH | 10000000 | | party3 | ETH | 10000000 | And the parties submit the following liquidity provision: - | id | party | market id | commitment amount | fee | lp type | - | lp_1 | lp1 | ETH/MAR24 | 100000 | 0.02 | submission | + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR24 | 100000 | 0.02 | submission | Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | @@ -94,14 +94,14 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ And the party "party3" has the following discount factor "0" Then the parties place the following orders: - | party | market id | side | volume | price | resulting trades | type | tif | - | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | - | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | And the market data for the market "ETH/MAR24" should be: | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 7113 | 100000 | 1 | When the network moves ahead "1" epochs # The taker trades above are not enough for party3 to hit the first level of the discount tier # so we get a zero for the discount factor - And the party "party3" has the following taker notional "500" + And the party "party3" has the following taker notional "2000" And the party "party3" has the following discount factor "0" diff --git a/core/volumediscount/engine.go b/core/volumediscount/engine.go index 2ae04d3354..575d1f62f7 100644 --- a/core/volumediscount/engine.go +++ b/core/volumediscount/engine.go @@ -155,7 +155,6 @@ func (e *Engine) notifyVolumeDiscountProgramEnded(ctx context.Context, epochTime } func (e *Engine) calculatePartiesVolumeForWindow(windowSize int) { - windowSizeAsDecimal := num.DecimalFromInt64(int64(windowSize)) for pi := range e.parties { total := num.UintZero() for i := 0; i < windowSize; i++ { @@ -165,7 +164,7 @@ func (e *Engine) calculatePartiesVolumeForWindow(windowSize int) { } total.AddSum(valueForEpoch) } - e.avgVolumePerParty[pi] = total.ToDecimal().Div(windowSizeAsDecimal) + e.avgVolumePerParty[pi] = total.ToDecimal() } } diff --git a/core/volumediscount/engine_test.go b/core/volumediscount/engine_test.go index 121ab784f3..ff6fe76efb 100644 --- a/core/volumediscount/engine_test.go +++ b/core/volumediscount/engine_test.go @@ -285,20 +285,20 @@ func TestDiscountFactorWithWindow(t *testing.T) { // party does not exist require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p8")) - // party is not eligible + // volume 900 require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p1")) - // over a window of 2 party2 has 500 - require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p2")) - // over a window of 2 party2 has 500.5 - require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p3")) - // average volume 1000 - require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p4").String()) - // average volume 1500 - require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p5").String()) - // average volume 2000 - require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p6").String()) - // average volume 2500 - require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p7").String()) + // volume 1000 + require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p2").String()) + // volume 1001 + require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p3").String()) + // volume 2000 + require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p4").String()) + // volume 3000 + require.Equal(t, "0.5", engine.VolumeDiscountFactorForParty("p5").String()) + // volume 4000 + require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p6").String()) + // volume 5000 + require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p7").String()) // running for another epoch marketActivityTracker.EXPECT().NotionalTakerVolumeForAllParties().Return(map[types.PartyID]*num.Uint{ @@ -319,30 +319,30 @@ func TestDiscountFactorWithWindow(t *testing.T) { require.NoError(t, err) loadedEngine := assertSnapshotMatches(t, key, hashAfter2Epochs) - // now p8 exists and the average notional is 1000 - require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p8").String()) - require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p8").String()) - // party1 now has a total of 2000 with average of 1000 they're not eligible - require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p1").String()) - require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p1").String()) - // over a window of 2 party2 has 500 so not eligible - require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p2")) - require.Equal(t, num.DecimalZero(), loadedEngine.VolumeDiscountFactorForParty("p2")) - // over a window of 2 party2 has 500.5 so not eligible - require.Equal(t, num.DecimalZero(), engine.VolumeDiscountFactorForParty("p3")) - require.Equal(t, num.DecimalZero(), loadedEngine.VolumeDiscountFactorForParty("p3")) - // average volume 1000 - require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p4").String()) - require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p4").String()) - // average volume 3500 - require.Equal(t, "0.5", engine.VolumeDiscountFactorForParty("p5").String()) - require.Equal(t, "0.5", loadedEngine.VolumeDiscountFactorForParty("p5").String()) - // average volume 4000 + // now p8 exists and the volume is 2000 + require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p8").String()) + require.Equal(t, "0.2", loadedEngine.VolumeDiscountFactorForParty("p8").String()) + // volume 2400 + require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p1").String()) + require.Equal(t, "0.2", loadedEngine.VolumeDiscountFactorForParty("p1").String()) + // volume 1000 + require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p2").String()) + require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p2").String()) + // volume 1001 + require.Equal(t, "0.1", engine.VolumeDiscountFactorForParty("p3").String()) + require.Equal(t, "0.1", loadedEngine.VolumeDiscountFactorForParty("p3").String()) + // volume 2000 + require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p4").String()) + require.Equal(t, "0.2", loadedEngine.VolumeDiscountFactorForParty("p4").String()) + // volume 7000 + require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p5").String()) + require.Equal(t, "1", loadedEngine.VolumeDiscountFactorForParty("p5").String()) + // volume 8000 require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p6").String()) require.Equal(t, "1", loadedEngine.VolumeDiscountFactorForParty("p6").String()) - // average volume 2500 - require.Equal(t, "0.2", engine.VolumeDiscountFactorForParty("p7").String()) - require.Equal(t, "0.2", loadedEngine.VolumeDiscountFactorForParty("p7").String()) + // volume 5000 + require.Equal(t, "1", engine.VolumeDiscountFactorForParty("p7").String()) + require.Equal(t, "1", loadedEngine.VolumeDiscountFactorForParty("p7").String()) marketActivityTracker.EXPECT().NotionalTakerVolumeForAllParties().Return(map[types.PartyID]*num.Uint{}).Times(1)