R语言调用Binance API填充数据框的方式是否高效规范?
关于Binance API批量获取K线数据的实现规范与效率优化咨询
我尝试通过Binance API获取BTCUSDT的1分钟开盘价,该API单次请求最多返回1500根K线。所需数据的时间范围为2019-09-09至2023-03-15。作为自学开发者,未来会频繁使用此类方法,因此想确认当前实现方式是否规范高效。目前我的实现需要发起1231次请求,整个过程耗时约15分钟,以下是我的R实现代码:
library(httr) library(magrittr) library(xts) base_url <- "https://fapi.binance.com" path1 <- "/fapi/v1/klines?symbol=BTCUSDT&interval=1m&startTime=" path2 <- "&limit=1500" fxn <- get("GET") ## Total hours total_hours = as.integer(as.Date("2023-03-15") - as.Date("2019-09-09")) * 24 ## API requests needed requests = (total_hours * 60) / 1500 ## Available data starts 2019-09-09 startTime = (as.numeric(as.POSIXct("2019-09-09 00:00:00 EST", origin = "1970-01-01")) * 1000) ## Create data frame of opening prices prices = data.frame(matrix(ncol = 1)) colnames(prices) = "Open" ## Fill prices data frame for(i in 1:requests){ url <- paste0(base_url, path1, startTime, path2) call <- fxn(url) response <- content(call) prices %<>% add_row(Open = as.double(sapply(response, "[[", 2))) startTime = startTime + (1500*60000) print(i) }
现有实现的问题与优化方向
1. 时间处理的准确性问题
- 用
as.Date计算天数转小时的方式,会忽略结束日期当天的分钟级数据,应该用完整的POSIXct时间戳计算总分钟数,避免数据遗漏或多算。 - Binance API统一使用UTC时间,代码中用EST时区可能导致时间偏移,建议全程用UTC处理时间戳,消除时区转换误差。
2. 数据拼接效率低下
- 循环中反复调用
add_row扩展数据框,每次都会复制整个数据对象,数据量越大耗时越明显。建议预先用列表收集每批结果,最后一次性合并,效率会大幅提升。
3. 缺失错误处理机制
- 没有处理API请求失败的情况(比如网络波动、限流触发),一旦某次请求出错,整个循环会直接中断,之前的数据也可能丢失。需要添加错误捕获和重试逻辑。
4. API请求的合理性优化
- Binance API有请求频率限制,串行请求如果过于密集容易触发限流,建议每次请求后添加0.1-0.5秒的延迟;也可以尝试并行请求,但需严格控制并发数不超过API限制。
- 可以通过
endTime参数明确指定每批请求的结束时间,避免因服务器数据缺失导致的时间偏移,保证数据连续性。
优化后的示例代码
library(httr) library(magrittr) library(xts) # 基础配置 base_url <- "https://fapi.binance.com" symbol <- "BTCUSDT" interval <- "1m" limit <- 1500 start_time_str <- "2019-09-09 00:00:00 UTC" end_time_str <- "2023-03-15 23:59:59 UTC" # 转换为Binance要求的毫秒级时间戳 start_time <- as.numeric(as.POSIXct(start_time_str)) * 1000 end_time <- as.numeric(as.POSIXct(end_time_str)) * 1000 # 计算总请求次数(向上取整避免遗漏最后一批数据) total_minutes <- (end_time - start_time) / 60000 requests <- ceiling(total_minutes / limit) # 初始化列表存储每批结果(比循环拼接数据框高效) price_list <- vector("list", length = requests) # 循环请求数据 for(i in 1:requests){ # 计算当前请求的结束时间(不超过总结束时间) current_end <- min(start_time + limit * 60000, end_time) url <- paste0(base_url, "/fapi/v1/klines?symbol=", symbol, "&interval=", interval, "&startTime=", start_time, "&endTime=", current_end, "&limit=", limit) # 带重试的请求逻辑 tryCatch({ response <- GET(url) stop_for_status(response) # 检查HTTP状态码,非200则抛出错误 data <- content(response) # 提取开盘价并转换为数值类型 price_list[[i]] <- as.double(sapply(data, "[[", 2)) cat("完成请求", i, "/", requests, "\n") }, error = function(e){ cat("请求", i, "失败:", e$message, ",1秒后重试...\n") Sys.sleep(1) i <- i - 1 # 重新执行当前请求 }) # 更新下一次请求的起始时间 start_time <- current_end + 60000 # 下一分钟的起始时间 Sys.sleep(0.2) # 避免触发API限流 } # 合并所有结果为数据框 prices <- data.frame(Open = unlist(price_list))
内容的提问来源于stack exchange,提问作者bernresearch
相关产品推荐
相关产品推荐

