diff --git a/CHANGELOG.md b/CHANGELOG.md index 806c17fce3..405d97e912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,9 +27,9 @@ - [11583](https://github.com/vegaprotocol/vega/issues/11583) - Rough bound on price interval when matching with `AMMs` is now looser and calculated in the `AMM` engine. - [11624](https://github.com/vegaprotocol/vega/issues/11624) - prevent creation of rewards with no payout, but with high computational cost. - [11619](https://github.com/vegaprotocol/vega/issues/11619) - Fix `EstimatePositions` API for capped futures. +- [11652](https://github.com/vegaprotocol/vega/issues/11652) - Apply fees and discounts on network disposal trades. - [11645](https://github.com/vegaprotocol/vega/issues/11645) - Support party stats with no markets to retrieve for all markets. - ## 0.78.0 ### 🛠 Improvements diff --git a/core/execution/future/liquidation.go b/core/execution/future/liquidation.go index 2f3cb0b373..e98b92c316 100644 --- a/core/execution/future/liquidation.go +++ b/core/execution/future/liquidation.go @@ -62,7 +62,7 @@ func (m *Market) checkNetwork(ctx context.Context, now time.Time) error { return nil } // transfer fees to the good party -> fees are now taken from the insurance pool - fees, _ := m.fee.GetFeeForPositionResolution(conf.Trades) + fees, _ := m.fee.GetFeeForPositionResolution(conf.Trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) tresps, err := m.collateral.TransferFees(ctx, m.GetID(), m.settlementAsset, fees) if err != nil { // we probably should reject the order, although if we end up here we have a massive problem. diff --git a/core/fee/engine.go b/core/fee/engine.go index 652070abf8..364cee80ce 100644 --- a/core/fee/engine.go +++ b/core/fee/engine.go @@ -195,7 +195,7 @@ func (e *Engine) CalculateForContinuousMode( e.feesStats.RegisterMakerFee(maker, taker, fee.MakerFee) - totalTradingFees := num.UintZero().AddSum(fee.MakerFee, fee.InfrastructureFee, fee.LiquidityFee) + totalTradingFees := num.UintZero().AddSum(fee.MakerFee, fee.InfrastructureFee, fee.LiquidityFee, fee.BuyBackFee, fee.TreasuryFee, fee.HighVolumeMakerFee) switch trade.Aggressor { case types.SideBuy: @@ -451,26 +451,41 @@ func (e *Engine) CalculateForFrequentBatchesAuctionMode( }, nil } -func (e *Engine) GetFeeForPositionResolution(trades []*types.Trade) (events.FeesTransfer, *types.Fee) { +func (e *Engine) GetFeeForPositionResolution(trades []*types.Trade, + referral ReferralDiscountRewardService, + volumeDiscount VolumeDiscountService, + volumeRebate VolumeRebateService, +) (events.FeesTransfer, *types.Fee) { if len(trades) == 0 { return nil, nil } var ( netFee *types.Fee - gt *types.Transfer + gt []*types.Transfer ) transfers := make([]*types.Transfer, 0, len(trades)) for _, t := range trades { fees := e.calculateContinuousModeFees(t) + + maker := t.Buyer + if t.Buyer == types.NetworkParty { + maker = t.Seller + } + size := num.NewUint(t.Size) + // multiply by size + tradeValueForFee := size.Mul(t.Price, size).ToDecimal().Div(e.positionFactor) + postRewardDiscountFees, _ := e.applyDiscountsAndRewards(types.NetworkParty, maker, tradeValueForFee, fees, referral, volumeDiscount, volumeRebate) + e.feesStats.RegisterMakerFee(maker, types.NetworkParty, postRewardDiscountFees.MakerFee) + goodParty := t.Buyer - t.SellerFee = fees + t.SellerFee = postRewardDiscountFees if t.Buyer == types.NetworkParty { goodParty = t.Seller t.SellerFee = types.NewFee() - t.BuyerFee = fees + t.BuyerFee = postRewardDiscountFees } - netFee, gt = e.getNetworkFeeWithMakerTransfer(fees, netFee, goodParty) - transfers = append(transfers, gt) + netFee, gt = e.getNetworkFeeWithMakerTransfer(postRewardDiscountFees, netFee, goodParty) + transfers = append(transfers, gt...) } netTf, total := e.getNetworkFeeTransfers(netFee) // calculate the @@ -549,29 +564,44 @@ func (e *Engine) buildLiquidityFeesTransfer( return ft } -func (e *Engine) getNetworkFeeWithMakerTransfer(fees *types.Fee, current *types.Fee, goodParty string) (*types.Fee, *types.Transfer) { - transfer := &types.Transfer{ +func (e *Engine) getNetworkFeeWithMakerTransfer(fees *types.Fee, current *types.Fee, goodParty string) (*types.Fee, []*types.Transfer) { + transfers := []*types.Transfer{} + transfers = append(transfers, &types.Transfer{ Owner: goodParty, Amount: &types.FinancialAmount{ Asset: e.asset, - Amount: fees.MakerFee, + Amount: fees.MakerFee.Clone(), }, MinAmount: num.UintZero(), Type: types.TransferTypeMakerFeeReceive, + }) + if !fees.HighVolumeMakerFee.IsZero() { + transfers = append(transfers, &types.Transfer{ + Owner: goodParty, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fees.HighVolumeMakerFee, + }, + MinAmount: num.UintZero(), + Type: types.TransferTypeHighMakerRebateReceive, + }) } + if current == nil { - return fees.Clone(), transfer + return fees.Clone(), transfers } current.MakerFee.AddSum(fees.MakerFee) current.LiquidityFee.AddSum(fees.LiquidityFee) current.InfrastructureFee.AddSum(fees.InfrastructureFee) current.BuyBackFee.AddSum(fees.BuyBackFee) current.TreasuryFee.AddSum(fees.TreasuryFee) - return current, transfer + current.HighVolumeMakerFee.AddSum(fees.HighVolumeMakerFee) + + return current, transfers } func (e *Engine) getNetworkFeeTransfers(fees *types.Fee) ([]*types.Transfer, *num.Uint) { - return []*types.Transfer{ + transfers := []*types.Transfer{ { Owner: types.NetworkParty, Amount: &types.FinancialAmount{ @@ -617,7 +647,20 @@ func (e *Engine) getNetworkFeeTransfers(fees *types.Fee) ([]*types.Transfer, *nu MinAmount: num.UintZero(), Type: types.TransferTypeTreasuryPay, }, - }, num.Sum(fees.MakerFee, fees.InfrastructureFee, fees.LiquidityFee) + } + if !fees.HighVolumeMakerFee.IsZero() { + transfers = append(transfers, &types.Transfer{ + Owner: types.NetworkParty, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fees.HighVolumeMakerFee.Clone(), + }, + MinAmount: num.UintZero(), + Type: types.TransferTypeHighMakerRebatePay, + }) + } + + return transfers, num.Sum(fees.MakerFee, fees.InfrastructureFee, fees.LiquidityFee, fees.BuyBackFee, fees.HighVolumeMakerFee) } func (e *Engine) applyDiscountsAndRewards(taker string, maker string, tradeValueForFeePurposes num.Decimal, fees *types.Fee, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, volumeRebate VolumeRebateService) (*types.Fee, *types.ReferrerReward) { diff --git a/core/fee/engine_test.go b/core/fee/engine_test.go index e9b145ceb5..8d57b416b6 100644 --- a/core/fee/engine_test.go +++ b/core/fee/engine_test.go @@ -446,8 +446,8 @@ func testCalcContinuousTradingAndCheckAmountsExtended(t *testing.T) { TotalFeesPaidAndReceived: []*eventspb.PartyAmount{ { Party: "party1", - Amount: "875", - QuantumAmount: "875", + Amount: "3375", + QuantumAmount: "3375", }, { Party: "party2", @@ -744,8 +744,8 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountExtended(t *testing.T) TotalFeesPaidAndReceived: []*eventspb.PartyAmount{ { Party: "party1", - Amount: "443", - QuantumAmount: "443", + Amount: "2943", + QuantumAmount: "2943", }, { Party: "party2", @@ -1204,7 +1204,7 @@ func testCalcContinuousTradingExtended(t *testing.T) { feeAmounts := ft.TotalFeesAmountPerParty() party1Amount, ok := feeAmounts["party1"] assert.True(t, ok) - assert.Equal(t, num.NewUint(43928), party1Amount) + assert.Equal(t, num.NewUint(45221), party1Amount) // get the transfer and check we have enough of each types transfers := ft.Transfers() @@ -1670,12 +1670,17 @@ func testCalcBatchAuctionTradingDifferentBatches(t *testing.T) { func testCloseoutFees(t *testing.T) { eng := getTestFee(t) ctrl := gomock.NewController(t) + referralDiscountService := mocks.NewMockReferralDiscountRewardService(ctrl) + referralDiscountService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() + referralDiscountService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() trades := []*types.Trade{ { Aggressor: types.SideSell, @@ -1707,10 +1712,10 @@ func testCloseoutFees(t *testing.T) { }, } - ft, fee := eng.GetFeeForPositionResolution(trades) + ft, fee := eng.GetFeeForPositionResolution(trades, referralDiscountService, volumeDiscountService, volumeRebateService) assert.NotNil(t, fee) allTransfers := ft.Transfers() - // first we have the network -> pay transfers, then 1 transfer per good party + // first we have the network -> pay transfers , then 1 transfer per good party // two additional transfers for the buy back and treasury fees assert.Equal(t, len(trades), len(allTransfers)-5) goodPartyTransfers := allTransfers[5:]