This content originally appeared on DEV Community and was authored by PeakLine
在本文中,我们将通过C++接入Infoway API的贵金属实时行情数据接口,帮助你获取黄金和白银等贵金属的K线数据。我们会使用 libcurl 库进行HTTP请求,并处理API返回的数据。
一、API请求地址
Infoway API主要提供实时行情数据,详细的对比可以参考这篇文章。贵金属的实时行情通过如下API获取:
https://data.infoway.io/common/batch_kline/{klineType}/{klineNum}/{codes}
## 官网:www.infoway.io
入参说明:
{klineType}
是K线的时间周期,传入不同的值代表不同周期的K线:
| ID | K线周期 |
|—-|————-|
| 1 | 1分钟k线 |
| 2 | 5分钟k线 |
| 3 | 15分钟k线 |
| 4 | 30分钟k线 |
| 5 | 1小时k线 |
| 6 | 2小时k线 |
| 7 | 4小时k线 |
| 8 | 1日k线 |
| 9 | 1周k线 |
| 10 | 1月k线 |
| 11 | 1季k线 |
| 12 | 1年k线 |
{klineNum}
是需要的K线数量,这个接口支持能查询最近的500根K线。
{codes}
是资产代码,比如黄金是XAUUSD
二、代码示例
假设我们需要查询黄金和白银的1分钟K线,请求地址是:
https://data.infoway.io/common/batch_kline/1/2/XAUUSD%2CXAGUSD
// 这个地址能返回黄金和白银最近的2根1分钟K线
完整代码如下:
#include <iostream>
#include <string>
#include <curl/curl.h>
// 回调函数,用来接收HTTP响应的数据
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* out) {
size_t total_size = size * nmemb;
out->append((char*)contents, total_size);
return total_size;
}
int main() {
CURL* curl;
CURLcode res;
// 设置 API URL 和请求头
// 申请API Key: www.infoway.io
const std::string api_url = "https://data.infoway.io/common/batch_kline/1/2/XAUUSD%2CXAGUSD";
// 初始化 libcurl
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
// 设置 URL
curl_easy_setopt(curl, CURLOPT_URL, api_url.c_str());
// 设置请求头
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "User-Agent: Mozilla/5.0");
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "apiKey: yourApikey");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// 存储响应结果
std::string response_string;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);
// 发送 GET 请求
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "Curl request failed: " << curl_easy_strerror(res) << std::endl;
} else {
// 输出 HTTP 状态码
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
std::cout << "HTTP code: " << http_code << std::endl;
std::cout << "Message: " << response_string << std::endl;
}
// 清理请求头
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
// 清理 libcurl
curl_global_cleanup();
return 0;
}
三、返回示例
{
"ret": 200,
"msg": "success",
"traceId": "43fe7163-abb2-4a59-b6b2-2af4dc8c4141",
"data": [
{
"s": "XAUUSD",
"respList": [
{
"t": "1750177320",
"h": "1950.07400",
"o": "1949.17600",
"l": "1948.17600",
"c": "1950.07400",
"v": "15.0",
"vw": "1950.7220",
"pc": "0.12%",
"pca": "2.78600"
},
{
"t": "1750177260",
"h": "1949.41400",
"o": "1948.28800",
"l": "1948.28800",
"c": "1949.41400",
"v": "10.0",
"vw": "1949.1220",
"pc": "0.03%",
"pca": "0.52400"
}
]
},
{
"s": "XAGUSD",
"respList": [
{
"t": "1750177320",
"h": "25.07400",
"o": "25.17600",
"l": "25.17600",
"c": "25.07400",
"v": "30.0",
"vw": "25.7220",
"pc": "0.10%",
"pca": "0.78600"
},
{
"t": "1750177260",
"h": "25.01400",
"o": "25.04800",
"l": "25.04800",
"c": "25.01400",
"v": "20.0",
"vw": "25.3220",
"pc": "0.02%",
"pca": "0.52400"
}
]
}
]
}
字段说明:
返回的字段包含周期内的高开低收数据,比如我们查询的是1分钟K线,那么返回的数据就是1分钟内的高开低收。
| 字段名 | 类型 | 必填 | 描述 | 示例值 |
|——–|——–|——|———-|—————-|
| t
| String | 是 | 成交时间 | 1747382898892 |
| h
| String | 是 | 最高价 | 18.01 |
| o
| String | 是 | 开盘价 | 18.01 |
| l
| String | 是 | 最低价 | 18.01 |
| c
| String | 是 | 收盘价 | 18.01 |
| v
| String | 是 | 成交量 | 18000 |
| vm
| String | 是 | 成交额 | 20000 |
| pc
| String | 是 | 涨跌幅 | 0.12% |
| pca
| String | 是 | 涨跌额 | 0.11 |
常见问题
1. 如何优化 libcurl 请求以处理高频 K 线数据(如 1 分钟 K 线)在高并发场景下的性能?
在高频 K 线数据请求场景下,建议以下优化措施:
连接复用:使用 curl_easy_init()
和 curl_easy_cleanup()
创建的 CURL 句柄可以通过设置 CURLOPT_TCP_KEEPALIVE
和 CURLOPT_KEEPALIVE
启用 TCP 连接复用,减少每次请求的连接建立开销。
示例代码:
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(curl, CURLOPT_KEEPALIVE_TIME, 60L);
异步请求:对于高并发场景,建议使用 libcurl 的多接口(multi interface)结合 curl_multi_perform 实现异步请求,允许多个请求并行处理。
批量请求:将多个资产代码(如 XAUUSD、XAGUSD)合并到单次请求中,减少 API 调用次数,降低服务器压力。
错误重试机制:实现指数退避重试策略,处理因网络波动或 API 限频导致的失败(如 HTTP 429 错误)。可以使用 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code) 检查状态码并设置重试逻辑。
2. API 返回的成交时间(t)字段是 Unix 时间戳,如何确保时间戳与本地时区的同步?
API 返回的 t
字段为 Unix 时间戳(秒或毫秒,需参考文档确认)。为确保与本地时区同步:
确认时间戳单位:如果 t
是毫秒(如 1747382898892),需除以 1000 转换为秒。
使用 C++ 的 chrono
库或 gmtime/localtime
函数将时间戳转换为本地时间。例如:
#include <chrono>
#include <iomanip>
#include <sstream>
std::string timestampToLocalTime(const std::string& timestamp) {
auto ts = std::stoll(timestamp) / 1000; // 假设毫秒
auto time = std::chrono::system_clock::from_time_t(ts);
auto local_time = std::chrono::system_clock::to_time_t(time);
std::stringstream ss;
ss << std::put_time(std::localtime(&local_time), "%Y-%m-%d %H:%M:%S");
return ss.str();
}
3. 如何处理 API 返回数据中可能出现的缺失 K 线(如非交易时段的空数据)?
贵金属市场(如 XAUUSD)在周末或节假日可能存在非交易时段,导致 K 线数据缺失。处理方式包括:
预检查时间范围:在请求前,排除非交易时段(如周六、周日或特定市场假期)。可通过查询 API 文档或历史数据确定交易时间窗口。
数据补全逻辑:对于缺失的 K 线,可用前一根 K 线的收盘价c
补全缺失数据的 o
、h
、l
、c
字段,设置成交量 v
为 0。示例:
if (kline_data.empty()) {
// 假设 last_kline 是前一根 K 线数据
kline_data.o = last_kline.c;
kline_data.h = last_kline.c;
kline_data.l = last_kline.c;
kline_data.c = last_kline.c;
kline_data.v = "0";
}
WebSocket 订阅:相比 HTTP 轮询,WebSocket 可实时检测市场状态变化,减少对空数据的处理需求。参考 Infoway API 官网的 WebSocket 文档。
4. 如何在多线程环境中安全地复用 libcurl 句柄以提高吞吐量?
libcurl
本身是线程安全的,但需注意以下事项:
每个线程独立句柄:为每个线程创建独立的 CURL* 句柄,避免共享同一句柄导致竞争条件。不要在多线程间共享 curl_easy_init()
返回的句柄。
全局初始化线程安全:调用 curl_global_init(CURL_GLOBAL_ALL)
时确保只调用一次,通常在程序启动时完成,且使用互斥锁保护:
#include <mutex>
static std::once_flag curl_init_flag;
std::call_once(curl_init_flag, []() { curl_global_init(CURL_GLOBAL_ALL); });
连接池管理:使用 libcurl 的 curl_share_init
创建共享对象,共享 DNS 缓存、连接池等资源,减少开销。示例:
CURLSH* share = curl_share_init();
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
curl_easy_setopt(curl, CURLOPT_SHARE, share);
清理资源:确保每个线程在完成请求后调用 curl_easy_cleanup
,程序退出时调用 curl_global_cleanup
。
5. API 返回的成交量(v)和成交额(vm)字段如何用于计算市场活跃度?
成交量v
和成交额vm
可用于分析市场活跃度和流动性:
成交量加权价格(VWAP
):通过 vw
字段(成交量加权平均价)直接获取周期内的 VWAP,或手动计算:VWAP = vm / v。VWAP 可用于评估市场价格趋势的稳定性。
活跃度指标:比较多周期的成交量(如 1 分钟 vs. 5 分钟 K 线),若短周期成交量激增,可能表示市场短期活跃或突发事件。
异常检测:设置阈值(如 v 超过历史均值的 2 倍标准差),检测异常交易活动。示例:
double calculateActivity(const std::string& volume, double avg_volume, double stddev_volume) {
double v = std::stod(volume);
return (v - avg_volume) / stddev_volume; // Z 分数
}
6. 如何通过 WebSocket 订阅降低延迟并处理断线重连?
WebSocket 订阅可显著降低数据获取延迟,适合实时交易场景:
订阅实现:参考 API 官网 WebSocket 文档,使用库如 libwebsocket
或 cpprestsdk
建立连接,订阅特定资产和 K 线类型(如 wss://data.infoway.io/ws/kline/1/XAUUSD
)。
断线重连:实现心跳机制(如每 30 秒发送 PING 消息),检测连接状态。若断线,设置指数退避重试(初始 1 秒,最大 60 秒)。示例:
void reconnectWebSocket() {
int retry_delay = 1;
while (!connect()) {
std::this_thread::sleep_for(std::chrono::seconds(retry_delay));
retry_delay = std::min(retry_delay * 2, 60); // 指数退避
}
}
数据一致性:断线后重新订阅时,需通过 HTTP API 拉取断线期间的 K 线数据,合并到本地数据流,确保数据连续性。
This content originally appeared on DEV Community and was authored by PeakLine