Files
2025-12-08 01:43:22 +08:00

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
}