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

fix: correct calculation of party market fees #11680

Merged
merged 2 commits into from
Sep 13, 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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@
- [11650](https://github.com/vegaprotocol/vega/issues/11650) - Add include sub accounts flag to `listPositions`.
- [11641](https://github.com/vegaprotocol/vega/issues/11641) - Panic with pegged orders.
- [11646](https://github.com/vegaprotocol/vega/issues/11646) - Add tier numbers to API.
- [11665](https://github.com/vegaprotocol/vega/issues/11665) - Delay the final termination of a transfer to the following epoch.
- [11679](https://github.com/vegaprotocol/vega/issues/11679) - Fix calculation of fees in party `stats`.
- [11665](https://github.com/vegaprotocol/vega/issues/11665) - Delay the final termination of a transfer to the following epoch.


## 0.78.1

### 🐛 Fixes
Expand Down
76 changes: 53 additions & 23 deletions datanode/service/party_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ type partyFeeFactors struct {
maker num.Decimal
infra num.Decimal
liquidity num.Decimal
rebate num.Decimal
}

func NewPartyStatsService(epoch EpochStore, ref ReferralSetStore, vds VDSStore, vrs VRSStore, mkt MktStore, rp RPStore, vd VDStore, vr VRStore) *PSvc {
Expand Down Expand Up @@ -118,7 +117,10 @@ func (s *PSvc) GetPartyStats(ctx context.Context, partyID string, markets []stri
lastE := uint64(epoch.ID - 1)

data := &v2.GetPartyDiscountStatsResponse{}
pfFactors := partyFeeFactors{}
rfDiscountFactors := partyFeeFactors{}
rfRewardFactors := partyFeeFactors{}
vdDiscountFactors := partyFeeFactors{}

// now that we've gotten the epoch and all markets, get the party stats.
// 1. referral set stats.
refStats, _, err := s.ref.GetReferralSetStats(ctx, nil, &lastE, ptr.From(entities.PartyID(partyID)), entities.DefaultCursorPagination(true))
Expand All @@ -130,7 +132,7 @@ func (s *PSvc) GetPartyStats(ctx context.Context, partyID string, markets []stri
if err != nil {
return nil, err
}
if err := addRefFeeFactors(&pfFactors, refStats[0]); err != nil {
if err := setRefFeeFactors(&rfRewardFactors, &rfDiscountFactors, refStats[0]); err != nil {
return nil, err
}
if tier != nil {
Expand All @@ -147,7 +149,7 @@ func (s *PSvc) GetPartyStats(ctx context.Context, partyID string, markets []stri
if err != nil {
return nil, err
}
if err := addVolFeeFactors(&pfFactors, vdStats[0]); err != nil {
if err := setVolFeeFactors(&vdDiscountFactors, vdStats[0]); err != nil {
return nil, err
}
if tier != nil {
Expand All @@ -159,51 +161,55 @@ func (s *PSvc) GetPartyStats(ctx context.Context, partyID string, markets []stri
if err != nil {
return nil, err
}
var rebate num.Decimal
if len(vrStats) > 0 {
tier, err := s.getVolumeRebateTier(ctx, vrStats[0])
if err != nil {
return nil, err
}
rebate, err := num.DecimalFromString(vrStats[0].AdditionalRebate)
rebate, err = num.DecimalFromString(vrStats[0].AdditionalRebate)
if err != nil {
return nil, err
}
pfFactors.rebate = rebate
if tier != nil {
data.VolumeRebateTier = *tier.TierNumber
}
}
for _, mkt := range mkts {
// @TODO ensure non-nil slice!
if err := setMarketFees(data, mkt, pfFactors); err != nil {
if err := setMarketFees(data, mkt, rfDiscountFactors, rfRewardFactors, vdDiscountFactors, rebate); err != nil {
return nil, err
}
}
return data, nil
}

func setMarketFees(data *v2.GetPartyDiscountStatsResponse, mkt entities.Market, factors partyFeeFactors) error {
maker, infra, liquidity, err := feeFactors(mkt)
func setMarketFees(data *v2.GetPartyDiscountStatsResponse, mkt entities.Market, rfDiscount, rfRewards, vdFactors partyFeeFactors, rebate num.Decimal) error {
maker, infra, liquidity, bb, treasury, err := feeFactors(mkt)
if err != nil {
return err
}
// undiscounted
base := num.DecimalZero().Add(maker).Add(infra).Add(liquidity)
base := num.DecimalZero().Add(maker).Add(infra).Add(liquidity).Add(bb).Add(treasury)
// discounted
discounted := num.DecimalZero().Add(maker.Sub(factors.maker)).
Add(infra.Sub(factors.infra)).
Add(liquidity.Sub(factors.liquidity))
discountedMaker := maker.Mul(num.DecimalOne().Sub(rfDiscount.maker)).Mul(num.DecimalOne().Sub(vdFactors.maker)).Mul(num.DecimalOne().Sub(rfRewards.maker))
discountedInfra := infra.Mul(num.DecimalOne().Sub(rfDiscount.infra)).Mul(num.DecimalOne().Sub(vdFactors.infra)).Mul(num.DecimalOne().Sub(rfRewards.infra))
discountedLiquidity := liquidity.Mul(num.DecimalOne().Sub(rfDiscount.liquidity)).Mul(num.DecimalOne().Sub(vdFactors.liquidity)).Mul(num.DecimalOne().Sub(rfRewards.liquidity))
discounted := discountedMaker.Add(discountedInfra).Add(discountedLiquidity).Add(bb).Add(treasury)

effRebate := num.MinD(rebate, bb.Add(treasury))

data.PartyMarketFees = append(data.PartyMarketFees, &v2.MarketFees{
MarketId: mkt.ID.String(),
UndiscountedTakerFee: base.String(),
DiscountedTakerFee: discounted.String(),
BaseMakerRebate: maker.String(),
UserMakerRebate: maker.Add(factors.rebate).String(),
UserMakerRebate: maker.Add(effRebate).String(),
})
return nil
}

func feeFactors(mkt entities.Market) (maker, infra, liquidity num.Decimal, err error) {
func feeFactors(mkt entities.Market) (maker, infra, liquidity, bb, treasury num.Decimal, err error) {
if maker, err = num.DecimalFromString(mkt.Fees.Factors.MakerFee); err != nil {
return
}
Expand All @@ -213,44 +219,68 @@ func feeFactors(mkt entities.Market) (maker, infra, liquidity num.Decimal, err e
if liquidity, err = num.DecimalFromString(mkt.Fees.Factors.LiquidityFee); err != nil {
return
}
if bb, err = num.DecimalFromString(mkt.Fees.Factors.BuyBackFee); err != nil {
return
}
if treasury, err = num.DecimalFromString(mkt.Fees.Factors.TreasuryFee); err != nil {
return
}

return
}

func addRefFeeFactors(ff *partyFeeFactors, stats entities.FlattenReferralSetStats) error {
func setRefFeeFactors(rewards, discounts *partyFeeFactors, stats entities.FlattenReferralSetStats) error {
maker, err := num.DecimalFromString(stats.RewardFactors.MakerRewardFactor)
if err != nil {
return err
}
ff.maker = ff.maker.Add(maker)
rewards.maker = maker
infra, err := num.DecimalFromString(stats.RewardFactors.InfrastructureRewardFactor)
if err != nil {
return err
}
ff.infra = ff.infra.Add(infra)
rewards.infra = infra
liquidity, err := num.DecimalFromString(stats.RewardFactors.LiquidityRewardFactor)
if err != nil {
return err
}
ff.liquidity = ff.liquidity.Add(liquidity)
rewards.liquidity = liquidity

dmaker, err := num.DecimalFromString(stats.DiscountFactors.MakerDiscountFactor)
if err != nil {
return err
}
discounts.maker = dmaker
dinfra, err := num.DecimalFromString(stats.DiscountFactors.InfrastructureDiscountFactor)
if err != nil {
return err
}
discounts.infra = dinfra
dliquidity, err := num.DecimalFromString(stats.DiscountFactors.LiquidityDiscountFactor)
if err != nil {
return err
}
discounts.liquidity = dliquidity

return nil
}

func addVolFeeFactors(ff *partyFeeFactors, stats entities.FlattenVolumeDiscountStats) error {
func setVolFeeFactors(ff *partyFeeFactors, stats entities.FlattenVolumeDiscountStats) error {
maker, err := num.DecimalFromString(stats.DiscountFactors.MakerDiscountFactor)
if err != nil {
return err
}
ff.maker = ff.maker.Add(maker)
ff.maker = maker
infra, err := num.DecimalFromString(stats.DiscountFactors.InfrastructureDiscountFactor)
if err != nil {
return err
}
ff.infra = ff.infra.Add(infra)
ff.infra = infra
liquidity, err := num.DecimalFromString(stats.DiscountFactors.LiquidityDiscountFactor)
if err != nil {
return err
}
ff.liquidity = ff.liquidity.Add(liquidity)
ff.liquidity = liquidity
return nil
}

Expand Down
73 changes: 73 additions & 0 deletions datanode/service/party_stats_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (C) 2023 Gobalsky Labs Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package service

import (
"testing"

"code.vegaprotocol.io/vega/datanode/entities"
"code.vegaprotocol.io/vega/libs/num"
v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2"

"github.com/stretchr/testify/require"
)

func TestPartyFees(t *testing.T) {
data := &v2.GetPartyDiscountStatsResponse{}
mkt := entities.Market{
ID: entities.MarketID("1234"),
Fees: entities.Fees{
Factors: &entities.FeeFactors{
MakerFee: "0.0002",
InfrastructureFee: "0.0005",
LiquidityFee: "0.00001",
TreasuryFee: "0.0003",
BuyBackFee: "0.0001",
},
},
}
rfDiscount := partyFeeFactors{
maker: num.DecimalFromFloat(0.01),
infra: num.DecimalFromFloat(0.02),
liquidity: num.DecimalFromFloat(0.03),
}
rfReward := partyFeeFactors{
maker: num.DecimalFromFloat(0.001),
infra: num.DecimalFromFloat(0.002),
liquidity: num.DecimalFromFloat(0.003),
}
vdFactors := partyFeeFactors{
maker: num.DecimalFromFloat(0.0001),
infra: num.DecimalFromFloat(0.0002),
liquidity: num.DecimalFromFloat(0.0003),
}
rebate := num.DecimalFromFloat(0.005)
setMarketFees(data, mkt, rfDiscount, rfReward, vdFactors, rebate)
require.Equal(t, 1, len(data.PartyMarketFees))
// 0.0002 + 0.0005 + 0.00001 + 0.0003 + 0.0001
require.Equal(t, "0.00111", data.PartyMarketFees[0].UndiscountedTakerFee)

// 0.0002 * (1-0.01) * (1 - 0.0001) * (1 - 0.001) +
// 0.0005 * (1-0.02) * (1 - 0.0002) * (1 - 0.002) +
// 0.00001 * (1-0.03) * (1 - 0.0003) * (1 - 0.003) +
// 0.0003 +
// 0.0001 =
// 0.0001977822198 + 0.000488922196 + 0.00000966799873 + 0.0003 + 0.0001 = 0.00109637241453
require.Equal(t, "0.00109637241453", data.PartyMarketFees[0].DiscountedTakerFee)
require.Equal(t, "0.0002", data.PartyMarketFees[0].BaseMakerRebate)
// effective rebate is min(0.0003+0.0001=0.0004, 0.005) = 0.0004 + 0.0002 = 0.0006
require.Equal(t, "0.0006", data.PartyMarketFees[0].UserMakerRebate)
}
Loading