add QueryClosedOrders() and QueryTrades() for okex, also fix conflict for QueryOrderTrades() and update typo error in QueryOrderTrades()

This commit is contained in:
Alan.sung 2023-09-18 15:55:58 +08:00
parent cf31796224
commit 99a69f4f2f
9 changed files with 642 additions and 34 deletions

View File

@ -23,7 +23,7 @@ import (
// Market data limiter means public api, this includes QueryMarkets, QueryTicker, QueryTickers, QueryKLines // Market data limiter means public api, this includes QueryMarkets, QueryTicker, QueryTickers, QueryKLines
var ( var (
marketDataLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 5) marketDataLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 5)
tradeRateLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 5) tradeRateLimiter = rate.NewLimiter(rate.Every(300*time.Millisecond), 5)
orderRateLimiter = rate.NewLimiter(rate.Every(300*time.Millisecond), 5) orderRateLimiter = rate.NewLimiter(rate.Every(300*time.Millisecond), 5)
) )
@ -32,6 +32,11 @@ const ID = "okex"
// PlatformToken is the platform currency of OKEx, pre-allocate static string here // PlatformToken is the platform currency of OKEx, pre-allocate static string here
const PlatformToken = "OKB" const PlatformToken = "OKB"
const (
// Constant For query limit
defaultQueryLimit = 100
)
var log = logrus.WithFields(logrus.Fields{ var log = logrus.WithFields(logrus.Fields{
"exchange": ID, "exchange": ID,
}) })
@ -360,7 +365,7 @@ func (e *Exchange) QueryOrder(ctx context.Context, q types.OrderQuery) (*types.O
return nil, errors.New("okex.QueryOrder: OrderId or ClientOrderId is required parameter") return nil, errors.New("okex.QueryOrder: OrderId or ClientOrderId is required parameter")
} }
req := e.client.NewGetOrderDetailsRequest() req := e.client.NewGetOrderDetailsRequest()
req.InstrumentID(q.Symbol). req.InstrumentID(toLocalSymbol(q.Symbol)).
OrderID(q.OrderID). OrderID(q.OrderID).
ClientOrderID(q.ClientOrderID) ClientOrderID(q.ClientOrderID)
@ -380,9 +385,9 @@ func (e *Exchange) QueryOrderTrades(ctx context.Context, q types.OrderQuery) ([]
log.Warn("!!!OKEX EXCHANGE API NOTICE!!! Okex does not support searching for trades using OrderClientId.") log.Warn("!!!OKEX EXCHANGE API NOTICE!!! Okex does not support searching for trades using OrderClientId.")
} }
req := e.client.NewGetTransactionHistoriesRequest() req := e.client.NewGetTransactionHistoryRequest()
if len(q.Symbol) != 0 { if len(q.Symbol) != 0 {
req.InstrumentID(q.Symbol) req.InstrumentID(toLocalSymbol(q.Symbol))
} }
if len(q.OrderID) != 0 { if len(q.OrderID) != 0 {
@ -411,6 +416,131 @@ func (e *Exchange) QueryOrderTrades(ctx context.Context, q types.OrderQuery) ([]
if errs != nil { if errs != nil {
return nil, errs return nil, errs
} }
return trades, nil
}
/*
QueryClosedOrders can query closed orders in last 3 months, there are no time interval limitations, as long as until >= since.
Please Use lastOrderID as cursor, only return orders later than that order, that order is not included.
If you want to query orders by time range, please just pass since and until.
If you want to query by cursor, please pass lastOrderID.
Because it gets the correct response even when you pass all parameters with the right time interval and invalid lastOrderID, like 0.
Time interval boundary unit is second.
since is inclusive, ex. order created in 1694155903, get response if query since 1694155903, get empty if query since 1694155904
until is not inclusive, ex. order created in 1694155903, get response if query until 1694155904, get empty if query until 1694155903
*/
func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) ([]types.Order, error) {
if symbol == "" {
return nil, ErrSymbolRequired
}
if err := tradeRateLimiter.Wait(ctx); err != nil {
return nil, fmt.Errorf("query closed order rate limiter wait error: %w", err)
}
var lastOrder string
if lastOrderID <= 0 {
lastOrder = ""
} else {
lastOrder = strconv.FormatUint(lastOrderID, 10)
}
res, err := e.client.NewGetOrderHistoryRequest().
InstrumentID(toLocalSymbol(symbol)).
StartTime(since).
EndTime(until).
Limit(defaultQueryLimit).
Before(lastOrder).
Do(ctx)
if err != nil {
return nil, fmt.Errorf("failed to call get order histories error: %w", err)
}
var orders []types.Order
for _, order := range res {
o, err2 := toGlobalOrder(&order)
if err2 != nil {
err = multierr.Append(err, err2)
continue
}
orders = append(orders, *o)
}
if err != nil {
return nil, err
}
return types.SortOrdersAscending(orders), nil
}
/*
QueryTrades can query trades in last 3 months, there are no time interval limitations, as long as end_time >= start_time.
OKEX do not provide api to query by tradeID, So use /api/v5/trade/orders-history-archive as its official site do.
Please Use LastTradeID as cursor, only return trades later than that trade, that trade is not included.
If you want to query trades by time range, please just pass start_time and end_time.
If you want to query by cursor, please pass LastTradeID.
Because it gets the correct response even when you pass all parameters with the right time interval and invalid LastTradeID, like 0.
*/
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) ([]types.Trade, error) {
if symbol == "" {
return nil, ErrSymbolRequired
}
req := e.client.NewGetOrderHistoryRequest().InstrumentID(toLocalSymbol(symbol))
limit := uint64(options.Limit)
if limit > defaultQueryLimit || limit <= 0 {
log.Debugf("limit is exceeded default limit %d or zero, got: %d, Do not pass limit", defaultQueryLimit, options.Limit)
} else {
req.Limit(limit)
}
if err := tradeRateLimiter.Wait(ctx); err != nil {
return nil, fmt.Errorf("query trades rate limiter wait error: %w", err)
}
var err error
var response []okexapi.OrderDetails
// query by time interval
if options.StartTime != nil || options.EndTime != nil {
if options.StartTime != nil {
req.StartTime(*options.StartTime)
}
if options.EndTime != nil {
req.EndTime(*options.EndTime)
}
response, err = req.Do(ctx)
if err != nil {
return nil, fmt.Errorf("failed to call get order histories error: %w", err)
}
} else if options.StartTime == nil && options.EndTime == nil && options.LastTradeID == 0 { // query by no any parameters
response, err = req.Do(ctx)
if err != nil {
return nil, fmt.Errorf("failed to call get order histories error: %w", err)
}
} else { // query by trade id
lastTradeID := strconv.FormatUint(options.LastTradeID, 10)
res, err := req.Do(ctx)
if err != nil {
return nil, fmt.Errorf("failed to call get order histories error: %w", err)
}
for _, trade := range res {
if trade.LastTradeID == lastTradeID {
response, err = req.Before(trade.OrderID).Do(ctx)
if err != nil {
return nil, fmt.Errorf("failed to call get order histories error: %w", err)
}
break
}
}
}
trades, err := toGlobalTrades(response)
if err != nil {
return nil, fmt.Errorf("failed to trans order detail to trades error: %w", err)
}
return trades, nil return trades, nil
} }

View File

@ -0,0 +1,40 @@
package okexapi
import (
"time"
"github.com/c9s/requestgen"
)
//go:generate GetRequest -url "/api/v5/trade/orders-history-archive" -type GetOrderHistoryRequest -responseDataType .APIResponse
type GetOrderHistoryRequest struct {
client requestgen.AuthenticatedAPIClient
instrumentType InstrumentType `param:"instType,query"`
instrumentID *string `param:"instId,query"`
orderType *OrderType `param:"ordType,query"`
// underlying and instrumentFamil Applicable to FUTURES/SWAP/OPTION
underlying *string `param:"uly,query"`
instrumentFamily *string `param:"instFamily,query"`
state *OrderState `param:"state,query"`
after *string `param:"after,query"`
before *string `param:"before,query"`
startTime *time.Time `param:"begin,query,milliseconds"`
// endTime for each request, startTime and endTime can be any interval, but should be in last 3 months
endTime *time.Time `param:"end,query,milliseconds"`
// limit for data size per page. Default: 100
limit *uint64 `param:"limit,query"`
}
type OrderList []OrderDetails
// NewGetOrderHistoriesRequest is descending order by createdTime
func (c *RestClient) NewGetOrderHistoryRequest() *GetOrderHistoryRequest {
return &GetOrderHistoryRequest{
client: c,
instrumentType: InstrumentTypeSpot,
}
}

View File

@ -0,0 +1,319 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/v5/trade/orders-history-archive -type GetOrderHistoryRequest -responseDataType .OrderList"; DO NOT EDIT.
package okexapi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"reflect"
"regexp"
"strconv"
"time"
)
func (g *GetOrderHistoryRequest) InstrumentType(instrumentType InstrumentType) *GetOrderHistoryRequest {
g.instrumentType = instrumentType
return g
}
func (g *GetOrderHistoryRequest) InstrumentID(instrumentID string) *GetOrderHistoryRequest {
g.instrumentID = &instrumentID
return g
}
func (g *GetOrderHistoryRequest) OrderType(orderType OrderType) *GetOrderHistoryRequest {
g.orderType = &orderType
return g
}
func (g *GetOrderHistoryRequest) Underlying(underlying string) *GetOrderHistoryRequest {
g.underlying = &underlying
return g
}
func (g *GetOrderHistoryRequest) InstrumentFamily(instrumentFamily string) *GetOrderHistoryRequest {
g.instrumentFamily = &instrumentFamily
return g
}
func (g *GetOrderHistoryRequest) State(state OrderState) *GetOrderHistoryRequest {
g.state = &state
return g
}
func (g *GetOrderHistoryRequest) After(after string) *GetOrderHistoryRequest {
g.after = &after
return g
}
func (g *GetOrderHistoryRequest) Before(before string) *GetOrderHistoryRequest {
g.before = &before
return g
}
func (g *GetOrderHistoryRequest) StartTime(startTime time.Time) *GetOrderHistoryRequest {
g.startTime = &startTime
return g
}
func (g *GetOrderHistoryRequest) EndTime(endTime time.Time) *GetOrderHistoryRequest {
g.endTime = &endTime
return g
}
func (g *GetOrderHistoryRequest) Limit(limit uint64) *GetOrderHistoryRequest {
g.limit = &limit
return g
}
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetOrderHistoryRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
// check instrumentType field -> json key instType
instrumentType := g.instrumentType
// TEMPLATE check-valid-values
switch instrumentType {
case InstrumentTypeSpot, InstrumentTypeSwap, InstrumentTypeFutures, InstrumentTypeOption, InstrumentTypeMARGIN:
params["instType"] = instrumentType
default:
return nil, fmt.Errorf("instType value %v is invalid", instrumentType)
}
// END TEMPLATE check-valid-values
// assign parameter of instrumentType
params["instType"] = instrumentType
// check instrumentID field -> json key instId
if g.instrumentID != nil {
instrumentID := *g.instrumentID
// assign parameter of instrumentID
params["instId"] = instrumentID
} else {
}
// check orderType field -> json key ordType
if g.orderType != nil {
orderType := *g.orderType
// TEMPLATE check-valid-values
switch orderType {
case OrderTypeMarket, OrderTypeLimit, OrderTypePostOnly, OrderTypeFOK, OrderTypeIOC:
params["ordType"] = orderType
default:
return nil, fmt.Errorf("ordType value %v is invalid", orderType)
}
// END TEMPLATE check-valid-values
// assign parameter of orderType
params["ordType"] = orderType
} else {
}
// check underlying field -> json key uly
if g.underlying != nil {
underlying := *g.underlying
// assign parameter of underlying
params["uly"] = underlying
} else {
}
// check instrumentFamily field -> json key instFamily
if g.instrumentFamily != nil {
instrumentFamily := *g.instrumentFamily
// assign parameter of instrumentFamily
params["instFamily"] = instrumentFamily
} else {
}
// check state field -> json key state
if g.state != nil {
state := *g.state
// TEMPLATE check-valid-values
switch state {
case OrderStateCanceled, OrderStateLive, OrderStatePartiallyFilled, OrderStateFilled:
params["state"] = state
default:
return nil, fmt.Errorf("state value %v is invalid", state)
}
// END TEMPLATE check-valid-values
// assign parameter of state
params["state"] = state
} else {
}
// check after field -> json key after
if g.after != nil {
after := *g.after
// assign parameter of after
params["after"] = after
} else {
}
// check before field -> json key before
if g.before != nil {
before := *g.before
// assign parameter of before
params["before"] = before
} else {
}
// check startTime field -> json key begin
if g.startTime != nil {
startTime := *g.startTime
// assign parameter of startTime
// convert time.Time to milliseconds time stamp
params["begin"] = strconv.FormatInt(startTime.UnixNano()/int64(time.Millisecond), 10)
} else {
}
// check endTime field -> json key end
if g.endTime != nil {
endTime := *g.endTime
// assign parameter of endTime
// convert time.Time to milliseconds time stamp
params["end"] = strconv.FormatInt(endTime.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 {
}
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 *GetOrderHistoryRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetOrderHistoryRequest) 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 *GetOrderHistoryRequest) 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 *GetOrderHistoryRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetOrderHistoryRequest) 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 *GetOrderHistoryRequest) 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 *GetOrderHistoryRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetOrderHistoryRequest) 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
}
func (g *GetOrderHistoryRequest) Do(ctx context.Context) (OrderList, error) {
// no body params
var params interface{}
query, err := g.GetQueryParameters()
if err != nil {
return nil, err
}
apiURL := "/api/v5/trade/orders-history-archive"
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
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
var data OrderList
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return data, nil
}

View File

@ -6,8 +6,8 @@ import (
"github.com/c9s/requestgen" "github.com/c9s/requestgen"
) )
//go:generate GetRequest -url "/api/v5/trade/fills-history" -type GetTransactionHistoriesRequest -responseDataType .APIResponse //go:generate GetRequest -url "/api/v5/trade/fills-history" -type GetTransactionHistoryRequest -responseDataType .APIResponse
type GetTransactionHistoriesRequest struct { type GetTransactionHistoryRequest struct {
client requestgen.AuthenticatedAPIClient client requestgen.AuthenticatedAPIClient
instrumentType InstrumentType `param:"instType,query"` instrumentType InstrumentType `param:"instType,query"`
@ -29,11 +29,9 @@ type GetTransactionHistoriesRequest struct {
limit *uint64 `param:"limit,query"` limit *uint64 `param:"limit,query"`
} }
type OrderList []OrderDetails
// NewGetOrderHistoriesRequest is descending order by createdTime // NewGetOrderHistoriesRequest is descending order by createdTime
func (c *RestClient) NewGetTransactionHistoriesRequest() *GetTransactionHistoriesRequest { func (c *RestClient) NewGetTransactionHistoryRequest() *GetTransactionHistoryRequest {
return &GetTransactionHistoriesRequest{ return &GetTransactionHistoryRequest{
client: c, client: c,
instrumentType: InstrumentTypeSpot, instrumentType: InstrumentTypeSpot,
} }

View File

@ -1,4 +1,4 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/v5/trade/fills-history -type GetTransactionHistoriesRequest -responseDataType .OrderList"; DO NOT EDIT. // Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/v5/trade/fills-history -type GetTransactionHistoryRequest -responseDataType .OrderList"; DO NOT EDIT.
package okexapi package okexapi
@ -13,63 +13,63 @@ import (
"time" "time"
) )
func (g *GetTransactionHistoriesRequest) InstrumentType(instrumentType InstrumentType) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) InstrumentType(instrumentType InstrumentType) *GetTransactionHistoryRequest {
g.instrumentType = instrumentType g.instrumentType = instrumentType
return g return g
} }
func (g *GetTransactionHistoriesRequest) InstrumentID(instrumentID string) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) InstrumentID(instrumentID string) *GetTransactionHistoryRequest {
g.instrumentID = &instrumentID g.instrumentID = &instrumentID
return g return g
} }
func (g *GetTransactionHistoriesRequest) OrderType(orderType OrderType) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) OrderType(orderType OrderType) *GetTransactionHistoryRequest {
g.orderType = &orderType g.orderType = &orderType
return g return g
} }
func (g *GetTransactionHistoriesRequest) OrderID(orderID string) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) OrderID(orderID string) *GetTransactionHistoryRequest {
g.orderID = orderID g.orderID = orderID
return g return g
} }
func (g *GetTransactionHistoriesRequest) Underlying(underlying string) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) Underlying(underlying string) *GetTransactionHistoryRequest {
g.underlying = &underlying g.underlying = &underlying
return g return g
} }
func (g *GetTransactionHistoriesRequest) InstrumentFamily(instrumentFamily string) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) InstrumentFamily(instrumentFamily string) *GetTransactionHistoryRequest {
g.instrumentFamily = &instrumentFamily g.instrumentFamily = &instrumentFamily
return g return g
} }
func (g *GetTransactionHistoriesRequest) After(after string) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) After(after string) *GetTransactionHistoryRequest {
g.after = &after g.after = &after
return g return g
} }
func (g *GetTransactionHistoriesRequest) Before(before string) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) Before(before string) *GetTransactionHistoryRequest {
g.before = &before g.before = &before
return g return g
} }
func (g *GetTransactionHistoriesRequest) StartTime(startTime time.Time) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) StartTime(startTime time.Time) *GetTransactionHistoryRequest {
g.startTime = &startTime g.startTime = &startTime
return g return g
} }
func (g *GetTransactionHistoriesRequest) EndTime(endTime time.Time) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) EndTime(endTime time.Time) *GetTransactionHistoryRequest {
g.endTime = &endTime g.endTime = &endTime
return g return g
} }
func (g *GetTransactionHistoriesRequest) Limit(limit uint64) *GetTransactionHistoriesRequest { func (g *GetTransactionHistoryRequest) Limit(limit uint64) *GetTransactionHistoryRequest {
g.limit = &limit g.limit = &limit
return g return g
} }
// GetQueryParameters builds and checks the query parameters and returns url.Values // GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetTransactionHistoriesRequest) GetQueryParameters() (url.Values, error) { func (g *GetTransactionHistoryRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{} var params = map[string]interface{}{}
// check instrumentType field -> json key instType // check instrumentType field -> json key instType
instrumentType := g.instrumentType instrumentType := g.instrumentType
@ -187,14 +187,14 @@ func (g *GetTransactionHistoriesRequest) GetQueryParameters() (url.Values, error
} }
// GetParameters builds and checks the parameters and return the result in a map object // GetParameters builds and checks the parameters and return the result in a map object
func (g *GetTransactionHistoriesRequest) GetParameters() (map[string]interface{}, error) { func (g *GetTransactionHistoryRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{} var params = map[string]interface{}{}
return params, nil return params, nil
} }
// GetParametersQuery converts the parameters from GetParameters into the url.Values format // GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetTransactionHistoriesRequest) GetParametersQuery() (url.Values, error) { func (g *GetTransactionHistoryRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{} query := url.Values{}
params, err := g.GetParameters() params, err := g.GetParameters()
@ -216,7 +216,7 @@ func (g *GetTransactionHistoriesRequest) GetParametersQuery() (url.Values, error
} }
// GetParametersJSON converts the parameters from GetParameters into the JSON format // GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetTransactionHistoriesRequest) GetParametersJSON() ([]byte, error) { func (g *GetTransactionHistoryRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters() params, err := g.GetParameters()
if err != nil { if err != nil {
return nil, err return nil, err
@ -226,13 +226,13 @@ func (g *GetTransactionHistoriesRequest) GetParametersJSON() ([]byte, error) {
} }
// GetSlugParameters builds and checks the slug parameters and return the result in a map object // GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetTransactionHistoriesRequest) GetSlugParameters() (map[string]interface{}, error) { func (g *GetTransactionHistoryRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{} var params = map[string]interface{}{}
return params, nil return params, nil
} }
func (g *GetTransactionHistoriesRequest) applySlugsToUrl(url string, slugs map[string]string) string { func (g *GetTransactionHistoryRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs { for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b") needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v) url = needleRE.ReplaceAllString(url, _v)
@ -241,7 +241,7 @@ func (g *GetTransactionHistoriesRequest) applySlugsToUrl(url string, slugs map[s
return url return url
} }
func (g *GetTransactionHistoriesRequest) iterateSlice(slice interface{}, _f func(it interface{})) { func (g *GetTransactionHistoryRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice) sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ { for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface() it := sliceValue.Index(_i).Interface()
@ -249,7 +249,7 @@ func (g *GetTransactionHistoriesRequest) iterateSlice(slice interface{}, _f func
} }
} }
func (g *GetTransactionHistoriesRequest) isVarSlice(_v interface{}) bool { func (g *GetTransactionHistoryRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v) rt := reflect.TypeOf(_v)
switch rt.Kind() { switch rt.Kind() {
case reflect.Slice: case reflect.Slice:
@ -258,7 +258,7 @@ func (g *GetTransactionHistoriesRequest) isVarSlice(_v interface{}) bool {
return false return false
} }
func (g *GetTransactionHistoriesRequest) GetSlugsMap() (map[string]string, error) { func (g *GetTransactionHistoryRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{} slugs := map[string]string{}
params, err := g.GetSlugParameters() params, err := g.GetSlugParameters()
if err != nil { if err != nil {
@ -272,7 +272,7 @@ func (g *GetTransactionHistoriesRequest) GetSlugsMap() (map[string]string, error
return slugs, nil return slugs, nil
} }
func (g *GetTransactionHistoriesRequest) Do(ctx context.Context) (OrderList, error) { func (g *GetTransactionHistoryRequest) Do(ctx context.Context) (OrderList, error) {
// no body params // no body params
var params interface{} var params interface{}

View File

@ -0,0 +1,62 @@
package okex
import (
"context"
"testing"
"time"
"github.com/c9s/bbgo/pkg/testutil"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
func Test_QueryClosedOrders(t *testing.T) {
key, secret, passphrase, ok := testutil.IntegrationTestWithPassphraseConfigured(t, types.ExchangeOKEx.String())
if !ok {
t.Skip("Please configure all credentials about OKEX")
}
e := New(key, secret, passphrase)
queryOrder := types.OrderQuery{
Symbol: "BTCUSDT",
}
// test by order id as a cursor
closedOrder, err := e.QueryClosedOrders(context.Background(), string(queryOrder.Symbol), time.Time{}, time.Time{}, 609869603774656544)
if assert.NoError(t, err) {
assert.NotEmpty(t, closedOrder)
}
t.Logf("closed order detail: %+v", closedOrder)
// test by time interval
closedOrder, err = e.QueryClosedOrders(context.Background(), string(queryOrder.Symbol), time.Now().Add(-90*24*time.Hour), time.Now(), 0)
if assert.NoError(t, err) {
assert.NotEmpty(t, closedOrder)
}
t.Logf("closed order detail: %+v", closedOrder)
// test by no parameter
closedOrder, err = e.QueryClosedOrders(context.Background(), string(queryOrder.Symbol), time.Time{}, time.Time{}, 0)
if assert.NoError(t, err) {
assert.NotEmpty(t, closedOrder)
}
t.Logf("closed order detail: %+v", closedOrder)
// test by time interval (boundary test)
closedOrder, err = e.QueryClosedOrders(context.Background(), string(queryOrder.Symbol), time.Unix(1694155903, 999), time.Now(), 0)
if assert.NoError(t, err) {
assert.NotEmpty(t, closedOrder)
}
t.Logf("closed order detail: %+v", closedOrder)
// test by time interval (boundary test)
closedOrder, err = e.QueryClosedOrders(context.Background(), string(queryOrder.Symbol), time.Unix(1694154903, 999), time.Unix(1694155904, 0), 0)
if assert.NoError(t, err) {
assert.NotEmpty(t, closedOrder)
}
t.Logf("closed order detail: %+v", closedOrder)
// test by time interval and order id together
closedOrder, err = e.QueryClosedOrders(context.Background(), string(queryOrder.Symbol), time.Unix(1694154903, 999), time.Now(), 609869603774656544)
if assert.NoError(t, err) {
assert.NotEmpty(t, closedOrder)
}
t.Logf("closed order detail: %+v", closedOrder)
}

View File

@ -25,7 +25,7 @@ func Test_QueryOrder(t *testing.T) {
e := New(key, secret, passphrase) e := New(key, secret, passphrase)
queryOrder := types.OrderQuery{ queryOrder := types.OrderQuery{
Symbol: "BTC-USDT", Symbol: "BTCUSDT",
OrderID: "609869603774656544", OrderID: "609869603774656544",
} }
orderDetail, err := e.QueryOrder(context.Background(), queryOrder) orderDetail, err := e.QueryOrder(context.Background(), queryOrder)

View File

@ -0,0 +1,57 @@
package okex
import (
"context"
"testing"
"time"
"github.com/c9s/bbgo/pkg/testutil"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
func Test_QueryTrades(t *testing.T) {
key, secret, passphrase, ok := testutil.IntegrationTestWithPassphraseConfigured(t, "OKEX")
if !ok {
t.Skip("Please configure all credentials about OKEX")
}
e := New(key, secret, passphrase)
queryOrder := types.OrderQuery{
Symbol: "BTCUSDT",
}
since := time.Now().AddDate(0, -3, 0)
until := time.Now()
queryOption := types.TradeQueryOptions{
StartTime: &since,
EndTime: &until,
Limit: 100,
}
// query by time interval
transactionDetail, err := e.QueryTrades(context.Background(), queryOrder.Symbol, &queryOption)
if assert.NoError(t, err) {
assert.NotEmpty(t, transactionDetail)
}
t.Logf("transaction detail: %+v", transactionDetail)
// query by trade id
transactionDetail, err = e.QueryTrades(context.Background(), queryOrder.Symbol, &types.TradeQueryOptions{LastTradeID: 432044402})
if assert.NoError(t, err) {
assert.NotEmpty(t, transactionDetail)
}
t.Logf("transaction detail: %+v", transactionDetail)
// query by no time interval and no trade id
transactionDetail, err = e.QueryTrades(context.Background(), queryOrder.Symbol, &types.TradeQueryOptions{})
if assert.NoError(t, err) {
assert.NotEmpty(t, transactionDetail)
}
t.Logf("transaction detail: %+v", transactionDetail)
// query by limit exceed default value
transactionDetail, err = e.QueryTrades(context.Background(), queryOrder.Symbol, &types.TradeQueryOptions{Limit: 150})
if assert.NoError(t, err) {
assert.NotEmpty(t, transactionDetail)
}
t.Logf("transaction detail: %+v", transactionDetail)
}

View File

@ -3,6 +3,7 @@ package testutil
import ( import (
"os" "os"
"regexp" "regexp"
"strings"
"testing" "testing"
) )
@ -26,6 +27,7 @@ func IntegrationTestConfigured(t *testing.T, prefix string) (key, secret string,
func IntegrationTestWithPassphraseConfigured(t *testing.T, prefix string) (key, secret, passphrase string, ok bool) { func IntegrationTestWithPassphraseConfigured(t *testing.T, prefix string) (key, secret, passphrase string, ok bool) {
var hasKey, hasSecret, hasPassphrase bool var hasKey, hasSecret, hasPassphrase bool
prefix = strings.ToUpper(prefix)
key, hasKey = os.LookupEnv(prefix + "_API_KEY") key, hasKey = os.LookupEnv(prefix + "_API_KEY")
secret, hasSecret = os.LookupEnv(prefix + "_API_SECRET") secret, hasSecret = os.LookupEnv(prefix + "_API_SECRET")
passphrase, hasPassphrase = os.LookupEnv(prefix + "_API_PASSPHRASE") passphrase, hasPassphrase = os.LookupEnv(prefix + "_API_PASSPHRASE")