From ba0dd68be04f6d1ff307ccfba4fc966696dbda14 Mon Sep 17 00:00:00 2001 From: c9s Date: Sun, 26 Mar 2023 01:54:39 +0800 Subject: [PATCH] xfunding: callcate funding fee --- pkg/strategy/xfunding/profitstats.go | 31 ++++++++++++++++++++++ pkg/strategy/xfunding/strategy.go | 39 +++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/pkg/strategy/xfunding/profitstats.go b/pkg/strategy/xfunding/profitstats.go index 80467a4ce..c72ad2a07 100644 --- a/pkg/strategy/xfunding/profitstats.go +++ b/pkg/strategy/xfunding/profitstats.go @@ -1 +1,32 @@ package xfunding + +import ( + "fmt" + + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type FundingFee struct { + Asset string `json:"asset"` + Amount fixedpoint.Value `json:"amount"` +} + +type ProfitStats struct { + *types.ProfitStats + + FundingFeeCurrency string `json:"fundingFeeCurrency"` + TotalFundingFee fixedpoint.Value `json:"totalFundingFee"` + FundingFeeRecords []FundingFee `json:"fundingFeeRecords"` +} + +func (s *ProfitStats) AddFundingFee(fee FundingFee) error { + s.FundingFeeRecords = append(s.FundingFeeRecords, fee) + s.TotalFundingFee = s.TotalFundingFee.Add(fee.Amount) + if s.FundingFeeCurrency == "" { + s.FundingFeeCurrency = fee.Asset + } else if s.FundingFeeCurrency != fee.Asset { + return fmt.Errorf("unexpected error, funding fee currency is not matched, given: %s, wanted: %s", fee.Asset, s.FundingFeeCurrency) + } + return nil +} diff --git a/pkg/strategy/xfunding/strategy.go b/pkg/strategy/xfunding/strategy.go index 42a8d7adc..d650cb4e2 100644 --- a/pkg/strategy/xfunding/strategy.go +++ b/pkg/strategy/xfunding/strategy.go @@ -119,7 +119,7 @@ type Strategy struct { FuturesSession string `json:"futuresSession"` Reset bool `json:"reset"` - ProfitStats *types.ProfitStats `persistence:"profit_stats"` + ProfitStats *ProfitStats `persistence:"profit_stats"` // SpotPosition is used for the spot position (usually long position) // so that we know how much spot we have bought and the average cost of the spot. @@ -260,7 +260,12 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order } if s.ProfitStats == nil || s.Reset { - s.ProfitStats = types.NewProfitStats(s.Market) + s.ProfitStats = &ProfitStats{ + ProfitStats: types.NewProfitStats(s.Market), + + // when receiving funding fee, the funding fee asset is the quote currency of that market. + FundingFeeCurrency: s.futuresMarket.QuoteCurrency, + } } if s.SpotPosition == nil || s.Reset { @@ -373,7 +378,35 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order case binance.AccountUpdateEventReasonWithdraw: case binance.AccountUpdateEventReasonFundingFee: + // EventBase:{ + // Event:ACCOUNT_UPDATE + // Time:1679760000932 + // } + // Transaction:1679760000927 + // AccountUpdate:{ + // EventReasonType:FUNDING_FEE + // Balances:[{ + // Asset:USDT + // WalletBalance:56.64251742 + // CrossWalletBalance:56.64251742 + // BalanceChange:-0.00037648 + // }] + // } + // } + for _, b := range e.AccountUpdate.Balances { + if b.Asset != s.ProfitStats.FundingFeeCurrency { + continue + } + err := s.ProfitStats.AddFundingFee(FundingFee{ + Asset: b.Asset, + Amount: b.BalanceChange, + }) + if err != nil { + log.WithError(err).Error("unable to add funding fee to profitStats") + } + } + bbgo.Sync(ctx, s) } }) } @@ -840,6 +873,6 @@ func (s *Strategy) allocateOrderExecutor(ctx context.Context, session *bbgo.Exch orderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) { bbgo.Sync(ctx, s) }) - orderExecutor.BindProfitStats(s.ProfitStats) + orderExecutor.BindProfitStats(s.ProfitStats.ProfitStats) return orderExecutor }