You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
2.8 KiB
126 lines
2.8 KiB
package okx |
|
|
|
import ( |
|
"context" |
|
"errors" |
|
"fmt" |
|
"net/http" |
|
"net/url" |
|
"sig-pub/pkg/types" |
|
"strconv" |
|
"strings" |
|
"time" |
|
|
|
"github.com/bytedance/sonic" |
|
"github.com/go-resty/resty/v2" |
|
"github.com/govalues/decimal" |
|
) |
|
|
|
const ( |
|
HttpBaseUrl = "https://www.okx.com" |
|
) |
|
|
|
type OkxFetcher struct { |
|
client *resty.Client |
|
httpProxy string |
|
} |
|
|
|
func NewOkxFetcher(httpProxy string) (f *OkxFetcher) { |
|
client := resty.New() |
|
client.SetTimeout(30 * time.Second) |
|
client.SetTransport(&http.Transport{ |
|
MaxIdleConns: 100, |
|
MaxConnsPerHost: 10, |
|
IdleConnTimeout: 90 * time.Second, |
|
TLSHandshakeTimeout: 10 * time.Second, |
|
}) |
|
if httpProxy != "" { |
|
client.SetProxy(httpProxy) |
|
} |
|
|
|
f = &OkxFetcher{ |
|
client: client, |
|
httpProxy: httpProxy, |
|
} |
|
return |
|
} |
|
|
|
func (okx *OkxFetcher) ExhcangeType() types.Exchange { |
|
return types.ExchangeOKX |
|
} |
|
|
|
// FetchHistoryKlines 获取交易产品历史K线数据 |
|
// https://my.okx.com/docs-v5/zh/#order-book-trading-market-data-get-candlesticks-history |
|
// 周期区间 after > before, (after, before) |
|
func (f *OkxFetcher) FetchHistoryKlines(ctx context.Context, okxInstId string, interval types.Interval, after, before int64) (klines []*types.Kline, err error) { |
|
if okxInstId == "" { |
|
err = errors.New("instid is empty") |
|
return |
|
} |
|
if after <= 0 && before <= 0 { |
|
err = errors.New("time range zero") |
|
return |
|
} |
|
if err = fetchHistoryKlineLimiter.Wait(ctx); err != nil { |
|
return |
|
} |
|
|
|
var params []string |
|
params = append(params, fmt.Sprintf("instId=%s", url.QueryEscape(okxInstId))) |
|
params = append(params, "limit=100") // 最大为100 |
|
if v, ok := subscribeCandles[interval]; ok { |
|
params = append(params, "bar="+v) |
|
} |
|
if after > 0 { |
|
params = append(params, fmt.Sprintf("after=%d", after)) |
|
} |
|
if before > 0 { |
|
params = append(params, fmt.Sprintf("before=%d", before)) |
|
} |
|
url := fmt.Sprintf("%s/api/v5/market/history-candles?%s", HttpBaseUrl, strings.Join(params, "&")) |
|
|
|
resp, err := f.client.R().Get(url) |
|
if err != nil { |
|
return |
|
} |
|
|
|
// http status: 429 Too Many Requests |
|
status := resp.StatusCode() |
|
if status != 200 { |
|
err = fmt.Errorf("request history klines status error: %s, %s", url, resp.Status()) |
|
return |
|
} |
|
|
|
r := new(RespHistoryKline) |
|
if err = sonic.Unmarshal(resp.Body(), r); err != nil { |
|
return |
|
} |
|
if r.Code != "0" { |
|
err = fmt.Errorf("code: %s, msg: %s", r.Code, r.Msg) |
|
return |
|
} |
|
for _, data := range r.Data { |
|
ts, e := strconv.ParseInt(data[0], 10, 64) |
|
if e != nil { |
|
err = e |
|
return |
|
} |
|
kline := &types.Kline{ |
|
Interval: interval, |
|
Ts: ts, |
|
Confirm: data[8] == "1", |
|
} |
|
var kinds = []*decimal.Decimal{&kline.Open, &kline.High, &kline.Low, &kline.Close, &kline.Vol, nil, &kline.VolQuote} |
|
for i := 1; i <= 7; i++ { |
|
if kinds[i-1] == nil { |
|
continue |
|
} |
|
*kinds[i-1], err = decimal.Parse(data[i]) |
|
if err != nil { |
|
return |
|
} |
|
} |
|
klines = append(klines, kline) |
|
} |
|
return |
|
}
|
|
|