From cf154aaf14df2dbafa71cc71be2761ecb3ceb71e Mon Sep 17 00:00:00 2001 From: c9s Date: Thu, 23 Jan 2025 17:19:44 +0800 Subject: [PATCH] okx: implement QueryDepositHistory method --- pkg/exchange/okex/convert.go | 68 ++++++ pkg/exchange/okex/exchange.go | 22 ++ .../get_asset_deposit_history_request.go | 46 ++++ ...sset_deposit_history_request_requestgen.go | 230 ++++++++++++++++++ 4 files changed, 366 insertions(+) create mode 100644 pkg/exchange/okex/okexapi/get_asset_deposit_history_request.go create mode 100644 pkg/exchange/okex/okexapi/get_asset_deposit_history_request_requestgen.go diff --git a/pkg/exchange/okex/convert.go b/pkg/exchange/okex/convert.go index c4c5943a1..98d715fb1 100644 --- a/pkg/exchange/okex/convert.go +++ b/pkg/exchange/okex/convert.go @@ -388,3 +388,71 @@ func toGlobalMarginRepay(record okexapi.MarginHistoryEntry) types.MarginRepay { IsolatedSymbol: "", } } + +// DepositRecord.state represents the deposit state +/* +Status of deposit +0: Waiting for confirmation +1: Deposit credited +2: Deposit successful +8: Pending due to temporary deposit suspension on this crypto currency +11: Match the address blacklist +12: Account or deposit is frozen +13: Sub-account deposit interception +14: KYC limit +*/ +func toDepositStatusMessage(state types.StrInt64) string { + switch state { + case 0: + return "Waiting for confirmation" + case 1: + return "Deposit credited" + case 2: + return "Deposit successful" + case 8: + return "Pending due to temporary deposit suspension on this crypto currency" + case 11: + return "Match the address blacklist" + case 12: + return "Account or deposit is frozen" + case 13: + return "Sub-account deposit interception" + case 14: + return "KYC limit" + } + + return "" +} + +func toGlobalDepositStatus(state types.StrInt64) types.DepositStatus { + switch state { + case 0: + return types.DepositPending + case 1: + return types.DepositCredited + case 2: + return types.DepositSuccess + case 8: + return types.DepositPending + default: + return types.DepositRejected + } +} + +func toGlobalDeposit(record okexapi.DepositRecord) types.Deposit { + return types.Deposit{ + Exchange: types.ExchangeOKEx, + Time: types.Time(record.Ts), + Amount: record.Amount, + Asset: record.Currency, + Address: record.From, + AddressTag: "", + TransactionID: record.DepId, + Status: toGlobalDepositStatus(record.State), + RawStatus: record.State.String(), + UnlockConfirm: 0, + Confirmation: record.ActualDepBlkConfirm.String(), + Network: strings.ToUpper(record.Chain), + } + +} diff --git a/pkg/exchange/okex/exchange.go b/pkg/exchange/okex/exchange.go index 631ed531f..d57049481 100644 --- a/pkg/exchange/okex/exchange.go +++ b/pkg/exchange/okex/exchange.go @@ -682,6 +682,28 @@ func (e *Exchange) QueryInterestHistory(ctx context.Context, asset string, start return nil, nil } +func (e *Exchange) QueryDepositHistory(ctx context.Context, asset string, startTime, endTime *time.Time) ([]types.Deposit, error) { + req := e.client.NewGetAssetDepositHistoryRequest().Currency(asset) + if endTime != nil { + req.Before(*endTime) + } + if startTime != nil { + req.After(*startTime) + } + + resp, err := req.Do(ctx) + if err != nil { + return nil, err + } + + var records []types.Deposit + for _, r := range resp { + records = append(records, toGlobalDeposit(r)) + } + + return records, nil +} + /* QueryTrades can query trades in last 3 months, there are no time interval limitations, as long as end_time >= start_time. okx does not provide an API to query by trade ID, so we use the bill ID to do it. The trades result is ordered by timestamp. diff --git a/pkg/exchange/okex/okexapi/get_asset_deposit_history_request.go b/pkg/exchange/okex/okexapi/get_asset_deposit_history_request.go new file mode 100644 index 000000000..73a61a970 --- /dev/null +++ b/pkg/exchange/okex/okexapi/get_asset_deposit_history_request.go @@ -0,0 +1,46 @@ +package okexapi + +import ( + "time" + + "github.com/c9s/requestgen" + + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data +//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data + +type DepositRecord struct { + ActualDepBlkConfirm types.StrInt64 `json:"actualDepBlkConfirm"` + Amount fixedpoint.Value `json:"amt"` + AreaCodeFrom string `json:"areaCodeFrom"` + Currency string `json:"ccy"` + Chain string `json:"chain"` + DepId string `json:"depId"` + From string `json:"from"` + FromWdId string `json:"fromWdId"` + State types.StrInt64 `json:"state"` + To string `json:"to"` + + Ts types.MillisecondTimestamp `json:"ts"` + + TxId string `json:"txId"` +} + +//go:generate GetRequest -url "/api/v5/asset/deposit-history" -type GetAssetDepositHistoryRequest -responseDataType []DepositRecord +type GetAssetDepositHistoryRequest struct { + client requestgen.AuthenticatedAPIClient + + currency *string `param:"ccy"` + after *time.Time `param:"after,milliseconds"` + before *time.Time `param:"before,milliseconds"` + limit *uint64 `param:"limit" defaultValue:"100"` +} + +func (c *RestClient) NewGetAssetDepositHistoryRequest() *GetAssetDepositHistoryRequest { + return &GetAssetDepositHistoryRequest{ + client: c, + } +} diff --git a/pkg/exchange/okex/okexapi/get_asset_deposit_history_request_requestgen.go b/pkg/exchange/okex/okexapi/get_asset_deposit_history_request_requestgen.go new file mode 100644 index 000000000..63c68f475 --- /dev/null +++ b/pkg/exchange/okex/okexapi/get_asset_deposit_history_request_requestgen.go @@ -0,0 +1,230 @@ +// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/v5/asset/deposit-history -type GetAssetDepositHistoryRequest -responseDataType []DepositRecord"; DO NOT EDIT. + +package okexapi + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + "reflect" + "regexp" + "strconv" + "time" +) + +func (g *GetAssetDepositHistoryRequest) Currency(currency string) *GetAssetDepositHistoryRequest { + g.currency = ¤cy + return g +} + +func (g *GetAssetDepositHistoryRequest) After(after time.Time) *GetAssetDepositHistoryRequest { + g.after = &after + return g +} + +func (g *GetAssetDepositHistoryRequest) Before(before time.Time) *GetAssetDepositHistoryRequest { + g.before = &before + return g +} + +func (g *GetAssetDepositHistoryRequest) Limit(limit uint64) *GetAssetDepositHistoryRequest { + g.limit = &limit + return g +} + +// GetQueryParameters builds and checks the query parameters and returns url.Values +func (g *GetAssetDepositHistoryRequest) GetQueryParameters() (url.Values, error) { + var params = map[string]interface{}{} + + query := url.Values{} + for _k, _v := range params { + query.Add(_k, fmt.Sprintf("%v", _v)) + } + + return query, nil +} + +// GetParameters builds and checks the parameters and return the result in a map object +func (g *GetAssetDepositHistoryRequest) GetParameters() (map[string]interface{}, error) { + var params = map[string]interface{}{} + // check currency field -> json key ccy + if g.currency != nil { + currency := *g.currency + + // assign parameter of currency + params["ccy"] = currency + } else { + } + // check after field -> json key after + if g.after != nil { + after := *g.after + + // assign parameter of after + // convert time.Time to milliseconds time stamp + params["after"] = strconv.FormatInt(after.UnixNano()/int64(time.Millisecond), 10) + } else { + } + // check before field -> json key before + if g.before != nil { + before := *g.before + + // assign parameter of before + // convert time.Time to milliseconds time stamp + params["before"] = strconv.FormatInt(before.UnixNano()/int64(time.Millisecond), 10) + } else { + } + // check limit field -> json key limit + if g.limit != nil { + limit := *g.limit + + // assign parameter of limit + params["limit"] = limit + } else { + } + + return params, nil +} + +// GetParametersQuery converts the parameters from GetParameters into the url.Values format +func (g *GetAssetDepositHistoryRequest) GetParametersQuery() (url.Values, error) { + query := url.Values{} + + params, err := g.GetParameters() + if err != nil { + return query, err + } + + for _k, _v := range params { + if g.isVarSlice(_v) { + g.iterateSlice(_v, func(it interface{}) { + query.Add(_k+"[]", fmt.Sprintf("%v", it)) + }) + } else { + query.Add(_k, fmt.Sprintf("%v", _v)) + } + } + + return query, nil +} + +// GetParametersJSON converts the parameters from GetParameters into the JSON format +func (g *GetAssetDepositHistoryRequest) GetParametersJSON() ([]byte, error) { + params, err := g.GetParameters() + if err != nil { + return nil, err + } + + return json.Marshal(params) +} + +// GetSlugParameters builds and checks the slug parameters and return the result in a map object +func (g *GetAssetDepositHistoryRequest) GetSlugParameters() (map[string]interface{}, error) { + var params = map[string]interface{}{} + + return params, nil +} + +func (g *GetAssetDepositHistoryRequest) applySlugsToUrl(url string, slugs map[string]string) string { + for _k, _v := range slugs { + needleRE := regexp.MustCompile(":" + _k + "\\b") + url = needleRE.ReplaceAllString(url, _v) + } + + return url +} + +func (g *GetAssetDepositHistoryRequest) iterateSlice(slice interface{}, _f func(it interface{})) { + sliceValue := reflect.ValueOf(slice) + for _i := 0; _i < sliceValue.Len(); _i++ { + it := sliceValue.Index(_i).Interface() + _f(it) + } +} + +func (g *GetAssetDepositHistoryRequest) isVarSlice(_v interface{}) bool { + rt := reflect.TypeOf(_v) + switch rt.Kind() { + case reflect.Slice: + return true + } + return false +} + +func (g *GetAssetDepositHistoryRequest) GetSlugsMap() (map[string]string, error) { + slugs := map[string]string{} + params, err := g.GetSlugParameters() + if err != nil { + return slugs, nil + } + + for _k, _v := range params { + slugs[_k] = fmt.Sprintf("%v", _v) + } + + return slugs, nil +} + +// GetPath returns the request path of the API +func (g *GetAssetDepositHistoryRequest) GetPath() string { + return "/api/v5/asset/deposit-history" +} + +// Do generates the request object and send the request object to the API endpoint +func (g *GetAssetDepositHistoryRequest) Do(ctx context.Context) ([]DepositRecord, error) { + + // empty params for GET operation + var params interface{} + query, err := g.GetParametersQuery() + if err != nil { + return nil, err + } + + var apiURL string + + apiURL = g.GetPath() + + req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params) + if err != nil { + return nil, err + } + + response, err := g.client.SendRequest(req) + if err != nil { + return nil, err + } + + var apiResponse APIResponse + + 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 + } + } + var data []DepositRecord + if err := json.Unmarshal(apiResponse.Data, &data); err != nil { + return nil, err + } + return data, nil +}