yfinance 价格修复机制深度解析:用 repair=True 修复 Yahoo 行情中的价格与股息错误
2026/9/11 12:40:16 网站建设 项目流程

yfinance 价格修复机制深度解析:用 repair=True 修复 Yahoo 行情中的价格与股息错误

【免费下载链接】yfinanceDownload market data from Yahoo! Finance's API项目地址: https://gitcode.com/GitHub_Trending/yf/yfinance

导读

yfinance 从 Yahoo! Finance API 获取的行情数据并非总是完美——除美国市场外,全球其他市场(尤其是伦敦、香港、北欧等交易所)经常出现股息调整缺失、拆股未调整、价格单位 100 倍错误、数据行缺失等质量问题。本文围绕 yfinance 官方文档 price_repair.rst 展开,系统讲解history()download()中的repair=True参数:它能自动修复上述多类错误,并在返回表中新增Repaired?列标记被修复的行。读完本文,你将掌握价格修复的四大场景、九类股息错误修复的判定逻辑与前后对比、误报风险与规避策略,以及从源码层面理解 yfinance 的完整修复管线。

快速上手:一行代码开启数据修复

history()download()中传入repair=True即可开启修复:

import yfinance as yf # 方式一:Ticker.history() df = yf.Ticker("8TRA.DE").history(period="1y", interval="1d", repair=True) # 方式二:yf.download() df = yf.download("MOB.ST", period="1y", interval="1d", repair=True)

从 yfinance/scrapers/history.py 的history()签名可以看到官方对repair参数的定义:

repair : bool— Fixes price errors in Yahoo data: 100x, missing, bad dividend adjust. Default: False.

开启修复后有两个关键行为:

  1. 新增Repaired?:返回的 DataFrame 会多出一列Repaired?(布尔值),逐行标注该行是否被修复。唯一的例外是"缺失股息调整"——该修复只修改Adj Close,并不会把Repaired?置为True
  2. 货币修复:修复逻辑还会纠正 Yahoo 的货币单位混淆(如GBpGBPZAcZARILAILS)。因此官方明确建议:要获取真实货币,请检查Ticker.history_metadata['currency'],而不是Ticker.info。这一点在源码 history.py 的get_history_metadata()中也有印证——注释明确指出 "repair affects currency, particularly GBp -> GBP",并且repair的默认值会跟随上一次history()调用的设置。

价格修复的四大场景

场景一:缺失股息调整(Missing dividend adjustment)

判定依据:数据中存在股息(Dividends 非 0),但前一日的Adj Close等于Close(即股息没有被折算进复权价)。

修复方式:手动将股息调整应用到Adj Close

注意:这种情况下Repaired?不会被置为True,因为修复只改变了Adj Close列。

场景二:缺失拆股调整(Missing split adjustment)

判定依据:数据中存在拆股事件(Stock Splits),但拆股前的价格没有被调整。

修复方式:手动将拆股比例应用到拆股前的价格序列。

使用前提:请求的日期范围必须包含拆股日之后的 1 天数据用于校准——因为 Yahoo 偶尔会在拆股当天忘记调整价格。

场景三:缺失数据重建(Missing data)

判定依据:某行价格数据明显缺失或损坏(如整行价格为零或 NaN)。

修复方式:使用更小的时间间隔重新拉取数据并重建该行——例如用1h数据修复1d数据。yfinance 文档给出了三种典型表现:

  • 整行缺失(如 1COV.DE);
  • 盘中(intraday)价格变化但 Volume 缺失;
  • 日线价格变化但 Volume 缺失(如 0316.HK)。

场景四:100x 单位错误(100x errors)

根因:Yahoo 偶尔混淆货币单位,例如把$写成cents、把£写成pence,导致部分价格恰好放大 100 倍

两种表现形式

  1. 随机散布:100x 错误随机分散在整张表中——这类错误通过scipy模块检测(源码见 _fix_unit_random_mixups,它使用scipy.ndimage与局部中位数对比找出 ~100x 的离群值);
  2. 成块出现:错误集中在一个时间段内,原因是 Yahoo 在某一天决定永久切换货币单位(即"货币切换"事件,源码见 _fix_unit_switch)。

价格重建的算法细节

当需要用小周期数据重建缺失行时,yfinance 采用_reconstruct_intervals_batch()(源码 history.py)执行,其关键设计如下:

  • 逐级降周期:修复按1wk → 1d → 1h → 30m → 15m → 5m → 2m → 1m的链条逐级取更小周期作为子区间;1m是最小周期,无法继续降级。
  • 数据时限感知:子区间数据有严格的回溯上限——1h最多只能回溯730 天(2 年)30m/15m/5m/2m最多 60 天,1m最多 30 天(min_lookbacks字典,history.py)。超过时限的旧数据会被标记为"无法重建"而跳过,这正是文档强调"1hcannot be fetched beyond 2 years"的源码依据。
  • 最大递归深度为 2:重建过程最多嵌套两层(如1d → 1h → 30m),防止请求风暴。
  • 按组分批抓取以减少请求:相邻的待修复行被聚合成组(组大小按子区间不同,1d/1wk/1mo组可跨 2 年,1h组 1 年,1m组 5 天),每组只发起一次 Yahoo 请求,以此最小化对 Yahoo 的请求压力(Spam minimised by grouping fetches)
  • 重建数据与官方数据可能不同:文档明确指出,如果日后 Yahoo 修复了原始坏数据,你会看到修复后的价格与重建价格"略有差异、Volume 往往显著不同"——这是"有数据胜过缺失数据"的务实取舍。

值得一提的是,重建时会递归调用self.history(..., interval=sub_interval, repair=True)(history.py),即重建用的细粒度数据本身也经过修复管线,最后通过聚合方式把细粒度行合并回目标周期,Repaired?列在聚合时取any(任一子行被修复即标记该行)。

股息修复(Dividend repair)

除价格外,repair=True还会修复股息数据中的错误(官方文档标注为 "new" 功能)。可修复的错误类型共 5 类:

  1. 股息调整缺失,或调整幅度 100x 过大/过小;
  2. 重复股息(7 天内出现两次);
  3. 股息相对除息日价格跌幅 100x 过大/过小;
  4. 除息日(ex-div date)错误(价格跌幅出现在数天/数周之后);
  5. 新增:资本利得(capital gains)被重复计算

官方经验之谈:大多数股息错误出现在伦敦证券交易所(£/pence 单位混淆),但没有任何交易所绝对安全。测试数据目录 tests/data/ 中大量*-bad-div*.csv*-fixed.csv测试对(如1398-HK-1d-bad-divKME-MI-1d-bad-divSSNLF-1d-bad-div4063-T-1d-bad-stock-split)正是这些真实错误样本的固化。

下面是文档给出的全部"修复前后"对照(这些表格直接来自官方文档,逐列保留了 OHLC、Adj Close、Dividends、Capital Gains 等列):

1. 调整缺失(Adjustment missing)— 1398.HK

# ORIGINAL: Close Adj Close Dividends 2024-07-08 00:00:00+08:00 4.33 4.33 0.335715 2024-07-04 00:00:00+08:00 4.83 4.83 0.000000
# REPAIRED: Close Adj Close Dividends 2024-07-08 00:00:00+08:00 4.33 4.330000 0.335715 2024-07-04 00:00:00+08:00 4.83 4.494285 0.000000

修复逻辑:把 0.335715 的股息从 7 月 8 日的Adj Close中扣除,回溯调整 7 月 4 日的复权价(4.83 − 0.335715 = 4.494285)。

2. 调整过小(Adjustment too small)— 3IN.L

# ORIGINAL: Close Adj Close Dividends 2024-06-13 00:00:00+01:00 3.185 3.185000 0.05950 2024-06-12 00:00:00+01:00 3.270 3.269405 0.00000
# REPAIRED: Close Adj Close Dividends 2024-06-13 00:00:00+01:00 3.185 3.185000 0.05950 2024-06-12 00:00:00+01:00 3.270 3.210500 0.00000

原始Adj Close只扣了 0.000595(明显过小),修复后完整扣除 0.0595 的股息。

3. 重复股息(Duplicate within 7 days)— ALC.SW

# ORIGINAL: Close Adj Close Dividends 2023-05-10 00:00:00+02:00 70.580002 70.352142 0.21 2023-05-09 00:00:00+02:00 65.739998 65.318443 0.21 2023-05-08 00:00:00+02:00 66.379997 65.745682 0.00
# REPAIRED: Close Adj Close Dividends 2023-05-10 00:00:00+02:00 70.580002 70.352142 0.00 2023-05-09 00:00:00+02:00 65.739998 65.527764 0.21 2023-05-08 00:00:00+02:00 66.379997 65.956371 0.00

两天内出现两笔 0.21 的股息,明显是重复记账:删除 5 月 10 日的重复股息并重新调整复权价。

4. 股息过大(Dividend too big)— HLCL.L

# ORIGINAL: Close Adj Close Dividends 2024-06-27 00:00:00+01:00 2.360 2.3600 1.78 2024-06-26 00:00:00+01:00 2.375 2.3572 0.00
# REPAIRED: Close Adj Close Dividends 2024-06-27 00:00:00+01:00 2.360 2.3600 0.0178 2024-06-26 00:00:00+01:00 2.375 2.3572 0.0000

1.78 的股息相对 2.36 的价格显然不合理(£/pence 混淆),修正为 0.0178。

5. 股息与调整均过大(Dividend & adjust too big)— LTI.L

# ORIGINAL: Close Adj Close Adj Dividends 2024-08-08 00:00:00+01:00 768.0 768.0 1.0000 5150.0 2024-08-07 00:00:00+01:00 819.0 -4331.0 -5.2882 0.0
# REPAIRED: Close Adj Close Adj Dividends 2024-08-08 00:00:00+01:00 768.0 768.0 1.0000 51.5 2024-08-07 00:00:00+01:00 819.0 767.5 0.9371 0.0

原始数据中 5150.0 的股息直接把Adj Close砸成负数(-4331.0)、Adj系数 -5.2882,属于 100x 错误;修复后股息 51.5、Adj 系数 0.9371。

6. 股息过小(Dividend too small)— BVT.L

# ORIGINAL: Close Adj Close Adj Dividends 2022-02-03 00:00:00+00:00 0.7534 0.675197 0.8962 0.00001 2022-02-01 00:00:00+00:00 0.7844 0.702970 0.8962 0.00000
# REPAIRED: Close Adj Close Adj Dividends 2022-02-03 00:00:00+00:00 0.7534 0.675197 0.8962 0.001 2022-02-01 00:00:00+00:00 0.7844 0.702075 0.8950 0.000

0.00001 相对真实除息跌幅过小,修正为 0.001 并同步调整复权系数。

7. 前一日被重复调整 2 次(Adjusted 2x on day before)— 2020.OL

识别线索:Close < Low(收盘价低于最低价,数据自相矛盾)

# ORIGINAL: Low Close Adj Close Dividends 2023-12-21 00:00:00+01:00 120.199997 121.099998 118.868782 0.18 2023-12-20 00:00:00+01:00 122.000000 121.900002 119.477371 0.00
# REPAIRED: Low Close Adj Close Dividends 2023-12-21 00:00:00+01:00 120.199997 121.099998 118.868782 0.18 2023-12-20 00:00:00+01:00 122.000000 122.080002 119.654045 0.00

8. 除息日错误(ex-div date wrong)— TETY.ST

# ORIGINAL: Close Adj Close Dividends 2022-06-22 00:00:00+02:00 66.699997 60.085415 0.0 2022-06-21 00:00:00+02:00 71.599998 64.499489 0.0 2022-06-20 00:00:00+02:00 71.800003 64.679657 5.0 2022-06-17 00:00:00+02:00 71.000000 59.454838 0.0
# REPAIRED: Close Adj Close Dividends 2022-06-22 00:00:00+02:00 66.699997 60.085415 5.0 2022-06-21 00:00:00+02:00 71.599998 60.007881 0.0 2022-06-20 00:00:00+02:00 71.800003 60.175503 0.0 2022-06-17 00:00:00+02:00 71.000000 59.505021 0.0

原始数据把 5.0 的股息记在 6 月 20 日,但真正的价格跌幅发生在 6 月 22 日(71.6 → 66.7),因此把股息移到 6 月 22 日并重算全部复权价。

9. 资本利得重复计算(Capital-gains double-counted)— DODFX

识别线索:价格跌幅与"股息"的吻合度优于"股息+资本利得"的合计。

# ORIGINAL: Close Adj Close Dividends Capital Gains Date 2025-12-18 00:00:00-05:00 16.219999 16.219999 0.837 0.417 2025-12-17 00:00:00-05:00 16.920000 15.665999 0.000 0.000
# REPAIRED: Close Adj Close Dividends Capital Gains Date 2025-12-18 00:00:00-05:00 16.219999 16.219999 0.42 0.417 2025-12-17 00:00:00-05:00 16.920000 16.083000 0.00 0.000

这是文档中标注为NEW的最新修复能力。Yahoo 会把资本利得预先叠加进股息列导致Adj Close被双重扣除;源码 _repair_capital_gains 的实现思路是:计算当日价格跌幅,分别与"纯股息"和"股息+资本利得"比较,若跌幅更接近纯股息,则判定资本利得被重复计算,并修正股息值与复权价。

重要警告:误报(False Positives)

由于上述第 3 类修复(股息相对价格跌幅 100x 过大/过小)依赖价格行为判断,存在把好数据误判为坏数据的风险。官方文档给出了明确的误报边界:

  • 周期越长误报率越高,因此只有1d数据会被直接修复。如果你对多日周期(周线、月线等)请求修复,yfinance 会先向 Yahoo 拉取1d数据、执行修复、再重采样(resample)成目标周期——这一设计顺带解决了 Yahoo 对多日周期复权调整的固有缺陷。测试文件 tests/test_price_repair.py 中的test_resampling正是验证了"1d 修复后重采样到周线/月线"的行为。
  • 1d的误报率很小,但仍可能发生:典型场景是微型股息(如 0.5%)——模型可能把正常的市场波动误认为"比股息大 100 倍的除息跌幅",从而触发对"过小股息"的修复。(修复逻辑本身已经通过减去中位数来吸收正常波动。)
  • 规避建议:要么接受这一微小风险;要么拉取6–12 个月、至少包含 2 次股息的行情,将多次股息放在一起分析,从而识别出误报。

源码视角:完整的修复管线

repair=True传入后,history()在 history.py 中按严格顺序执行以下修复步骤:

  1. _standardise_currency()(history.py):统一货币单位——GBp→GBPZAc→ZARILA→ILS,必要时把价格放大/缩小 100 倍,并在history_metadata中标记currencyRepaired
  2. Volume 中的 NaN 先填 0;
  3. _fix_bad_div_adjust()(history.py):修复股息调整问题。源码细节:直接跳过1wk/1mo/3mo/1y周期;科威特第纳尔(KWF)按 1000 进位而非 100;"过大"的判定阈值是0.035(3.5%);若表中存在资本利得则整个函数跳过(避免与资本利得修复互相干扰);
  4. 先修复最后一行的零值(确保最新行在后续修复前正确);
  5. 对非外汇(ticker 不含=)标的执行_fix_unit_mixups()(100x 单位错误)与_fix_bad_stock_splits()(拆股修复,history.py);
  6. _fix_zeroes():修复零值价格;
  7. _repair_capital_gains():资本利得重复计算修复(新版功能);
  8. 还原第一步的货币换算,保证返回数据使用标的实际报价货币。

此外还有两个值得注意的边界行为:

  • repair=True5d周期冲突:源码 history.py 明确抛出ValueError("Yahoo's interval '5d' is nonsense, not supported with repair")——因为 Yahoo 的5d周期本身定义混乱,无法与修复管线兼容;
  • 30m数据以15m抓取:这是 yfinance 对 Yahoo API 已知 bug 的通用规避手段(history.py),与修复管线叠加使用时同样生效。

如何验证:测试与样本数据

仓库为价格修复提供了完整的回归测试与真实样本:

  • tests/test_price_repair.py:覆盖重采样(test_resampling)、100x 随机/成块错误(test_repair_100x_random_dailytest_repair_100x_block_daily)、零值修复(test_repair_zeroes_daily)、坏拆股(test_repair_bad_stock_splits)、坏股息调整(test_repair_bad_div_adjusts)、资本利得重复计算(test_repair_capital_gains_double_count)、GBp 货币不转换(test_repair_gbp_not_converted)等;
  • tests/data/:存放大量"坏数据 / 修复后数据"成对 CSV,例如1398-HK-1d-bad-div.csv1398-HK-1d-bad-div-fixed.csv4063-T-1d-bad-stock-split.csv4063-T-1d-bad-stock-split-fixed.csvAET-L-1d-100x-error.csvAET-L-1d-100x-error-fixed.csvDODFX-1d-cg-double-count.csvDODFX-1d-cg-double-count-fixed.csv等,与本文中的各类修复场景一一对应,可直接用于复现或二次开发。

结语与使用建议

repair=True是 yfinance 面向非美国市场数据质量问题的"一站式"修复方案:它既处理价格层的缺失/单位/拆股问题,也处理股息层的重复、倍率与除息日错误,还能顺带修正货币单位并解决 Yahoo 多日周期复权缺陷。建议的实践姿势是:对多日周期(周线/月线)始终开启repair=True以获得更干净的复权数据;对1d数据按需开启并留意微型股息可能引发的极低概率误报;获取真实货币时以Ticker.history_metadata['currency']为准。如需深入研读实现,可重点查看 yfinance/scrapers/history.py 中的修复管线与 tests/test_price_repair.py 的测试用例。

【免费下载链接】yfinanceDownload market data from Yahoo! Finance's API项目地址: https://gitcode.com/GitHub_Trending/yf/yfinance

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询