黄金历史行情API实测:用Python复盘6次CPI后的涨跌

用户头像sh_*599ojc
2026-11-11 发布

摘要:做黄金事件研究,怎样取得CPI公布前后的分钟行情,并判断最初涨跌是否延续?本文通过TickDB黄金历史行情API,对照6次公布后的5、15、30分钟变化,给出完整Python取数代码、逐次研究表和实际输出,帮助量化研究与数据开发人员复算结果,再按相同条件追加研究。

研究同事问“CPI公布后黄金怎么走”,你给出的结果,可能取决于观察了多长时间。

在这次核对的2026年9月11日行情里,XAUUSD前5分钟下跌约0.26%,半小时结束时却已比公布前上涨约1.09%。如果研究表只留一个“CPI后涨跌幅”,这段转向就很容易被漏掉。

要继续判断最初方向有没有延续,需要保留统一起点下的多个观察时点,再把不同公布日放到一起。本文用TickDB黄金历史K线取得六次事件的分钟数据,由Python生成逐次结果和统计摘要;读者可以直接运行代码,得到一张可以继续追加日期的研究表。

先看一次行情 研究问题才具体

9月11日这次公布后,相对公布前同一个价格起点:5分钟结束时约为−0.26%,15分钟时转为+0.30%,30分钟时约为+1.09%。

image.png

图1:2026年9月11日CPI公布前后,XAUUSD一分钟收盘报价相对公布前基准的变化。点位按分钟结束时刻排列。

如果你关注消息刚公布时的反应,就单独研究前5分钟;如果想知道最初方向是否延续,再观察15分钟和30分钟的位置。每个结果都应写明它回答的是哪段时间的问题。

另一种容易改变印象的做法,是看到金价反弹后改用低点作起点。这样得到的涨幅有它的用途,但应另列一项;与其他公布日比较时,仍沿用原先约定的基准。

把比较条件定好 再请求数据

这次研究使用以下条件:

项目 本例做法
数据对象 XAUUSD黄金报价序列
事件范围 2026年5月至10月的6次已公布月度CPI
公布时刻 每个事件当日美东08:30,按纽约时区转换
行情周期 一分钟K线
查询区间 公布前10分钟至公布后30分钟,共需40根完整分钟记录
比较起点 公布前最后一根完整分钟线的收盘报价
观察时点 公布后5、15、30分钟结束时

事件日期先按美国劳工统计局BLS归档确定,再查询行情,没有根据黄金涨跌挑选日期。

原始响应和运行结果于2026年11月10日保存。下面的采集代码已经实际完成六次请求,本文沿用这批历史数据;各次变化按未舍入报价计算。这里比较的是黄金报价序列,不能直接套成某只黄金ETF或期货合约的收益。

一分钟K线按开始时刻标记。假设公布时刻为08:30,基准取08:29那根线的收盘,前5分钟的末值取08:34那根线的收盘,也就是到08:35时结束的五根分钟线。

计算式很简单:

观察期变化 =(观察时点报价 ÷ 公布前基准报价 − 1)× 100%

按这个起点计算,才方便区分“最初反应”和“后续走势”。扩展到冬令时事件时,也让程序按事件当日纽约时区转换,避免把固定北京时间套到所有日期。

六次结果放在一起 有哪些值得继续查

CPI公布日期 公布后5分钟 公布后15分钟 公布后30分钟
2026-05-12 -0.0611% +0.1540% +0.2108%
2026-06-10 +0.5392% +0.4884% +0.1693%
2026-07-14 +1.6415% +1.5668% +1.1548%
2026-08-12 +0.0389% +0.1239% +0.3186%
2026-09-11 -0.2587% +0.2972% +1.0915%
2026-10-14 -0.9093% -1.4384% -1.3308%

5月12日和9月11日,前5分钟为负,后面转为正。6月10日最初上涨较多,半小时后回落,但仍高于公布前。10月14日三个观察时点都低于公布前。

这些差异能把后续研究的问题缩小:哪些事件的最初方向延续了,哪些发生了转向?时间拉长以后,保留了多少涨幅或跌幅?研究表留住过程,接下来才有具体日期可以回查。

平均变化和上涨次数要一起看。这六次前5分钟的平均变化约为+0.1651%,但上涨和下跌各3次。

原因可以从表里直接找到。7月14日上涨约1.64%,对均值的贡献远大于8月12日约0.04%的上涨;统计方向时,两者各记一次。均值回答幅度怎样汇总,次数记录方向出现过多少次。

再看中位数。六项从小到大排列,中间两项为−0.0611%和+0.0389%,取平均约为−0.0111%,接近零。于是,这组样本同时呈现出均值为正、中间位置接近零、涨跌次数各半的特点。

研究报告可以写得更具体:“这6次CPI公布后前5分钟,XAUUSD上涨、下跌各3次,平均变化约+0.1651%,其中一次较大上涨对均值有明显影响。”继续增加事件时,检查这些差异是否还存在。

用Python生成可以复算的研究表

程序按六个日期查询一分钟K线,保存原始响应、请求参数和取数时间,再输出event_windows.csv。全部事件计算成功后,才写出完整研究表。

代码使用Python 3.10及以上版本和requests。将自己的API Key通过安全配置放入TICKDB_API_KEY环境变量,并确认账号可查询XAUUSD及所需历史区间。环境缺少时区数据时,可安装tzdata。

先安装依赖:

python3 -m pip install requests

将下面代码保存为gold_cpi_study.py:

"""Python 3.10+; install requests. API key: TICKDB_API_KEY."""
import argparse
import csv
import json
import os
from datetime import datetime, timezone
from decimal import Decimal
from pathlib import Path
from statistics import median
from uuid import uuid4
from zoneinfo import ZoneInfo

import requests

EVENTS = ["2026-05-12", "2026-06-10", "2026-07-14",
          "2026-08-12", "2026-09-11", "2026-10-14"]
MINUTE = 60_000


def event_time(day):
    release = datetime.fromisoformat(day + "T08:30:00")
    return int(release.replace(tzinfo=ZoneInfo("America/New_York")).timestamp() * 1000)


def fetch(day, out):
    t = event_time(day)
    params = dict(symbol="XAUUSD", type="forex", interval="1m",
                  start_time=t - 10 * MINUTE, end_time=t + 30 * MINUTE - 1,
                  limit=1000)
    key = os.environ["TICKDB_API_KEY"]
    response = requests.get("//api.tickdb.ai/v1/market/kline", params=params,
                            headers={"X-API-Key": key}, timeout=30)
    if key.encode() in response.content:
        raise ValueError("Unexpected credential echo")
    raw = out / (day + "_1m.json")
    raw.write_bytes(response.content)
    meta = dict(params=params, http_status=response.status_code,
                retrieved_utc=datetime.now(timezone.utc).isoformat())
    raw.with_suffix(".request.json").write_text(json.dumps(meta, indent=2))
    response.raise_for_status()
    return response.json()


def calculate(day, body):
    data = body.get("data", {})
    if body.get("code") != 0 or (data.get("symbol"), data.get("type"),
                                data.get("interval")) != ("XAUUSD", "forex", "1m"):
        raise ValueError("Unexpected API result or product identity")
    rows = data["klines"]
    if any(type(row.get("time")) is not int for row in rows):
        raise ValueError("Expected integer millisecond timestamps")
    by_time = {row["time"]: row for row in rows}
    if len(by_time) != len(rows):
        raise ValueError("Duplicate minute")
    t = event_time(day)
    expected = set(range(t - 10 * MINUTE, t + 30 * MINUTE, MINUTE))
    if not expected.issubset(by_time):
        raise ValueError("Missing expected minute; inspect the saved raw response")
    for time in expected:
        o, h, l, c = (Decimal(by_time[time][k]) for k in ("open", "high", "low", "close"))
        if not all(v.is_finite() and v > 0 for v in (o, h, l, c)):
            raise ValueError("Invalid price")
        if not l <= min(o, c) <= max(o, c) <= h:
            raise ValueError("Invalid OHLC")
    baseline = Decimal(by_time[t - MINUTE]["close"])
    result = dict(date=day, baseline=str(baseline))
    for minutes in (5, 15, 30):
        close = Decimal(by_time[t + (minutes - 1) * MINUTE]["close"])
        result[f"change_{minutes}m_pct"] = str((close / baseline - 1) * 100)
    return result


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--out", default="gold_cpi_study")
    args = parser.parse_args()
    out = Path(args.out) / uuid4().hex
    out.mkdir(parents=True)
    results = [calculate(day, fetch(day, out)) for day in EVENTS]
    with (out / "event_windows.csv").open("w", newline="") as file:
        writer = csv.DictWriter(file, fieldnames=list(results[0]))
        writer.writeheader()
        writer.writerows(results)
    for minutes in (5, 15, 30):
        values = [Decimal(row[f"change_{minutes}m_pct"]) for row in results]
        print(f"{minutes}m: n={len(values)}, mean={sum(values)/len(values):+.4f}%, "
              f"median={median(values):+.4f}%, "
              f"up={sum(v > 0 for v in values)}, down={sum(v < 0 for v in values)}")
    print("Saved:", out)


if __name__ == "__main__":
    main()

运行:

python3 gold_cpi_study.py

本次真实采集后的统计输出如下;使用留存的六份原始响应离线复算,结果一致:

5m: n=6, mean=+0.1651%, median=-0.0111%, up=3, down=3
15m: n=6, mean=+0.1986%, median=+0.2256%, up=5, down=1
30m: n=6, mean=+0.2690%, median=+0.2647%, up=5, down=1

每次运行会创建独立目录,避免覆盖上一轮留存:

gold_cpi_study/
  本次运行ID/
    2026-05-12_1m.json
    2026-05-12_1m.request.json
    其他公布日的响应与请求记录
    event_windows.csv

如果同事对某个结果有疑问,可以沿公布日期找到原始报价,再核对基准与末值。调用成功以后,还要检查所需分钟是否齐全、时间有无重复、价格是否有效;代码已经包含这些检查。缺少预定分钟或出现无效价格时,它会停止计算,保留已取得的响应供定位。

让这张表继续服务研究

先沿用相同起点和观察时长,在EVENTS中加入新的已公布日期。程序按同一规则查询和计算,方便逐次比较。若改为其他标的、周期或跨休市区间,则应同步调整预期分钟与完整性检查。

这里的30分钟变化仍相对公布前基准。如果想研究“第5分钟至第30分钟”,应另以第5分钟的报价作起点计算,避免把两个问题混在一起。

准备解释行情原因时,再补充CPI实际值与公布前一致预期,并对齐同期利率、美元等变量。世界黄金协会2022年9月22日的一篇研究就记录过:高通胀支持黄金的同时,利率上升和强美元也形成了压力。这个历史背景提示了继续查哪些条件;具体到本次六个事件,还要分别补齐相应数据。

本文的取数流程按标的、周期和时间区间查询黄金历史行情,研究程序负责比较与统计。需要把同样的方法用于自己的黄金或外汇区间时,可参考统一实时行情数据 API 的 GitHub 仓库(地址见文末资料),先核对所需数据,再扩展研究。

六次记录先帮助我们把问题展开。若要继续设计交易策略,再明确何时产生信号、用什么价格成交,并加入点差、滑点和费用。研究表中的上涨次数描述历史方向,策略结果还需要按这些交易条件重新计算。

下一次交给研究同事的,可以是一张逐次表、一段明确的计算方法和对应原始数据。读者既能看见走势,也能追着具体日期复查判断。

资料与数据来源

  • 美国劳工统计局BLS,2026年5月12日、6月10日、7月14日、8月12日、9月11日、10月14日CPI新闻稿归档,用于核对公布日期与美东08:30的公布时刻。
  • TickDB历史K线接口,XAUUSD一分钟行情;本文所用六事件原始响应与采集程序运行结果于2026年11月10日保存。
  • 世界黄金协会,2022年9月22日,Why has gold not performed better in 2022 despite high inflation?,用于多因素研究的历史背景。
  • 统一实时行情数据 API 的 GitHub 仓库:https://github.com/TickDB/tickdb-unified-realtime-marketdata-api,供核对接口相关信息。

评论