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.
开启修复后有两个关键行为:
- 新增
Repaired?列:返回的 DataFrame 会多出一列Repaired?(布尔值),逐行标注该行是否被修复。唯一的例外是"缺失股息调整"——该修复只修改Adj Close,并不会把Repaired?置为True。 - 货币修复:修复逻辑还会纠正 Yahoo 的货币单位混淆(如
GBp与GBP、ZAc与ZAR、ILA与ILS)。因此官方明确建议:要获取真实货币,请检查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 倍。
两种表现形式:
- 随机散布:100x 错误随机分散在整张表中——这类错误通过
scipy模块检测(源码见 _fix_unit_random_mixups,它使用scipy.ndimage与局部中位数对比找出 ~100x 的离群值); - 成块出现:错误集中在一个时间段内,原因是 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 类:
- 股息调整缺失,或调整幅度 100x 过大/过小;
- 重复股息(7 天内出现两次);
- 股息相对除息日价格跌幅 100x 过大/过小;
- 除息日(ex-div date)错误(价格跌幅出现在数天/数周之后);
- 新增:资本利得(capital gains)被重复计算。
官方经验之谈:大多数股息错误出现在伦敦证券交易所(£/pence 单位混淆),但没有任何交易所绝对安全。测试数据目录 tests/data/ 中大量*-bad-div*.csv与*-fixed.csv测试对(如1398-HK-1d-bad-div、KME-MI-1d-bad-div、SSNLF-1d-bad-div、4063-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.00001.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.0000.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.008. 除息日错误(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 中按严格顺序执行以下修复步骤:
_standardise_currency()(history.py):统一货币单位——GBp→GBP、ZAc→ZAR、ILA→ILS,必要时把价格放大/缩小 100 倍,并在history_metadata中标记currencyRepaired;- Volume 中的 NaN 先填 0;
_fix_bad_div_adjust()(history.py):修复股息调整问题。源码细节:直接跳过1wk/1mo/3mo/1y周期;科威特第纳尔(KWF)按 1000 进位而非 100;"过大"的判定阈值是0.035(3.5%);若表中存在资本利得则整个函数跳过(避免与资本利得修复互相干扰);- 先修复最后一行的零值(确保最新行在后续修复前正确);
- 对非外汇(ticker 不含
=)标的执行_fix_unit_mixups()(100x 单位错误)与_fix_bad_stock_splits()(拆股修复,history.py); _fix_zeroes():修复零值价格;_repair_capital_gains():资本利得重复计算修复(新版功能);- 还原第一步的货币换算,保证返回数据使用标的实际报价货币。
此外还有两个值得注意的边界行为:
repair=True与5d周期冲突:源码 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_daily、test_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.csv与1398-HK-1d-bad-div-fixed.csv、4063-T-1d-bad-stock-split.csv与4063-T-1d-bad-stock-split-fixed.csv、AET-L-1d-100x-error.csv与AET-L-1d-100x-error-fixed.csv、DODFX-1d-cg-double-count.csv与DODFX-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),仅供参考