综合

指数行情API选型:六个验证步骤,判断行情数据源能不能进入你的策略

作者: TickDB Research · 发布: 2026/9/7 · 阅读: 62

标签: 知乎

去年底参与的一个跨市场策略项目启动基准搭建。需要同时接入沪深300、恒生指数和纳斯达克100的日线历史和实时快照。

用yfinance拿到了IXIC,用AKShare调通了000300.SH,恒生指数也从另一个源查到了历史。三条链各自都能跑。

合并DataFrame之后,同一天的数据对不上。三个源用的时区基准完全不同。删掉错位的K线之后,又发现各市场历史数据的除权方式没有声明。两周时间消耗在数据层,一行因子代码都没写成。

大多数人选指数数据源的方式从根上就是错的。不是错在选了哪个,是错在选的标准:你在比谁"看起来全",而不是比谁"允许你验证"。

这篇文章的方法分三层:先用一张表建立"选型时该看什么"的全局认知;再用六步实测穿透一个数据源的每一个边界;最后沉淀为五把尺子,让你能验证任何数据源。横评表不是结论,是验证清单。实测不是推荐,是演示方法。

一、五源横评:选型时该看什么

1.1 五个维度为什么重要

在展开对比之前,先回答一个更基础的问题:为什么是这五个维度?

多市场覆盖:跨市场策略的起点。你的三个市场指数如果不能在一个目录里找到,后面一切免谈。目录不只是"有没有",更是"能不能搜"——你能不能在两分钟内确认目标指数存在,而不是翻数据字典翻半小时。

K线历史与周期:回测的时间深度由它决定。历史不够长,你的回撤验证就没有统计意义。周期粒度不够,你的日内策略就没法做。

实时更新协议:盘中和盘后策略的数据生命线。REST轮询和WebSocket推送的延迟特性完全不同,秒级策略和日频策略对"实时"的定义差着数量级。

交易时段定义:集合竞价、午休、盘前盘后,你的策略在这些时段的行为取决于数据源怎么定义它们。如果数据源不告诉你它的定义,你只能猜。

数据口径一致性:跨市场对齐的底线。时区、复权、交易日历各市场不一致,你的DataFrame合并后全是NaN。

1.2 五源在五维度上的表现

验收维度MassiveTickDBAKShareTushareyfinance
多市场指数覆盖美股为主,非美股有限US/HK/CN/GLOBAL四类标签,目录13,702条A股为主,海外依赖爬虫A股为主,海外覆盖依赖高权限全球主要指数,分类粗糙
K线历史与周期美股完整11个周期(1m~1M)依赖上游部分历史短粒度不一致
实时更新协议官方实时,仅美股REST + WebSocket部分支持有限非官方实时
交易时段支持仅美股get_trading_sessions
数据口径一致性仅美股口径统一目录和交易日历无声明无声明跨市场不一致

这张表的使用方式:不是看哪个格子写得满。是问每一列——这个维度上,你的目标数据源能不能用代码验证它宣称的能力?

1.3 五个数据源的一句话特征

表格给的是信息,这里给的是判断。

Massive:美股专业级数据源。官方实时、美股数据深度好,但出了美股市场覆盖就断崖。如果你只做美股,它是一个可靠的选择;如果做跨市场,它需要被拼接。

TickDB:多市场统一目录的设计路径。REST + WebSocket 双通道,关键是它把每一个能力都暴露为可验证的接口——你可以用代码确认多市场覆盖,可以验证交易时段定义,可以搜索全收益指数。这是本文后面六步实测会详细展示的。

AKShare:A股单市场研究的实用选择。接口丰富,免费开放,但底层依赖爬虫,稳定性不适合生产环境。适合快速验证想法,不适合实盘数据管道。

Tushare:A股基本面数据的社区标准。历史数据质量不错,但海外覆盖和实时能力受权限等级限制。进阶功能需要积分和权限,不同用户拿到的数据能力不一样。

yfinance:快速验证想法的好工具。全球覆盖广,调起来顺手,但口径不透明,不适宜严肃回测。时区规则和交易日历完全依赖Yahoo的实现,跨市场行为不可控。

1.4 横评的事实边界

TickDB 数据来自本文六步实测。其他四个数据源的维度信息来自各自官方文档和 PyPI README(2026年9月查阅),仅反映其公开宣称的能力,不代表本文已做同口径验证。 读者在将表格用于决策前,应使用本文第三部分的方法自行验证目标数据源。

表格给你全局,但表格不给你答案。 因为表格里的每一个格子,都需要你在自己的场景里验证。而"验证"这件事,恰恰是大多数数据源不让你做的。

二、为什么"验证能力"是稀缺的

验证一个数据源,在大多数情况下是会失败的。不是因为你不够专业,是因为数据源不给你验证材料。

三个典型场景:

  • 想知道某数据源的交易时段定义,翻了半天文档,找到一句"支持全球主流市场交易时段"。没有具体时间,没有返回示例。唯一路径:写邮件问销售。
  • 想知道有没有全收益指数,发现没有目录接口,只能下载数据字典自己翻。翻了半小时,放弃。
  • 想验证复权参数生效没有,拉两段数据对比,完全相同。是没生效,还是样本内无调整事件?你无法区分,因为数据源不给复权因子。

学术研究告诉你失真存在,但数据源不给你手电筒。

Scholes 和 Williams(1977)证明非同步交易让指数收益自相关。Kawaller 等人(1987)发现期货领先现货指数 20-45 分钟。Stoll 和 Whaley(1990)证实开盘后滞后最明显。但如果你用的数据源不给时间戳、不给交易时段、不给字段状态,你知道这些学术结论,也无法在你的数据里找到它们。

一个数据源的专业程度,不体现在它宣传自己覆盖多全,体现在它给你多少把手电筒。

下面是 TickDB 的六步实测。目的不是展示它多完美——它有边界,我会如实写。目的是展示:一个可以被验证的数据源,长什么样。 每一步按统一结构展开:接口返回什么 → 会影响什么投资决策 → 用错会造成什么风险 → 如何在代码中处理。

三、六步实测

实测一:ticker 快照——时间戳与盘口字段的真实状态

验证目标:快照里有没有时间戳?盘口字段在不在?值是多少?

统一请求函数(后续所有实测共用):

import requests

API_KEY = "YOUR_KEY"
BASE_URL = "https://api.tickdb.ai/v1"
HEADERS = {"X-API-Key": API_KEY}

def api_get(path, params):
    resp = requests.get(
        f"{BASE_URL}{path}",
        headers=HEADERS,
        params=params,
        timeout=20,
    )
    resp.raise_for_status()

    body = resp.json()
    if body.get("code") != 0:
        raise RuntimeError(f"API error: {body}")

    return body["data"]

调用 ticker 接口

for sym in ["000300.SH", "HSI", "HSCEI", "SPX", "NDX"]:
    rows = api_get("/market/ticker", {
        "symbols": sym,
        "type": "indices",
    })
    row = rows[0]

    print({
        "symbol": row["symbol"],
        "fields": list(row.keys()),
        "timestamp": row.get("timestamp"),
        "bid_price": row.get("bid_price", "字段不存在"),
        "ask_price": row.get("ask_price", "字段不存在"),
    })

返回结果

标的timestampbid_priceask_price
000300.SH1788751638000字段不存在字段不存在
HSI1788751563000字段不存在字段不存在
HSCEI1788752058000字段不存在字段不存在
SPX1788751575748"0.00000""0.00000"
NDX1788752068980"0.00000""0.00000"

投资价值:指数快照可用于判断市场方向或风险状态。它影响的是"是否启动股票、ETF 或期货策略",而不是直接把指数当作可成交标的。last_pricetimestamp 结合,可以判断指数方向与数据时点。

风险价值:SPX 和 NDX 本次返回 bid_price="0.00000"ask_price="0.00000"若策略把它们直接用于滑点、价差或可成交性模型,会虚构交易价格,回测和实盘预期会脱节。 字段存在但值为零,比字段不存在更危险——字段不存在,代码报错,你马上知道;字段存在但值为零,你的流动性分析模块会安静地产出垃圾结果。

场景价值:在美股风险偏好过滤策略中,用 last_pricetimestamp 判断指数方向与数据时点;实际成交、盘口和滑点必须转用 ETF 或期货数据。

代码关键字段last_pricetimestampbid_priceask_pricesymbol

本次限定性结论:三个成功样本均有 Unix 毫秒 timestamp;响应本身不单独返回时区字段。本次 SPX、NDX 均有 bid_price / ask_price 字段,但值为 0.00000;HSI、HSCEI 本次未返回这两个字段。指数快照适合作为方向和状态信号;不能仅凭字段存在就把它当成可交易盘口。

实测二:交易时段——边界透明到能写进注释

验证目标:数据源怎么定义"交易时段"?和交易所规则有没有差异?能否快速确认?

for market in ["CN", "HK", "US"]:
    data = api_get("/market/trading-sessions", {
        "market": market,
    })
    print(market, data[0]["trading_sessions"])

返回结果

CN: [{"begin_time":930,"end_time":1130},{"begin_time":1300,"end_time":1457}]
HK: [{"begin_time":930,"end_time":1200},{"begin_time":1300,"end_time":1600}]
US: [{"begin_time":400,"end_time":930,"trade_session":1},{"begin_time":930,"end_time":1600},{"begin_time":1600,"end_time":2000,"trade_session":2}]

投资价值:交易时段决定开盘前、盘中和盘后信号能否进入策略,直接影响进场时机与风控开关。一个覆盖全球市场的策略,如果数据源不告诉你每个市场各自的交易时段定义,你的风控开关就会在错误的时间打开或关闭。

风险价值:把集合竞价、连续竞价和盘后交易混为同一时间状态,可能引入前视偏差,或让回测使用实盘不可获得的价格。上交所和深交所的规则很明确:9:25 撮合产生开盘价,在此之前拿到的指数值是参考值。Stoll 和 Whaley(1990)也证明了开盘后前 5-10 分钟指数滞后最明显。

场景价值:A 股开盘动量策略若使用 09:15-09:25 的信息,不能只依赖当前返回的 CN 时段;美股隔夜策略则可依据 US 的 trade_session=1/2 区分盘前和盘后。

代码关键字段begin_timeend_timetrade_sessionmarket

审计发现:CN 返回 09:30-11:30 和 13:00-14:57,没有集合竞价时段。HK 没有开市前时段。US 完整。重点是:我在两分钟内确认了这个边界,不需要翻文档、写邮件、猜。

知道边界,就能处理边界:

# 本次 CN 响应只列出 09:30–11:30、13:00–14:57。
# 公开文档未列出获取 09:15–09:25 集合竞价的额外参数。
# 若策略依赖集合竞价,需另行确认数据入口和过滤规则。

本次限定性结论:本次 CN 与 HK 响应只返回连续交易时段;US 返回盘前、常规交易和盘后。这证明当前接口可让使用者快速看见已返回的时段边界;不能据此断言某市场不存在集合竞价或开市前数据。

实测三:K线复权——验证参数真的生效了

验证目标:数据源说支持复权,怎么验证它真的生效了?

kline_by_adjust = {}

for adjust in ["none", "forward", "backward"]:
    data = api_get("/market/kline", {
        "symbol": "000300.SH",
        "type": "indices",
        "interval": "1d",
        "limit": 30,
        "adjust": adjust,
    })
    bars = data["klines"]
    kline_by_adjust[adjust] = bars

    print(adjust, [
        {"time": bar["time"], "close": bar["close"]}
        for bar in bars[:3]
    ])

none_close = {bar["time"]: bar["close"] for bar in kline_by_adjust["none"]}
backward_close = {
    bar["time"]: bar["close"]
    for bar in kline_by_adjust["backward"]
}

diffs = [
    {"time": ts, "none": close, "backward": backward_close[ts]}
    for ts, close in none_close.items()
    if close != backward_close.get(ts)
]
print("none vs backward differences:", diffs)

返回结果

none:    [{"time": 1785168000000, "close": "4569.524"}, ...]
forward: [{"time": 1785168000000, "close": "4569.524"}, ...]
backward:[{"time": 1785168000000, "close": "4569.524"}, ...]
none vs backward differences: [{"time": 1788710400000, "none": "4556.451", "backward": "4556.776"}]

投资价值:K线的 close 可用于均线、动量、波动率和回撤计算,直接影响持仓、仓位和调仓时点。复权口径的选择,决定你算出来的趋势线是真实的还是扭曲的。

风险价值:混用价格指数、全收益指数和不同 adjust 参数,会让长期收益、最大回撤、夏普率等结果不可比。把一段样本中的差异直接解释成调整事件也会误判——你需要的是验证,不是猜测。

场景价值:在沪深300趋势策略中,用固定 adjustclose 计算 20 日均线;在指数轮动回测中,把 time 与市场日历对齐后再计算收益。

代码关键字段timeopenhighlowclosevolumequote_volumeadjust

审计发现:近 30 根 K 线里,noneforward 的收盘价相同,backward 在最近一根不同(none=4556.451 vs backward=4556.776)。你能做这个验证,是因为 TickDB 给了三种复权参数选择,返回结构一致,可放在同一个循环里跑。 数据源不提供复权参数,你连"验证有没有生效"都做不到。

本次限定性结论:本次 30 根样本中,noneforward 的收盘价相同,backward 在最近一根不同。这只证明三个参数都被接口接受,且本样本中后复权存在可观察差异;不能仅凭这段样本推断是否发生过调整事件,或说明差异的具体原因。

实测四:复权因子——五秒钟得到确定性答案

验证目标:当数据源不支持某个能力时,它是让你猜,还是明确告诉你?

resp = requests.get(
    f"{BASE_URL}/market/kline/ex-factors",
    headers=HEADERS,
    params={"symbol": "000300.SH", "type": "indices"},
    timeout=20,
)
print(resp.status_code, resp.json())

返回结果

400 {"error":"2001","message":"type must be stock for ex-factors","code":"2001"}

投资价值:复权因子是自建复权逻辑和合成全收益指数的基础。没有它,你只能依赖数据源提供的复权价格,无法独立验证复权逻辑,也无法自定义复权口径。

风险价值:如果数据源静默返回空数组或零值,你会误以为"没有复权因子可用"或"复权因子为零",在错误的假设上构建后续计算。明确的拒绝比静默的失败安全得多。

场景价值:如果你需要指数复权因子来合成全收益指数,这个返回结果在五秒内告诉你:TickDB 当前不满足这个需求,你需要换数据源或换思路。如果你只需要价格指数,这个边界不影响你继续使用。

代码关键字段errormessagecode

审计发现:一条三十字节的错误消息,把边界说得清清楚楚:复权因子只支持个股,不支持指数。 不是静默返回空数组,不是返回零值,不是"即将上线"。就是一个明确的拒绝。

本次限定性结论五秒钟,确定性答案。 "明确的否定"比"含糊的承诺"有价值。前者让你立刻行动:需要就换,不需要就继续。选型决策中最贵的不是"选错了",而是"一直不确定自己选没选对"。

实测五:目录搜索——三分钟确认全收益覆盖

验证目标:数据源有没有全收益指数?多快能得到确定性答案?

中证全收益指数的代码前缀是 H(如 H00300)。验证方法:拉完整个 CN 指数目录,搜 H 开头。

all_products = []
offset = 0
limit = 100

while True:
    data = api_get("/symbols/available", {
        "market": "CN",
        "type": "indices",
        "offset": offset,
        "limit": limit,
    })
    all_products.extend(data["products"])

    page = data["pagination"]
    offset += page["count"]
    if offset >= page["total"]:
        break

h_prefix = [
    item for item in all_products
    if item["symbol"].startswith("H")
]

print("CN 指数总数:", len(all_products))
print("H 开头条目数:", len(h_prefix))
print("是否存在 H00300:", any(x["symbol"] == "H00300" for x in h_prefix))

返回结果

CN 指数总数:598
H 开头条目数:0
是否存在 H00300:False

投资价值:全收益与价格指数口径的选择,影响长期资产配置、基准比较和策略收益归因。Dimson、Marsh 和 Staunton(2002)显示全收益与价格收益年化差约两个百分点,复利下是翻倍的差别。

风险价值:把价格指数当全收益指数,会低估含分红再投资假设下的历史回报;反过来,把未核实的目录缺口说成"所有全收益指数都没有",会错误排除数据源。你需要的是精确的验证结果,不是模糊的印象。

场景价值:长期指数配置回测前,先验证策略指定代码是否在目录中。若策略要求 H00300,发现目录未返回后应切换数据入口或修改基准,而不是静默替换成 000300.SH

代码关键字段symbolnamemarkettypeproductspagination.totalpagination.count

审计发现:一个 while 循环,不到三分钟,遍历全部 598 条 CN 指数记录,得到确定性结论。这就是可验证性的核心价值:不保证每个能力都存在,但保证你能快速知道每个能力存不存在。

本次限定性结论:本次已分页读取的 598 条 CN 指数目录记录中,没有发现以 H 开头的代码,H00300 也未出现。因此,若策略明确依赖中证 H 前缀代码体系下的全收益指数,当前目录不满足需求。 这不能证明所有全收益口径的指数均不存在,不同编制方可能使用其他代码体系。

实测六:交易日历——多市场对齐的基础

验证目标:多市场回测的交易日对齐,能不能用同一个接口完成?

for market in ["CN", "HK", "US"]:
    data = api_get("/market/trade-days", {
        "market": market,
        "beg_day": "20260101",
        "end_day": "20260131",
    })
    print({
        "market": data["market"],
        "trade_days": data["trade_days"],
        "half_trade_days": data["half_trade_days"],
    })

返回结果

CN: trade_days=[20260105,...,20260130], half_trade_days=[]
HK: trade_days=[20260102,...,20260130], half_trade_days=[]
US: trade_days=[20260102,...,20260130], half_trade_days=[]

投资价值:跨市场策略的信号日期映射和持仓切换依赖交易日历,影响进出场与风险暴露的实际发生日期。多市场回测的对齐基础就是交易日历。

风险价值:把 US 休市日与 HK/CN 交易日按自然日强行合并,可能制造不存在的隔夜收益、错配因果顺序,形成前视偏差。如果各市场日历来自不同源头,你的对齐逻辑会到处都是 NaN。

场景价值:在"美股收盘风险偏好—港股次日开盘"策略中,先分别取得 US、HK 交易日,再建立可执行日期映射;在跨市场回测中,把 half_trade_days 单列处理。

代码关键字段markettrade_dayshalf_trade_days

审计发现:三个市场在同一接口下均返回交易日列表,且本次 2026 年 1 月结果存在市场间差异。CN 从 1 月 5 日开始——元旦假期和周末被正确排除。

本次限定性结论:三个市场在同一接口下均返回交易日列表,且本次结果存在市场间差异。这证明接口可提供三市场日历;特定节假日是否正确,仍应与交易所官方日历逐日比对后再下结论。 本次只验证了 2026 年 1 月,春节和感恩节不在区间内,需要更长窗口确认。

四、五把尺子:选型验证框架

六步实测走完,沉淀为五把尺子。任何指数数据源都适用。尺子本身能不能被数据源回答,比尺子量出来的结果更重要。

第一把:快照有计算时间戳吗?

调 ticker,看有没有 timestamp。没有,你无法判断快照新鲜度。给了,你就能验证;不给,你连验证资格都没有。 有,再问它是什么时间基准——本次 TickDB 返回 Unix 毫秒,响应本身不声明时区,这个细节需要你自己处理。

第二把:盘口字段在不在,值是不是零?

三种情况:字段不存在(本次 HSI/HSCEI 的实测)、字段存在且为零(本次 SPX/NDX 的实测)、字段存在且有值(个股正常情况)。你的代码需要处理全部三种。 不处理,你的流动性分析就会在某一类标的上安静地产出垃圾结果。

第三把:交易时段接口返回什么?

连续竞价还是包含集合竞价?声明时区了吗?本次 TickDB 的 CN 响应只有 09:30-11:30 和 13:00-14:57。知道边界,就能处理边界;不知道边界,连缺什么都不知道。

第四把:全收益指数在目录里吗?复权因子支持指数吗?

搜代码前缀 H。调 ex-factors。本次 TickDB 实测:H 开头为零,ex-factors 对 indices 报错明确。确定的否定比含糊的承诺有价值——它让你立刻行动。

第五把:历史成分股有版本管理吗?

回测超过两年必须问。Brown 等人(1992)证明幸存者偏差系统性高估历史收益。没有 PIT 支持不丢人,丢人的是你不问。 当一个数据源面对这个问题含糊其辞,它就不适合做长周期回测基准。

十五分钟,做一次基础审计

打开终端,跑一遍这六步。把代码里的 TickDB 换成你正在用的数据源。

接得住验证,你知道自己选对了。接不住,你也知道自己哪里需要补。

选数据源时,不要只问"有没有某项能力",还要问:这个能力能否通过字段、时段、目录、参数和错误响应被独立验证?

本文的六步不能证明一个数据源覆盖所有需求。它们能帮助你在策略上线前确认:哪些数据可以进入模型,哪些数据只能当参考,哪些边界需要另找入口补齐。

对量化研究而言,能被及时发现的缺口,通常比未被发现的缺口更容易处理。

参考文献:

  1. Scholes, M. and Williams, J., "Estimating Betas from Nonsynchronous Data," Journal of Financial Economics, 1977, 5(3): 309–327.
  2. Kawaller, I.G., Koch, P.D. and Koch, T.W., "The Temporal Price Relationship Between S&P 500 Futures and the S&P 500 Index," Journal of Finance, 1987, 42(5): 1309–1329.
  3. Stoll, H.R. and Whaley, R.E., "The Dynamics of Stock Index and Stock Index Futures Returns," Journal of Financial and Quantitative Analysis, 1990, 25(4): 441–467.
  4. Chan, K., "A Further Analysis of the Lead-Lag Relationship Between the Cash Market and Stock Index Futures Market," Review of Financial Studies, 1992, 5(1): 123–152.
  5. Dimson, E., Marsh, P. and Staunton, M., Triumph of the Optimists: 101 Years of Global Investment Returns, Princeton University Press, 2002.
  6. Brown, S.J., Goetzmann, W.N., Ibbotson, R.G. and Ross, S.A., "Survivorship Bias in Performance Studies," Review of Financial Studies, 1992, 5(4): 553–580.
  7. 上海证券交易所,《上海证券交易所交易规则(2023年修订)》,2023.
  8. 深圳证券交易所,《深圳证券交易所交易规则(2023年修订)》,2023.
  9. 中证指数有限公司,《指数计算与维护细则》.
  10. TickDB API 官方文档:https://docs.tickdb.ai/zh-Hans/rest/api_trading_sessions

通过 TickDB API 获取实时行情数据

一个 API 接入外汇、加密货币、美股、港股、A股、贵金属和全球指数的实时行情。支持 WebSocket 低延迟推送,免费开始使用。

免费领取 API Key查看 API 文档

相关文章