From ab20b6db89a85c71f19c931c92f8974b2c42cd24 Mon Sep 17 00:00:00 2001 From: c9s Date: Wed, 31 Jul 2024 15:04:08 +0800 Subject: [PATCH] all: improve binance withdraw status convertion --- .../get_withdraw_history_request.go | 5 ++- ...get_withdraw_history_request_requestgen.go | 38 +++++++++++++++++-- .../binanceapi/withdrawstatus_string.go | 6 +-- pkg/exchange/binance/convert.go | 22 +++++++++++ pkg/exchange/binance/exchange.go | 8 +++- pkg/strategy/xalign/strategy.go | 20 ++++++++++ pkg/types/exchange.go | 5 +++ pkg/types/withdraw.go | 28 ++++++++++---- 8 files changed, 116 insertions(+), 16 deletions(-) diff --git a/pkg/exchange/binance/binanceapi/get_withdraw_history_request.go b/pkg/exchange/binance/binanceapi/get_withdraw_history_request.go index 4e84dbc24..de1b88f0c 100644 --- a/pkg/exchange/binance/binanceapi/get_withdraw_history_request.go +++ b/pkg/exchange/binance/binanceapi/get_withdraw_history_request.go @@ -8,8 +8,9 @@ import ( "github.com/c9s/bbgo/pkg/fixedpoint" ) -//go:generate stringer -type=TransferType // 1 for internal transfer, 0 for external transfer +// +//go:generate stringer -type=TransferType type TransferType int const ( @@ -33,7 +34,7 @@ type WithdrawRecord struct { TxID string `json:"txId"` } -//go:generate stringer -type=WithdrawStatus +//go:generate stringer -type=WithdrawStatus -trimprefix=WithdrawStatus type WithdrawStatus int // WithdrawStatus: 0(0:Email Sent,1:Cancelled 2:Awaiting Approval 3:Rejected 4:Processing 5:Failure 6:Completed) diff --git a/pkg/exchange/binance/binanceapi/get_withdraw_history_request_requestgen.go b/pkg/exchange/binance/binanceapi/get_withdraw_history_request_requestgen.go index 74717d3c4..eb40b3c73 100644 --- a/pkg/exchange/binance/binanceapi/get_withdraw_history_request_requestgen.go +++ b/pkg/exchange/binance/binanceapi/get_withdraw_history_request_requestgen.go @@ -212,6 +212,12 @@ func (g *GetWithdrawHistoryRequest) GetSlugsMap() (map[string]string, error) { return slugs, nil } +// GetPath returns the request path of the API +func (g *GetWithdrawHistoryRequest) GetPath() string { + return "/sapi/v1/capital/withdraw/history" +} + +// Do generates the request object and send the request object to the API endpoint func (g *GetWithdrawHistoryRequest) Do(ctx context.Context) ([]WithdrawRecord, error) { // empty params for GET operation @@ -221,7 +227,9 @@ func (g *GetWithdrawHistoryRequest) Do(ctx context.Context) ([]WithdrawRecord, e return nil, err } - apiURL := "/sapi/v1/capital/withdraw/history" + var apiURL string + + apiURL = g.GetPath() req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params) if err != nil { @@ -234,8 +242,32 @@ func (g *GetWithdrawHistoryRequest) Do(ctx context.Context) ([]WithdrawRecord, e } var apiResponse []WithdrawRecord - if err := response.DecodeJSON(&apiResponse); err != nil { - return nil, err + + type responseUnmarshaler interface { + Unmarshal(data []byte) error + } + + if unmarshaler, ok := interface{}(&apiResponse).(responseUnmarshaler); ok { + if err := unmarshaler.Unmarshal(response.Body); err != nil { + return nil, err + } + } else { + // The line below checks the content type, however, some API server might not send the correct content type header, + // Hence, this is commented for backward compatibility + // response.IsJSON() + if err := response.DecodeJSON(&apiResponse); err != nil { + return nil, err + } + } + + type responseValidator interface { + Validate() error + } + + if validator, ok := interface{}(&apiResponse).(responseValidator); ok { + if err := validator.Validate(); err != nil { + return nil, err + } } return apiResponse, nil } diff --git a/pkg/exchange/binance/binanceapi/withdrawstatus_string.go b/pkg/exchange/binance/binanceapi/withdrawstatus_string.go index 7c972b7fd..c3dd4f91c 100644 --- a/pkg/exchange/binance/binanceapi/withdrawstatus_string.go +++ b/pkg/exchange/binance/binanceapi/withdrawstatus_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=WithdrawStatus"; DO NOT EDIT. +// Code generated by "stringer -type=WithdrawStatus -trimprefix=WithdrawStatus"; DO NOT EDIT. package binanceapi @@ -17,9 +17,9 @@ func _() { _ = x[WithdrawStatusCompleted-6] } -const _WithdrawStatus_name = "WithdrawStatusEmailSentWithdrawStatusCancelledWithdrawStatusAwaitingApprovalWithdrawStatusRejectedWithdrawStatusProcessingWithdrawStatusFailureWithdrawStatusCompleted" +const _WithdrawStatus_name = "EmailSentCancelledAwaitingApprovalRejectedProcessingFailureCompleted" -var _WithdrawStatus_index = [...]uint8{0, 23, 46, 76, 98, 122, 143, 166} +var _WithdrawStatus_index = [...]uint8{0, 9, 18, 34, 42, 52, 59, 68} func (i WithdrawStatus) String() string { if i < 0 || i >= WithdrawStatus(len(_WithdrawStatus_index)-1) { diff --git a/pkg/exchange/binance/convert.go b/pkg/exchange/binance/convert.go index 824c20576..8f6d61922 100644 --- a/pkg/exchange/binance/convert.go +++ b/pkg/exchange/binance/convert.go @@ -9,10 +9,32 @@ import ( "github.com/adshao/go-binance/v2/futures" "github.com/pkg/errors" + "github.com/c9s/bbgo/pkg/exchange/binance/binanceapi" "github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/types" ) +func toGlobalWithdrawStatus(status binanceapi.WithdrawStatus) (types.WithdrawStatus, error) { + switch status { + case binanceapi.WithdrawStatusEmailSent: + return types.WithdrawStatusSent, nil + case binanceapi.WithdrawStatusCancelled: + return types.WithdrawStatusCancelled, nil + case binanceapi.WithdrawStatusAwaitingApproval: + return types.WithdrawStatusAwaitingApproval, nil + case binanceapi.WithdrawStatusRejected: + return types.WithdrawStatusRejected, nil + case binanceapi.WithdrawStatusProcessing: + return types.WithdrawStatusProcessing, nil + case binanceapi.WithdrawStatusFailure: + return types.WithdrawStatusFailed, nil + case binanceapi.WithdrawStatusCompleted: + return types.WithdrawStatusCompleted, nil + default: + return types.WithdrawStatusUnknown, fmt.Errorf("unable to convert the withdraw status: %s", status) + } +} + func toGlobalMarket(symbol binance.Symbol) types.Market { market := types.Market{ Exchange: types.ExchangeBinance, diff --git a/pkg/exchange/binance/exchange.go b/pkg/exchange/binance/exchange.go index 160253fdd..bc3e1001b 100644 --- a/pkg/exchange/binance/exchange.go +++ b/pkg/exchange/binance/exchange.go @@ -571,6 +571,11 @@ func (e *Exchange) QueryWithdrawHistory(ctx context.Context, asset string, since return nil, err } + status, err := toGlobalWithdrawStatus(d.Status) + if err != nil { + return nil, err + } + withdraws = append(withdraws, types.Withdraw{ Exchange: types.ExchangeBinance, ApplyTime: types.Time(applyTime), @@ -581,7 +586,8 @@ func (e *Exchange) QueryWithdrawHistory(ctx context.Context, asset string, since TransactionFee: d.TransactionFee, WithdrawOrderID: d.WithdrawOrderID, Network: d.Network, - Status: d.Status.String(), + Status: status, + OriginalStatus: fmt.Sprintf("%s (%d)", d.Status.String(), int(d.Status)), }) } diff --git a/pkg/strategy/xalign/strategy.go b/pkg/strategy/xalign/strategy.go index 74fcba644..83c1d7eeb 100644 --- a/pkg/strategy/xalign/strategy.go +++ b/pkg/strategy/xalign/strategy.go @@ -114,6 +114,26 @@ func (s *Strategy) aggregateBalances( return totalBalances, sessionBalances } +func (s *Strategy) detectActiveTransfers(ctx context.Context, sessions map[string]*bbgo.ExchangeSession) { + until := time.Now() + since := until.Add(-time.Hour * 24) + for _, session := range sessions { + if transferService, ok := session.Exchange.(types.ExchangeTransferHistoryService); ok { + withdraws, err := transferService.QueryWithdrawHistory(ctx, "", since, until) + if err != nil { + log.WithError(err).Errorf("unable to query withdraw history") + continue + } + + for _, withdraw := range withdraws { + _ = withdraw + // withdraw. + } + } + } + +} + func (s *Strategy) selectSessionForCurrency( ctx context.Context, sessions map[string]*bbgo.ExchangeSession, currency string, changeQuantity fixedpoint.Value, ) (*bbgo.ExchangeSession, *types.SubmitOrder) { diff --git a/pkg/types/exchange.go b/pkg/types/exchange.go index bb5344600..d4364cad2 100644 --- a/pkg/types/exchange.go +++ b/pkg/types/exchange.go @@ -149,6 +149,11 @@ type CustomIntervalProvider interface { IsSupportedInterval(interval Interval) bool } +type ExchangeTransferHistoryService interface { + QueryDepositHistory(ctx context.Context, asset string, since, until time.Time) (allDeposits []Deposit, err error) + QueryWithdrawHistory(ctx context.Context, asset string, since, until time.Time) (allWithdraws []Withdraw, err error) +} + type ExchangeTransferService interface { QueryDepositHistory(ctx context.Context, asset string, since, until time.Time) (allDeposits []Deposit, err error) QueryWithdrawHistory(ctx context.Context, asset string, since, until time.Time) (allWithdraws []Withdraw, err error) diff --git a/pkg/types/withdraw.go b/pkg/types/withdraw.go index 18781341c..ef3e6d788 100644 --- a/pkg/types/withdraw.go +++ b/pkg/types/withdraw.go @@ -7,14 +7,28 @@ import ( "github.com/c9s/bbgo/pkg/fixedpoint" ) +type WithdrawStatus string + +const ( + WithdrawStatusSent WithdrawStatus = "sent" + WithdrawStatusCancelled WithdrawStatus = "cancelled" + WithdrawStatusAwaitingApproval WithdrawStatus = "awaiting_approval" + WithdrawStatusRejected WithdrawStatus = "rejected" + WithdrawStatusProcessing WithdrawStatus = "processing" + WithdrawStatusFailed WithdrawStatus = "failed" + WithdrawStatusCompleted WithdrawStatus = "completed" + WithdrawStatusUnknown WithdrawStatus = "unknown" +) + type Withdraw struct { - GID int64 `json:"gid" db:"gid"` - Exchange ExchangeName `json:"exchange" db:"exchange"` - Asset string `json:"asset" db:"asset"` - Amount fixedpoint.Value `json:"amount" db:"amount"` - Address string `json:"address" db:"address"` - AddressTag string `json:"addressTag"` - Status string `json:"status"` + GID int64 `json:"gid" db:"gid"` + Exchange ExchangeName `json:"exchange" db:"exchange"` + Asset string `json:"asset" db:"asset"` + Amount fixedpoint.Value `json:"amount" db:"amount"` + Address string `json:"address" db:"address"` + AddressTag string `json:"addressTag"` + Status WithdrawStatus `json:"status"` + OriginalStatus string `json:"originalStatus"` TransactionID string `json:"transactionID" db:"txn_id"` TransactionFee fixedpoint.Value `json:"transactionFee" db:"txn_fee"`