From aed34a0ef98378b6a10811a9c90fa114becb8b1d Mon Sep 17 00:00:00 2001 From: ze97286 Date: Thu, 12 Sep 2024 21:36:35 +0100 Subject: [PATCH] fix: correct calculation of party market fees --- CHANGELOG.md | 3 +- datanode/service/party_stats.go | 74 +++++++++++++++++++--------- datanode/service/party_stats_test.go | 72 +++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 24 deletions(-) create mode 100644 datanode/service/party_stats_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 152a269303..1e2dd2afb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/datanode/service/party_stats.go b/datanode/service/party_stats.go index 63c7a56ad8..3a517c3a1f 100644 --- a/datanode/service/party_stats.go +++ b/datanode/service/party_stats.go @@ -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 { @@ -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)) @@ -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 { @@ -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 { @@ -159,51 +161,53 @@ 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) + 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(rebate).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 } @@ -213,44 +217,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 } diff --git a/datanode/service/party_stats_test.go b/datanode/service/party_stats_test.go new file mode 100644 index 0000000000..ce3b2a1be0 --- /dev/null +++ b/datanode/service/party_stats_test.go @@ -0,0 +1,72 @@ +// 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 . + +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) + require.Equal(t, "0.0052", data.PartyMarketFees[0].UserMakerRebate) +}