← 返回目录
p1.1动手⏱ 约 16 分钟

多源数据版图与选型

实测那天,东财系接口全挂了——这一节因此更值钱

🔎 最后验证 2026-07📚 来源:四源可用性/耗时/覆盖区间于 2026-07-31 在 ECS 实测,输出见 code/outputs/stdout.txt、降级链与交叉校验实测见 code/outputs/fallback.txt🧰 akshare 1.18.81、baostock、yfinance 1.5.2、Python 3.12.13
为什么学这个

这一节本来打算给你一张干净的数据源对比表。

结果实测那天出了状况:akshare 里所有走东方财富后端的接口,全部连不上——指数、个股、ETF,四个接口、各重试三次,清一色 RemoteDisconnected。而同一个库里走新浪后端的接口,0.11 秒返回 5960 行。

我决定不重跑到"好看"为止,而是把这次故障原样留在课里。因为它恰好演示了这一节真正要教的东西:

不要问"哪个数据源最好",要问"当我依赖的那个源今天挂了,我的项目会怎样"。

💡 打个比方

出门只带一张银行卡的人,和带两张不同银行卡的人,平时没区别。区别只在 ATM 吞卡的那一天。

数据源一样。单源项目不是"更简单",是"把故障延后到最不方便的那一天"。

一、实测结果(2026-07-31,境内可访问的服务器)

源 / 后端结果行数覆盖区间中位耗时
akshare · 东财 · 指数❌ ConnectionError
akshare · 东财 · 指数(另一接口)❌ ConnectionError
akshare · 东财 · 个股(后复权)❌ ConnectionError
akshare · 东财 · ETF❌ ConnectionError
akshare · 新浪 · 指数59602002-01-04 ~ 2026-07-300.11s
baostock · 指数51772005-04-08 ~ 2026-07-291.75s
yfinance · 000300.SS12992021-03-11 ~ 2026-07-290.01s

三条一眼可见的结论:

  1. "akshare 挂了"这句话本身就是错的。akshare 是个聚合库,后端有东财、新浪、同花顺等等。挂的是某一个后端,不是这个库。理解这一点,你才知道降级该往哪降。
  2. baostock 慢一个量级(1.75s vs 0.11s),但它有一个不可替代的价值:它是独立的第二来源,可以用来验证第一来源对不对。慢,是校验的成本。
  3. yfinance 那一行是本节最危险的一行——它没有报错。
⚠️ 避坑

最危险的不是报错,是"成功地少给你"。

我向 yfinance 要的是 2005-04-08 起的数据。它返回了 200 OK、1299 行、干干净净一张表——起点是 2021-03-11

十六年数据凭空消失,没有任何警告。如果你不检查区间,直接拿去回测:

  • 你的"21 年长周期验证"其实只有 5 年
  • 而这 5 年恰好避开了 2008 和 2015 两次大熊市
  • 于是你的策略最大回撤会显著偏小,夏普显著偏高

"能取到"和"取对了"是两件事。 取回来第一件事必须是检查区间和行数,不合格要当作失败处理。

二、选型:别选"最好的",选"能互相救的"

优势代价 / 坑本课定位
akshare免费、无需注册、覆盖最广(股/基/指/期/宏观)聚合多个后端,单后端可能整体不可用;接口签名变动较频繁主力取数
baostock免费、无需 token、区间完整、复权口径明确慢;需 login/logout;返回游标不是 DataFrame交叉校验源
tushare字段规范、文档完整、社区大需注册取 token,多数接口按积分限流(本课不内置任何账号,故未实测)可选替补
yfinance全球标的、极快境内标的覆盖不全(本次实测只给到 2021 起);数据口径与境内源不同仅用于境外标的

⚠️ 表中 tushare 一行依据官方文档,本节未实测——因为它需要账号。凡是没实测的,本课一律标注出来,不混在实测结论里。

🔧 动手做:查一查你的取数器,单点故障和静默截断(5 分钟)

本节实测那天,akshare 东财系四个接口全部 ConnectionError(各重试 3 次)。而 yfinance 请求 2005 年起的沪深300,只返回了 2021-03-11 起的 1299 行——它没报错,悄悄把前 16 年截掉了。

回去查你(或任何教程)的取数代码两件事:① 只有一个数据源吗?那个源今天挂了,你的整条管线就死了。② 你验过每个源返回的行数和起点吗?还是拿到数据就直接用?

想明白:数据源会,更阴的是会静默返回错的(少给一段、给了别的口径)。所以选型不是选"最好的",是选"能互相救的";交叉校验别用"相等",用"容差"。取到数 ≠ 取对了——每一批数据都要先验行数、起点、口径,再用。

三、交叉校验:别用「相等」,用「容差」

有了两个可用源,自然要问:它们说的是同一件事吗?

我把 akshare·新浪 与 baostock 在 5177 个共同交易日上的收盘价逐日比对:

完全相等(==)      65.73%   ← 这个数字没有意义
最大相对差        0.0033bp
超出 5bp 容差     0 天      ← 这个才是要看的

"完全相等只有 65.73%" 听起来像灾难,实际上毫无问题。两个源都对,只是一个存到小数点后 3 位、另一个后 4 位。最大分歧 0.0033 个基点——换算成 4600 点的指数,是 0.0015 个点

如果你用 df_a == df_b 做校验,你会得到 34% 的"不一致",然后花一整天去查一个不存在的 bug。

数值校验永远用容差,不用相等。本课默认容差 5bp(0.05%),超过才算实质分歧。

四、交付物:带降级与血缘的取数器

把上面所有教训写成一个组件(它会成为 qlab.data 的雏形):

SOURCES = [("akshare·东财", _from_akshare_em),    # 顺序 = 优先级
           ("akshare·新浪", _from_akshare_sina),
           ("baostock", _from_baostock),
           ("yfinance", _from_yfinance)]

def fetch_daily(symbol="000300", need_start="2005-04-08", min_rows=4000):
    failed = []
    for name, fn in SOURCES:
        try:
            df = fn()
        except Exception as e:
            failed.append(f"{name}({type(e).__name__})"); continue   # 挂了 → 降级
        start = df["date"].iloc[0]
        if start > need_start:                                        # 关键:能取到 ≠ 取对了
            failed.append(f"{name}(起点晚:{start})"); continue
        if len(df) < min_rows:
            failed.append(f"{name}(行数少:{len(df)})"); continue
        return df, Provenance(...)          # 带血缘返回
    raise RuntimeError(f"所有源均失败:{failed}")

实测运行效果——故障发生时它自己绕过去了:

 akshare·东财: ConnectionError  降级到下一个源
 akshare·新浪: 5960  [2002-01-04~2026-07-30] 0.65s  采用

交叉校验 akshare·新浪 vs baostock:共同交易日 5177 
  完全相等(==)      65.73%    这个数字没有意义
  最大相对差        0.0033bp
  超出 5.0bp 容差   0       这个才是要看的
   分歧全部在四舍五入位内,两源可互为验证

每份数据都要带血缘

{
  "symbol": "000300",
  "source": "akshare·新浪",
  "fetched_at": "2026-07-31 04:56:15",
  "rows": 5960,
  "start": "2002-01-04",
  "end": "2026-07-30",
  "fallbacks": ["akshare·东财(ConnectionError)"],
  "cross_checked_with": "baostock",
  "max_rel_diff_bp": 0.0033439168
}

纪律:没有血缘的数据不许进下游。

半年后你会遇到这样一天:某个回测结果和上次不一样了。这时候唯一能救你的,就是这个 json——它告诉你上次的数据是谁给的、什么时候拿的、覆盖到哪、和谁校验过。没有它,你只能推倒重来。

❓ 测验
你的取数函数向某源要 2005 年起的日线,它返回 200 OK 和一张 1299 行的干净表,起点是 2021 年。最该怎么处理?
✏️ 填空
两个数据源比对收盘价,不能用相等,要用 ___ ——本课默认 5bp。

📌 免责:本课为技术教学,文中出现的标的代码仅用于接口连通性测试,不构成任何投资建议;各数据源的可用性随时间与网络环境变化,课文数字为 2026-07-31 单次实测。

✅ 小结

这一节的收获来自一次真实故障:akshare 东财系四个接口全挂,新浪系 0.11 秒返回。由此得到三条纪律:

  1. 降级链:SOURCES 顺序 = 优先级,一个源挂了自动往下走
  2. 能取到 ≠ 取对了:yfinance 静默少给 16 年;区间与行数不达标 = 失败
  3. 校验用容差不用相等:完全相等 65.73% 毫无意义,最大相对差 0.0033bp 才是结论

外加一条会救你命的纪律:没有血缘的数据不许进下游

下一节进入 pandas,但不是入门教程——是量化专用的那几件事:时间序列对齐、resample 的陷阱,以及怎么在写法层面把未来函数堵死,让 p0.3 那道守卫永远抓不到你。

下一节 → 量化专用 pandas:未来函数的机械防线
🔎 来源与核验· 4 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「akshare 东财系四接口(index_zh_a_hist / stock_zh_index_daily_em / stock_zh_a_hist / fund_etf_hist_em)各重试 3 次均 ConnectionError:RemoteDisconnected;新浪系 stock_zh_index_daily 中位 0.11s 返回 5960 行(2002-01-04~2026-07-30)」
📚 本节 code/source_bench.py,2026-07-31 于 ECS 实测,输出见 code/outputs/stdout.txt✓ 已核验 2026-07
「baostock 返回 5177 行(2005-04-08~2026-07-29),中位 1.75s;yfinance 000300.SS 返回 1299 行,起点 2021-03-11(晚于请求的 2005-04-08)」
📚 同上实测✓ 已核验 2026-07
「akshare·新浪 与 baostock 在 5177 个共同交易日上:完全相等 65.73%,最大相对差 0.0033bp,超 5bp 容差 0 天」
📚 本节 code/fallback_fetch.py 实测,输出见 code/outputs/fallback.txt✓ 已核验 2026-07
「tushare 需注册获取 token 且多数接口按积分限流(本节未实测,依据官方文档)」
📚 tushare 官方文档 tushare.pro✓ 已核验 2026-07
数据源可用性随时间与网络环境变化,以上为单次实测快照,不代表任何源的长期质量评价。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · p1.2
量化专用 pandas:未来函数的机械防线
继续读下一节 →