yfinance 复权语义 · Close、Adj Close 与 auto_adjust

Close、Adj Close 与 auto_adjust:那一列到底去哪了?

这是本站认为最值得先弄懂的一处语义:auto_adjust 取默认值 True 时,Adj Close 这一列不是一个近似值,而是整列消失——因为实现把复权比率乘进了 OHLC 并重命名,再把原始价格列删掉。于是「我明明记得有 Adj Close」和「代码里 KeyError: 'Adj Close'」会同时出现。本页把源码级语义、实测列变化、A 股复权价证据与回测口径影响一次讲透。

实测版本:0.2.58 与 1.7.0(列变化一致)依据:源码 utils.auto_adjust + 本机实测关联:分红拆股与价格修复页
Adj Close ÷ Close
把比率乘进 OHLC
删除原始 OHLC
重命名并返回
依据官方源码 utils.auto_adjust() 绘制的列变换示意;非官方流程图。
yfinance · Source

源码级语义:auto_adjust 到底做了什么

下面这段实现取自官方仓库(固定 commit),删去了注释与空行,逻辑一字未改。

def auto_adjust(data):
    col_order = data.columns
    df = data.copy()
    ratio = (df["Adj Close"] / df["Close"]).to_numpy()
    df["Adj Open"] = df["Open"] * ratio
    df["Adj High"] = df["High"] * ratio
    df["Adj Low"]  = df["Low"]  * ratio

    df.drop(["Open", "High", "Low", "Close"], axis=1, inplace=True)

    df.rename(columns={
        "Adj Open": "Open", "Adj High": "High",
        "Adj Low": "Low", "Adj Close": "Close"
    }, inplace=True)

    return df[[c for c in col_order if c in df.columns]]
逐行看三件事:①复权只用到 Adj Close / Close 这一个比率,所以成交量不做复权;②原始的 OHLC 四列被直接 drop 掉,想同时保留就得自己再取一次;③最后按原列顺序过滤,因此 Adj Close 不在返回结果里。
yfinance · Measured

auto_adjust 怎么影响列:同一标的只改一个参数的实测对照

下表是本站对 AAPL、period="1mo" 的实测结果(0.2.58 与 1.7.0 一致)。

调用行数列数列清单结论
history(period="1mo")227Open, High, Low, Close, Volume, Dividends, Stock Splits默认即复权:Close 已是复权价,没有 Adj Close
history(period="1mo", auto_adjust=True)227同上与默认完全一致
history(period="1mo", auto_adjust=False)228上列 + Adj Close同时拿到原始 Close 与 Adj Close
history(period="1mo", auto_adjust=None)228同上None 不是 True:显式传 None 仍保留 Adj Close
history(period="1mo", actions=False)225Open, High, Low, Close, Volume去掉事件列,价格仍为复权口径
history(period="1mo", repair=True)2287 列 + Repaired?修复与复权是两个独立开关,可叠加
可复现的最小断言:set(raw.columns) - set(adj.columns) == {"Adj Close"}。本站实测该断言在两代版本上都成立——如果你跑出来不是这个结果,说明还有别的参数在起作用。
yfinance · Semantics

三种口径怎么选:场景 → 选项 → 理由

不要把口径当成风格偏好,yfinance 的 auto_adjust 直接决定你的数字代表什么。

你的场景该选哪个口径理由注意点
算收益、夏普、最大回撤等指标auto_adjust=True(默认)复权价把分红拆股的影响连续地体现在价格里,收益序列才连续此时 Close 不是真实成交价,不要拿去和行情软件对价
展示「当时股价是多少」auto_adjust=False,取 Close未复权收盘价才对应当天的真实报价分红除权日会出现价格跳空,那不是行情下跌
要同时看两种口径auto_adjust=False,同时用 Close 与 Adj Close一张表里两个列都在,便于交叉检查两列不要混着进同一个收益计算
核对 Yahoo 的复权是否正确repair=True + auto_adjust=Falserepair 会新增 Repaired? 列标记被修过的行repair 只在 1 日线口径下生效,详见分红修复页
A 股做回测auto_adjust=True本站实测 A 股默认返回的就是复权价,直接可用记得确认你复现的不是「行情软件的两位小数价」
跨市场批量对比收益率auto_adjust=True + 分开处理各市场日历并集表会出现 NaN,直接算收益会引入空洞详见取数结构页的并集与 dtype 说明
yfinance · Get both

想同时拿到 Close 与 Adj Close 怎么写:一段可直接复用的代码

如果你既要复权收益、又要真实价格,不要靠记忆切换参数,直接取两次并显式对齐。

import yfinance as yf
import pandas as pd

t = yf.Ticker("AAPL")
p = "1y"

real = t.history(period=p, auto_adjust=False)   # 真实价 + Adj Close
adj  = t.history(period=p, auto_adjust=True)    # 全复权,无 Adj Close

# 对齐后放进一张表:Close 是真实价,Ret_* 是两种口径的日收益
out = pd.DataFrame(index=real.index)
out["Close_real"]    = real["Close"]
out["Adj_Close"]     = real["Adj Close"]
out["Ret_from_Close"]  = real["Close"].pct_change()        # 未复权口径
out["Ret_from_AdjClose"] = real["Adj Close"].pct_change()  # 复权口径
out["Ret_from_auto_adjust"] = adj["Close"].pct_change()    # 与上一列应一致

gap = (out["Ret_from_AdjClose"] - out["Ret_from_auto_adjust"]).abs().max()
print("max diff:", float(gap))   # 复权口径的两种取法应当非常接近
为什么要跑这个对比:它同时验证了你理解的口径与库实现的口径是否一致。如果这一行的差值异常大,先查是不是取数区间不同、或某个来源被 repair 动过。
yfinance · Boundary

除了复权口径,还有哪些因素会改变你的结论?

价格口径只是其中一层,下面两件事同样会让「看起来一样的数据」算出不一样的收益。

分红与拆股事件本身

库会把 Dividends 与 Stock Splits 作为独立列返回(实测 AAPL 的 actions 表覆盖 1987 年起、共 97 行记录)。把它们与价格序列对照,是发现复权异常的实用手段。详见分红拆股与价格修复页。

Yahoo 侧的价格错误

官方 price repair 文档自述「Only US market data appears perfect」,并列出漏做分红调整、100 倍货币错位、除权日错位等错误类型。repair=True 可以修一部分,但它本身也有假阳性风险。

时区与交易日历

同一段自然时间在不同市场对应的交易日数不同(实测 1 个月窗口:美股 22 行、A 股 17 行)。跨市场比较收益率时,先对齐日历再谈口径。

FAQ

yfinance 常见问题

下面的回答都指向可核验的官方文件或本站实测;与官方表述冲突时,以官方仓库与 docs 为准。

yfinance 的 Close 和 Adj Close 有什么区别?

Close 是当日收盘价,Adj Close 是把分红与拆股等事件回溯调整后的收盘价。做收益序列要用复权口径,做真实点位/价格判断要用原始收盘价。本站实测:默认 auto_adjust=True 时表里只有 Close(它已经是复权价),auto_adjust=False 时才会同时出现 Close 与 Adj Close。

为什么设了 auto_adjust=True,Adj Close 就找不到了?

因为实现在复权后把该列删掉了。源码 utils.auto_adjust() 的逻辑是:先算 ratio = Adj Close / Close,把 ratio 乘到 Open/High/Low 上,然后 drop(["Open","High","Low","Close"]),再把 Adj Open/Adj High/Adj Low/Adj Close 重命名回原名。所以最终列集合是 Open/High/Low/Close/Volume(外加事件列),没有 Adj Close 这一项。

只记住一句话:默认到底是不是复权价?

默认 auto_adjust=True,即默认拿到的就是复权价。download() 运行时还会打印一行提示:YF.download() has changed argument auto_adjust default to True。本站实测两代版本都会打印这行,说明这是有意的默认值调整,不是巧合。

为什么我取 A 股,价格和行情软件对不上?

很可能因为默认复权。本站实测 000001.SZ(平安银行)在 period="1mo" 下的收盘价是 11.4488 这类带多位小数的值,而不是行情软件上的两位小数成交价——这就是复权后的结果。要真实成交价请显式 auto_adjust=False 并取 Close 列。

yfinance 的 auto_adjust 和 back_adjust 有什么不同?

官方签名里两个参数都在:auto_adjust 是把复权比率乘进价格列并去掉 Adj Close;back_adjust 是另一套向后调整的语义。两者默认分别为 True 与 False。本站实测覆盖了 auto_adjust 的 True/False/None 三种取值,back_adjust 的取值差异请以官方实现为准,本站不做未经实测的断言。

复权口径选错,对回测结果影响有多大?

影响的是「你回测的到底是什么」。用未复权价算收益,会把除权除息当成一次真实的暴跌;用复权价判断「当时股价是多少钱」则会失真。本站的建议是:收益与指标一律用复权价,价格展示与点位判断用未复权价,并在代码里显式写出你选了哪个口径,而不是依赖默认值。具体检查清单见数据审计与回测页。

下一步:把事件与错误分开

复权口径解决的是「怎么算」,分红拆股与 repair 解决的是「数据本身对不对」——两件事要分开处理。