diff --git a/migrations/mysql/20240103175426_add_funding_fees_table.sql b/migrations/mysql/20240103175426_add_funding_fees_table.sql new file mode 100644 index 000000000..703e308db --- /dev/null +++ b/migrations/mysql/20240103175426_add_funding_fees_table.sql @@ -0,0 +1,31 @@ +-- +up +-- +begin +CREATE TABLE `funding_fees` +( + `gid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + + -- transaction id + `txn_id` BIGINT UNSIGNED NOT NULL, + + -- for exchange + `exchange` VARCHAR(24) NOT NULL DEFAULT '', + + -- asset name, BTC, MAX, USDT ... etc + `asset` VARCHAR(5) NOT NULL, + + -- the amount of the funding fee + `amount` DECIMAL(16, 8) NOT NULL, + + `funded_at` DATETIME NOT NULL, + + PRIMARY KEY (`gid`), + + UNIQUE KEY `txn_id` (`txn_id`, `exchange`) +); +-- +end + +-- +down + +-- +begin +DROP TABLE `funding_fees`; +-- +end diff --git a/migrations/sqlite3/20240103175426_add_funding_fees_table.sql b/migrations/sqlite3/20240103175426_add_funding_fees_table.sql new file mode 100644 index 000000000..0f1dc6708 --- /dev/null +++ b/migrations/sqlite3/20240103175426_add_funding_fees_table.sql @@ -0,0 +1,31 @@ +-- +up +-- +begin +CREATE TABLE `funding_fees` +( + `gid` INTEGER PRIMARY KEY AUTOINCREMENT, + + -- transaction id + `txn_id` INTEGER NOT NULL, + + -- for exchange + `exchange` VARCHAR(24) NOT NULL DEFAULT '', + + -- asset name, BTC, MAX, USDT ... etc + `asset` VARCHAR(5) NOT NULL, + + -- the amount of the funding fee + `amount` DECIMAL(16, 8) NOT NULL, + + `funded_at` DATETIME NOT NULL, + + PRIMARY KEY (`gid`), + + UNIQUE KEY `txn_id` (`txn_id`, `exchange`) +); +-- +end + +-- +down + +-- +begin +DROP TABLE `funding_fees`; +-- +end diff --git a/pkg/strategy/xfunding/profitstats.go b/pkg/strategy/xfunding/profitstats.go index 6ae2780b5..0a7700cf2 100644 --- a/pkg/strategy/xfunding/profitstats.go +++ b/pkg/strategy/xfunding/profitstats.go @@ -17,8 +17,13 @@ type ProfitStats struct { FundingFeeCurrency string `json:"fundingFeeCurrency"` TotalFundingFee fixedpoint.Value `json:"totalFundingFee"` FundingFeeRecords []FundingFee `json:"fundingFeeRecords"` - LastFundingFeeTxn int64 `json:"lastFundingFeeTxn"` - LastFundingFeeTime time.Time `json:"lastFundingFeeTime"` + + // Fees map[string] + Last *FundingFee `json:"last"` + + LastFundingFeeTime time.Time `json:"lastFundingFeeTime"` + + txns map[int64]struct{} } func (s *ProfitStats) SlackAttachment() slack.Attachment { @@ -31,24 +36,29 @@ func (s *ProfitStats) SlackAttachment() slack.Attachment { // Pretext: "", // Text: text, Fields: fields, - Footer: fmt.Sprintf("Last Funding Fee Transation ID: %d Last Funding Fee Time %s", s.LastFundingFeeTxn, s.LastFundingFeeTime.Format(time.RFC822)), + Footer: fmt.Sprintf("Last Funding Fee Transation ID: %d Last Funding Fee Time %s", s.Last.Txn, s.Last.Time.Format(time.RFC822)), } } func (s *ProfitStats) AddFundingFee(fee FundingFee) error { + if s.txns == nil { + s.txns = make(map[int64]struct{}) + } + 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) } - if s.LastFundingFeeTxn == fee.Txn { + if _, ok := s.txns[fee.Txn]; ok { return errDuplicatedFundingFeeTxnId } s.FundingFeeRecords = append(s.FundingFeeRecords, fee) s.TotalFundingFee = s.TotalFundingFee.Add(fee.Amount) - s.LastFundingFeeTxn = fee.Txn - s.LastFundingFeeTime = fee.Time + s.Last = &fee + + s.txns[fee.Txn] = struct{}{} return nil } diff --git a/pkg/strategy/xfunding/strategy.go b/pkg/strategy/xfunding/strategy.go index 50d1b392b..ddc85f5d6 100644 --- a/pkg/strategy/xfunding/strategy.go +++ b/pkg/strategy/xfunding/strategy.go @@ -298,7 +298,6 @@ func (s *Strategy) CrossRun( FundingFeeCurrency: s.futuresMarket.QuoteCurrency, TotalFundingFee: fixedpoint.Zero, FundingFeeRecords: nil, - LastFundingFeeTime: time.Time{}, } } @@ -379,10 +378,9 @@ func (s *Strategy) CrossRun( bbgo.Notify("State: %s", s.State.PositionState.String()) // sync funding fee txns - s.syncFundingFeeRecords(ctx, s.ProfitStats.LastFundingFeeTime) + s.syncFundingFeeRecords(ctx, time.Now().Add(-5*24*time.Hour)) - // TEST CODE: - // s.syncFundingFeeRecords(ctx, time.Now().Add(-3*24*time.Hour)) + bbgo.Notify(s.ProfitStats) switch s.State.PositionState { case PositionClosed: @@ -833,9 +831,6 @@ func (s *Strategy) syncFuturesPosition(ctx context.Context) { bbgo.Notify("SpotPosition", s.SpotPosition) bbgo.Notify("FuturesPosition", s.FuturesPosition) bbgo.Notify("NeutralPosition", s.NeutralPosition) - - // DEBUG CODE - triggering closing position automatically - // s.startClosingPosition() return }