mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
105 lines
2.3 KiB
Go
105 lines
2.3 KiB
Go
package market
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
binanceFuturesKlinesURL = "https://fapi.binance.com/fapi/v1/klines"
|
|
binanceMaxKlineLimit = 1500
|
|
)
|
|
|
|
// GetKlinesRange fetches K-line series within specified time range (closed interval), returns data sorted by time in ascending order.
|
|
func GetKlinesRange(symbol string, timeframe string, start, end time.Time) ([]Kline, error) {
|
|
symbol = Normalize(symbol)
|
|
normTF, err := NormalizeTimeframe(timeframe)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !end.After(start) {
|
|
return nil, fmt.Errorf("end time must be after start time")
|
|
}
|
|
|
|
startMs := start.UnixMilli()
|
|
endMs := end.UnixMilli()
|
|
|
|
var all []Kline
|
|
cursor := startMs
|
|
|
|
client := &http.Client{Timeout: 15 * time.Second}
|
|
|
|
for cursor < endMs {
|
|
req, err := http.NewRequest("GET", binanceFuturesKlinesURL, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
q := req.URL.Query()
|
|
q.Set("symbol", symbol)
|
|
q.Set("interval", normTF)
|
|
q.Set("limit", fmt.Sprintf("%d", binanceMaxKlineLimit))
|
|
q.Set("startTime", fmt.Sprintf("%d", cursor))
|
|
q.Set("endTime", fmt.Sprintf("%d", endMs))
|
|
req.URL.RawQuery = q.Encode()
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("binance klines api returned status %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
var raw [][]interface{}
|
|
if err := json.Unmarshal(body, &raw); err != nil {
|
|
return nil, err
|
|
}
|
|
if len(raw) == 0 {
|
|
break
|
|
}
|
|
|
|
batch := make([]Kline, len(raw))
|
|
for i, item := range raw {
|
|
openTime := int64(item[0].(float64))
|
|
open, _ := parseFloat(item[1])
|
|
high, _ := parseFloat(item[2])
|
|
low, _ := parseFloat(item[3])
|
|
close, _ := parseFloat(item[4])
|
|
volume, _ := parseFloat(item[5])
|
|
closeTime := int64(item[6].(float64))
|
|
|
|
batch[i] = Kline{
|
|
OpenTime: openTime,
|
|
Open: open,
|
|
High: high,
|
|
Low: low,
|
|
Close: close,
|
|
Volume: volume,
|
|
CloseTime: closeTime,
|
|
}
|
|
}
|
|
|
|
all = append(all, batch...)
|
|
|
|
last := batch[len(batch)-1]
|
|
cursor = last.CloseTime + 1
|
|
|
|
// If returned quantity is less than request limit, reached the end, can exit early.
|
|
if len(batch) < binanceMaxKlineLimit {
|
|
break
|
|
}
|
|
}
|
|
|
|
return all, nil
|
|
}
|