(实时)贵金属行情接口 详细接入指南【2025最新教程】



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_KEEPALIVECURLOPT_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补全缺失数据的 ohlc 字段,设置成交量 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 文档,使用库如 libwebsocketcpprestsdk 建立连接,订阅特定资产和 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 线数据,合并到本地数据流,确保数据连续性。

更多详细接入步骤,可以参考官方文档,或者Github


This content originally appeared on DEV Community and was authored by PeakLine