摘要
面向已在 Codex 中接入 TickDB、需要将美股历史行情用于 Excel 或 Python 分析的研究者,演示如何把一次查询留成可复用的研究输入。TickDB 按股票、周期、日期和复权方式返回历史日线,Codex 组织查询,文中的完整 Python 脚本将原始响应转成 CSV,并记录实际首尾时间与条数。示例使用 AAPL.US;指定日期查询需具备相应账号权限。
把一只美股某段历史价格放进 Excel 或 Python 做分析时,股票代码、日期范围、K 线周期和复权方式需要一起保存。后面重新取数或复查结果,才能知道两份文件是否使用了相同口径。
TickDB 为开发者、量化研究和 AI 应用提供统一的市场数据接口,让实时与历史行情进入程序和研究工作流。在这篇教程里,我们使用它的历史 K 线能力,经 MCP 把按需查询的日线交给 Codex,再整理成可复用的研究文件。
本例在 2026 年 10 月 27 日于 Codex 中调用 TickDB,取得 AAPL.US 在 10 月 12 日至 16 日 UTC 日期范围内的 5 条不复权日线,并用文中的 Python 脚本导出 CSV。你可以沿同一流程更换研究对象与日期。
TickDB 在这条研究流程中承担什么?
先确定研究对象和取数口径,再让 Codex 调用 TickDB 的历史 K 线工具。返回的价格保留在原始响应中,后续解释、表格计算和文件转换都可以回到这份输入核对。TickDB 的 AI 接入说明提供了让客户端调用行情工具的入口。
这条流程里的分工是:
| 环节 | 负责的动作 | 交给下一步的内容 |
|---|---|---|
| 使用者 | 确定研究对象、日期、周期与复权口径 | 一组明确的查询条件 |
| Codex | 根据条件调用已连接的 TickDB 工具,并保存返回结果 | 原始业务 JSON |
| TickDB | 按请求返回历史日线与对应的数据字段 | 股票、周期、复权方式、时间和价格成交量记录 |
| 本文 Python 脚本 | 检查响应与所选条件,转换文件并记录范围 | CSV 与实际返回范围记录 |
CSV 转换和范围记录由本文脚本完成。TickDB 在这里的作用,是向 AI 研究流程提供带明确对象、周期和口径的历史行情输入;使用者可以在 Excel、Python 或后续 AI 分析中复用同一份原始数据。
一个历史行情接口,怎样帮助减少重查?
这次使用的 get_kline 不只返回几行价格。请求条件与返回字段一起保留,才能说明这份文件来自哪一次取数。下面几项直接影响后续使用:
| 参数或返回字段 | 本例怎样使用 | 研究时保留了什么依据 |
|---|---|---|
symbol、interval |
锁定AAPL.US 与 1d,导出前检查返回值 |
知道文件对应哪只股票、什么周期 |
adjust |
请求并核对none |
知道文件采用不复权价格,便于与同口径数据比较 |
time |
保留原始毫秒时间戳,另写一列 UTC 时间 | 可以回查记录时点,避免展示时丢掉时间依据 |
open/high/low/close/volume |
原值写入 CSV,不做四舍五入或缺失填零 | 下游计算可以追溯到同一份接口返回 |
例如,研究者准备观察 AAPL.US 这段日线时,先请求 1d、none 和指定日期;返回后检查对象与口径,再决定把这份数据用于接下来的计算。下次换日期或改为其他复权方式时,可以对照原始响应和范围记录,定位变化来自请求条件还是返回数据。
这里完成的是数据准备。历史数据进入回测时,还需根据研究方法处理交易日历、交易成本和样本等条件;本次没有运行完整回测。
用 Codex 下载美股历史数据,需要哪些条件?
准备已接入 TickDB MCP 的 Codex 环境,以及 Python 3.9 或以上版本。MCP 是让 AI 助手实际调用外部数据工具的连接方式;本例在 Python 3.14.2 上运行,CSV 转换使用标准库,无需另装数据处理包。
尚未连接的读者,可以先查看 TickDB AI 工具页的 MCP 配置说明。密钥填在客户端的连接配置中,下面的转换脚本不需要密钥。
还要确认账号具备查询美股和指定历史日期范围的权限。当前 K 线文档注明,免费版不能使用 start_time、end_time 指定时间范围;具体可访问的数据以账号权限和实际返回为准。
第一步:明确股票、日期和价格口径
先把这四项固定下来:
| 项目 | 本例设置 |
|---|---|
| 股票代码 | AAPL.US |
| 数据周期 | 日线,1d |
| 日期范围 | 2026-10-12 至 2026-10-16,按 UTC 日期筛选,两端日期均包含 |
| 复权方式 | none,不复权 |
UTC 是统一时间基准。本例保留原始时间戳及 UTC 时间,不把时间戳自动改写为交易所的交易日标签。将来研究盘前盘后或具体交易时刻时,需要另外处理美东时区和交易时段。
复权方式也要跟文件一起保存。不同复权口径的历史价格不能直接混在一列里比较;如果改为前复权或后复权,需要同时修改取数参数和转换命令。
第二步:用 Codex 按日期取数,并保存原始文件
在已连接 TickDB 的 Codex 对话中输入:
请实际调用 TickDB 的 get_kline 工具,查询下面这段美股历史日线:
symbol: AAPL.US
type: stock
interval: 1d
adjust: none
start_time: 1791763200000
end_time: 1792195199999
limit: 1000
这对应 2026-10-12 00:00:00.000 UTC 至
2026-10-16 23:59:59.999 UTC。
把完整的原始业务 JSON 响应保存为 aapl_daily.json,
保留 code、message、data 和价格字符串,不补齐或改写行情。
若工具调用失败,直接报告错误,不生成替代数据。
如果不能保存文件,请完整展示原始业务 JSON,供我保存。
这里的原始业务响应,指包含 code、message、data 的那份 JSON。AI 助手如果不能操作本地文件,可以把这份 JSON 复制到文本编辑器,按 UTF-8 保存为 aapl_daily.json,不要把 Markdown 的三个反引号一起复制进去。
本次 TickDB 工具返回 code=0,data 中保留了 symbol=AAPL.US、interval=1d、adjust=none 和 5 条日线。下面的收盘价来自这份原始响应,便于导出后逐项核对:
| 原始记录的 UTC 时间 | close 原值 |
|---|---|
| 2026-10-12 04:00:00+00:00 | 340.97000000 |
| 2026-10-13 04:00:00+00:00 | 341.27000000 |
| 2026-10-14 04:00:00+00:00 | 342.96000000 |
| 2026-10-15 04:00:00+00:00 | 342.00000000 |
| 2026-10-16 04:00:00+00:00 | 343.46000000 |
表中的时间来自返回记录,不能据此把 04:00 UTC 理解成美股开盘时间。下载时先保留这些原值,后面的展示和计算另做处理。
第三步:用完整脚本导出 CSV
把下面的代码保存为 export_csv.py,与 aapl_daily.json 放在同一个文件夹。
脚本先检查查询状态、股票代码、周期和复权方式,再检查时间戳是否重复或超出所选日期范围。检查通过后,原始价格和成交量字符串直接写入 CSV,不做四舍五入,也不把缺失数据填成零。
import argparse
import csv
import json
from datetime import date, datetime, time, timedelta, timezone
from pathlib import Path
def main():
p = argparse.ArgumentParser(description="Export a TickDB daily response to CSV")
p.add_argument("source", help="Original business JSON response")
p.add_argument("--symbol", default="AAPL.US")
p.add_argument("--start", default="2026-10-12", help="UTC calendar date")
p.add_argument("--end", default="2026-10-16", help="Inclusive UTC date")
p.add_argument("--adjust", choices=["none", "forward", "backward"], default="none")
a = p.parse_args()
start_date, end_date = date.fromisoformat(a.start), date.fromisoformat(a.end)
if start_date > end_date:
raise ValueError("Start date must not be after end date")
start = int(datetime.combine(start_date, time.min, timezone.utc).timestamp() * 1000)
end = int(datetime.combine(end_date + timedelta(days=1), time.min, timezone.utc).timestamp() * 1000) - 1
source = Path(a.source)
response = json.loads(source.read_text(encoding="utf-8-sig"))
if response.get("code") != 0:
raise ValueError("Query failed; inspect code/message before exporting")
data = response["data"]
if (data.get("symbol"), data.get("interval"), data.get("adjust")) != (a.symbol, "1d", a.adjust):
raise ValueError("Returned symbol/interval/adjust differs from your settings")
rows = sorted(data["klines"], key=lambda row: int(row["time"]))
if not rows or len(rows) >= 1000:
raise ValueError("Empty result or response limit reached; inspect the query")
stamps = [int(row["time"]) for row in rows]
if len(stamps) != len(set(stamps)) or any(not start <= ts <= end for ts in stamps):
raise ValueError("Duplicate or out-of-range timestamps; export stopped")
fields = ["open", "high", "low", "close", "volume"]
if any(row.get(field) is None for row in rows for field in fields):
raise ValueError("Missing OHLCV field; export stopped")
def utc(ts):
return datetime.fromtimestamp(ts / 1000, timezone.utc).isoformat()
output = source.with_suffix(".csv")
with output.open("w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(["symbol", "interval", "adjust", "time_ms", "time_utc"] + fields)
for row in rows:
writer.writerow([a.symbol, "1d", a.adjust, row["time"], utc(int(row["time"]))] + [row[k] for k in fields])
meta = dict(converted_at_utc=datetime.now(timezone.utc).isoformat(),
source=source.name, symbol=a.symbol, interval="1d", adjust=a.adjust,
requested_start_utc=utc(start), requested_end_utc=utc(end),
returned_rows=len(rows), actual_first_utc=utc(stamps[0]),
actual_last_utc=utc(stamps[-1]), calendar_completeness="not_checked")
output.with_suffix(".meta.json").write_text(json.dumps(meta, indent=2), encoding="utf-8")
print(f"SOURCE API code={response['code']} symbol={a.symbol} interval=1d adjust={a.adjust}")
print(f"PASS rows={len(rows)} first_utc={utc(stamps[0])} last_utc={utc(stamps[-1])}")
print(f"CSV {output.name}")
print("SUMMARY response=PASS timestamp_checks=PASS csv_export=PASS")
print("BOUNDARY one saved response; exchange calendar completeness not checked")
if __name__ == "__main__":
try:
main()
except (ValueError, KeyError, TypeError, OSError) as exc:
raise SystemExit(f"ERROR: {exc}")
在这个文件夹打开终端,运行:
python3 export_csv.py aapl_daily.json --symbol AAPL.US --start 2026-10-12 --end 2026-10-16 --adjust none
如果电脑使用 python 命令启动 Python,把命令开头的 python3 换成 python。
本次执行输出为:
SOURCE API code=0 symbol=AAPL.US interval=1d adjust=none
PASS rows=5 first_utc=2026-10-12T04:00:00+00:00 last_utc=2026-10-16T04:00:00+00:00
CSV aapl_daily.csv
SUMMARY response=PASS timestamp_checks=PASS csv_export=PASS
BOUNDARY one saved response; exchange calendar completeness not checked
文件夹里会多出两个文件:
aapl_daily.csv:可供 Excel、WPS 或后续程序读取的日线表格。aapl_daily.meta.json:记录转换时间、所选日期、复权方式、返回条数和实际首尾时间。
CSV 包含股票代码、周期、复权方式、原始毫秒时间戳、UTC 时间,以及开盘价、最高价、最低价、收盘价和成交量。CSV 使用带 BOM 的 UTF-8 编码,方便常见表格软件识别。
CSV 导出后,怎样确认实际拿到的数据范围?
先看 aapl_daily.meta.json 中的 returned_rows、actual_first_utc 和 actual_last_utc。它们能帮助你确认这次实际拿到了哪段数据。本例返回 5 条,首尾记录分别为 10 月 12 日与 16 日。
脚本检查的是返回数据本身的重复、范围和必要字段,没有对照交易日历验证是否漏了应有交易日。如果换成其他股票或日期后条数不符合预期,应先查看原始 JSON 和权限,再结合交易日历确认休市、上市时间或数据缺口,不要直接把空档补成零。
用 Excel 打开时,长时间戳可能显示为科学计数法,可以优先查看 time_utc。建议保留原始 JSON 和 CSV,展示或计算用另一份工作表,方便回查。
当前示例在单次响应内完成,不承担多年历史的分页下载。若工具返回达到单次上限,脚本会停止,避免把截断结果当成整个区间。需要批量获取时,可继续阅读历史数据批量拉取方案;其中的具体接口与权限应对照当前文档使用。
从一次导出,进入自己的研究流程
建议把原始 JSON、CSV 和范围记录放在一起。下一次更换股票或日期时,先修改 TickDB 查询条件,再同步修改转换命令;这样,表格里的结果仍能对应到一份明确的原始输入。
如果你的任务发展为多标的、分段历史数据准备,可以在这条路径上增加分页、续传和交易日历检查。若要建设持续接收盘中变化的监控产品,则需要按 WebSocket 文档设计订阅、心跳与断线恢复;本文使用的 MCP 是按需查询入口。
尚未接入的读者,可以从 TickDB 官方入口确认账号与查询权限,再按照 AI 工具页的 MCP 说明连接 Codex。已经连接的读者,直接用自己的股票和短日期区间完成一次取数、导出和回查。
常见问题
第一次应该从哪里开始?
先连接 TickDB MCP,确认账号能查美股及指定日期范围,再使用上面的短区间样例。原始 JSON 成功返回后,运行转换脚本。先取得一份能复查的小文件,再扩大日期范围。
返回空数据或导出报错,怎么处理?
先检查 JSON 中的 code 和 message,再核对股票代码、日期与权限。脚本若提示口径不一致、重复时间戳或范围越界,会停止导出;保留原文件,定位问题后重新取数和转换。
有了 CSV,下一步可以做什么?
可以把它作为 Excel 观察、Python 分析或回测前的数据输入。需要长期批量更新时,再增加分段查询、续传和交易日历检查;需要做行情界面时,可参考历史走势与 CSV 导出的面板示例。
把示例换成你要研究的股票和日期时,要同时修改取数请求与转换命令,保存对应的原始 JSON 和范围记录。
文中股票与价格用于数据操作演示,不构成投资建议。

