13 changed files with 339 additions and 88 deletions
@ -0,0 +1,92 @@ |
|||||||
|
package okx |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"net/http" |
||||||
|
"net/url" |
||||||
|
"sig-pub/pkg/types" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2" |
||||||
|
"golang.org/x/time/rate" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
HttpBaseUrl = "https://www.okx.com" |
||||||
|
KlineBefore0 int64 = 1672502400000 // k线开始数据 2023-01-01 00:00:00 GMT+8
|
||||||
|
) |
||||||
|
|
||||||
|
type OkxFetcher struct { |
||||||
|
client *resty.Client |
||||||
|
httpProxy string |
||||||
|
historyKlineLimiter *rate.Limiter |
||||||
|
} |
||||||
|
|
||||||
|
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, |
||||||
|
historyKlineLimiter: rate.NewLimiter(rate.Every(100*time.Millisecond), 20), // rate: 20次/2s
|
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 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 = f.historyKlineLimiter.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 |
||||||
|
} |
||||||
|
|
||||||
|
status := resp.StatusCode() |
||||||
|
if status != 200 { |
||||||
|
err = fmt.Errorf("request history klines status error: %s, %s", url, resp.Status()) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
fmt.Println(string(resp.Body())) |
||||||
|
return |
||||||
|
} |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
package okx |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"sig-pub/pkg/types" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
func TestFetchHistoryKlines(t *testing.T) { |
||||||
|
okxFetcher := NewOkxFetcher("http://192.168.1.5:7890") |
||||||
|
|
||||||
|
// get inst+interval before, if=0 -> global before
|
||||||
|
|
||||||
|
// for interval, adder := range types.SupportedIntervals {
|
||||||
|
// _, _ = interval, adder
|
||||||
|
// }
|
||||||
|
interval := types.Interval5m |
||||||
|
intervalAdder := types.SupportedIntervals[interval] |
||||||
|
before := intervalAdder(KlineBefore0, -1) |
||||||
|
for range 10 { |
||||||
|
after := intervalAdder(before, 10) |
||||||
|
klines, err := okxFetcher.FetchHistoryKlines(context.Background(), "BTC-USDT", interval, after, before) |
||||||
|
if err != nil { |
||||||
|
t.Error(err) |
||||||
|
return |
||||||
|
} |
||||||
|
lastTs := intervalAdder(after, -1) // todo ts(last kline)-1
|
||||||
|
before = lastTs |
||||||
|
for _, kline := range klines { |
||||||
|
fmt.Println(kline) |
||||||
|
} |
||||||
|
fmt.Println("-----------------------------------------------------") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestA(t *testing.T) { |
||||||
|
begin := time.UnixMilli(KlineBefore0) |
||||||
|
before := begin.AddDate(0, -1, 0) |
||||||
|
after := begin.AddDate(0, 3, 0) |
||||||
|
fmt.Println("before:", before.UnixMilli()) |
||||||
|
fmt.Println("after:", after.UnixMilli()) |
||||||
|
} |
||||||
Loading…
Reference in new issue