diff --git a/pkg/exchange/okex/convert.go b/pkg/exchange/okex/convert.go index bc05ce744..742ad56f3 100644 --- a/pkg/exchange/okex/convert.go +++ b/pkg/exchange/okex/convert.go @@ -2,7 +2,6 @@ package okex import ( "fmt" - "regexp" "strconv" "strings" @@ -206,11 +205,17 @@ func toGlobalOrderType(orderType okexapi.OrderType) (types.OrderType, error) { return "", fmt.Errorf("unknown or unsupported okex order type: %s", orderType) } -func toLocalInterval(src string) string { - var re = regexp.MustCompile(`\d+[hdw]`) - return re.ReplaceAllStringFunc(src, func(w string) string { - return strings.ToUpper(w) - }) +func toLocalInterval(interval types.Interval) (string, error) { + if _, ok := SupportedIntervals[interval]; !ok { + return "", fmt.Errorf("interval %s is not supported", interval) + } + + in, ok := ToLocalInterval[interval] + if !ok { + return "", fmt.Errorf("interval %s is not supported, got local interval %s", interval, in) + } + + return in, nil } func toGlobalSide(side okexapi.SideType) (s types.SideType) { diff --git a/pkg/exchange/okex/exchange.go b/pkg/exchange/okex/exchange.go index 6122545d5..a63162908 100644 --- a/pkg/exchange/okex/exchange.go +++ b/pkg/exchange/okex/exchange.go @@ -316,7 +316,10 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval type return nil, err } - intervalParam := toLocalInterval(interval.String()) + intervalParam, err := toLocalInterval(interval) + if err != nil { + return nil, fmt.Errorf("fail to get interval: %w", err) + } req := e.client.NewCandlesticksRequest(toLocalSymbol(symbol)) req.Bar(intervalParam) @@ -541,3 +544,12 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type } return trades, nil } + +func (e *Exchange) SupportedInterval() map[types.Interval]int { + return SupportedIntervals +} + +func (e *Exchange) IsSupportedInterval(interval types.Interval) bool { + _, ok := SupportedIntervals[interval] + return ok +} diff --git a/pkg/exchange/okex/query_kline_test.go b/pkg/exchange/okex/query_kline_test.go new file mode 100644 index 000000000..c3e703b45 --- /dev/null +++ b/pkg/exchange/okex/query_kline_test.go @@ -0,0 +1,85 @@ +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_QueryKlines(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: "BTC-USDT", + } + + now := time.Now() + // test supported interval - minute + klineDetail, err := e.QueryKLines(context.Background(), queryOrder.Symbol, types.Interval1m, types.KLineQueryOptions{ + Limit: 50, + EndTime: &now}) + if assert.NoError(t, err) { + assert.NotEmpty(t, klineDetail) + } + // test supported interval - hour - 1 hour + klineDetail, err = e.QueryKLines(context.Background(), queryOrder.Symbol, types.Interval1h, types.KLineQueryOptions{ + Limit: 50, + EndTime: &now}) + if assert.NoError(t, err) { + assert.NotEmpty(t, klineDetail) + } + // test supported interval - hour - 6 hour to test UTC time + klineDetail, err = e.QueryKLines(context.Background(), queryOrder.Symbol, types.Interval6h, types.KLineQueryOptions{ + Limit: 50, + EndTime: &now}) + if assert.NoError(t, err) { + assert.NotEmpty(t, klineDetail) + } + // test supported interval - day + klineDetail, err = e.QueryKLines(context.Background(), queryOrder.Symbol, types.Interval1d, types.KLineQueryOptions{ + Limit: 50, + EndTime: &now}) + if assert.NoError(t, err) { + assert.NotEmpty(t, klineDetail) + assert.NotEmpty(t, klineDetail[0].Exchange) + assert.NotEmpty(t, klineDetail[0].Symbol) + assert.NotEmpty(t, klineDetail[0].StartTime) + assert.NotEmpty(t, klineDetail[0].EndTime) + assert.NotEmpty(t, klineDetail[0].Interval) + assert.NotEmpty(t, klineDetail[0].Open) + assert.NotEmpty(t, klineDetail[0].Close) + assert.NotEmpty(t, klineDetail[0].High) + assert.NotEmpty(t, klineDetail[0].Low) + assert.NotEmpty(t, klineDetail[0].Volume) + } + // test supported interval - week + klineDetail, err = e.QueryKLines(context.Background(), queryOrder.Symbol, types.Interval1w, types.KLineQueryOptions{ + Limit: 50, + EndTime: &now}) + if assert.NoError(t, err) { + assert.NotEmpty(t, klineDetail) + } + // test supported interval - month + klineDetail, err = e.QueryKLines(context.Background(), queryOrder.Symbol, types.Interval1mo, types.KLineQueryOptions{ + Limit: 50, + EndTime: &now}) + if assert.NoError(t, err) { + assert.NotEmpty(t, klineDetail) + } + // test not supported interval + klineDetail, err = e.QueryKLines(context.Background(), queryOrder.Symbol, types.Interval("2m"), types.KLineQueryOptions{ + Limit: 50, + EndTime: &now}) + if assert.Error(t, err) { + assert.Empty(t, klineDetail) + } +} diff --git a/pkg/exchange/okex/types.go b/pkg/exchange/okex/types.go new file mode 100644 index 000000000..0217c1ee8 --- /dev/null +++ b/pkg/exchange/okex/types.go @@ -0,0 +1,40 @@ +package okex + +import "github.com/c9s/bbgo/pkg/types" + +var ( + // below are supported UTC timezone interval for okex + SupportedIntervals = map[types.Interval]int{ + types.Interval1m: 1 * 60, + types.Interval3m: 3 * 60, + types.Interval5m: 5 * 60, + types.Interval15m: 15 * 60, + types.Interval30m: 30 * 60, + types.Interval1h: 60 * 60, + types.Interval2h: 60 * 60 * 2, + types.Interval4h: 60 * 60 * 4, + types.Interval6h: 60 * 60 * 6, + types.Interval12h: 60 * 60 * 12, + types.Interval1d: 60 * 60 * 24, + types.Interval3d: 60 * 60 * 24 * 3, + types.Interval1w: 60 * 60 * 24 * 7, + types.Interval1mo: 60 * 60 * 24 * 30, + } + + ToLocalInterval = map[types.Interval]string{ + types.Interval1m: "1m", + types.Interval3m: "3m", + types.Interval5m: "5m", + types.Interval15m: "15m", + types.Interval30m: "30m", + types.Interval1h: "1H", + types.Interval2h: "2H", + types.Interval4h: "4H", + types.Interval6h: "6Hutc", + types.Interval12h: "12Hutc", + types.Interval1d: "1Dutc", + types.Interval3d: "3Dutc", + types.Interval1w: "1Wutc", + types.Interval1mo: "1Mutc", + } +)