yfinance 基本面与衍生品 · 财务报表与期权链

财务报表与期权链:字段、日期口径与空表到底怎么处理

财务数据与行情数据的差异不只是「内容不同」:报表的列是报告期,不是公告日,这在回测里会造成「用了当时还不知道的信息」;实测中 balance_sheet 的列 dtype 是 object 而不是数值型,个别报表还会在超时后返回空表;期权链虽然有 14 个字段,但不少合约的成交量是空的。本页把「字段是什么、日期代表什么、缺了怎么办」逐条写清楚。

实测版本:0.2.58 与 1.7.0实测标的:AAPL(美股)依据:本机实测行列数 + 官方签名文档
报表:报告期非公告日
dtype 与单位
期权链 14 字段
空表与流动性过滤
依据官方签名文档与本站实测绘制的财务/期权取数边界示意;非官方架构图。
yfinance · Statements

三个报表分别长什么样:实测形状与日期口径

每一项都是本站对 AAPL 的真实调用结果,行数会随公司不同而变化。

报表实测形状列是什么注意点
income_stmt(年)本次实测返回空表 (0, 0);quarterly_income_stmt 正常返回 33 行 × 5 列列 = 报告期结束日(如 2026-06-30)单接口空表要按「取数失败」处理并重试,不要当成公司无数据
balance_sheet69 行 × 4 列(2025-09-30 → 2022-09-30)列 = 财年结束日实测列 dtype 为 object,参与计算前先转数值
cashflow51 行 × 4 列列 = 财年结束日首行为 Free Cash Flow,可直接用于自由现金流口径核对
get_income_stmt(freq="yearly")官方签名:as_dict=False, pretty=False, freq='yearly'freq 可为 yearly/quarterly/trailing同一份数据的不同口径入口,比属性访问更可控
earnings_dates实测索引时区 America/New_York,时间 15:00 或 16:30列 = EPS Estimate / Reported EPS / Surprise(%)默认顺序不是时间递增,用之前先 sort_index()
history_metadata(对照项)含 currency 等元信息——核对真货币用它,不要用 info
yfinance · Point in time

point-in-time 问题:报表什么时候才「变成已知」

这类错误不会报错,只会让回测结果虚高,所以必须靠流程而不是靠代码检查来防。

错误写法

把 income_stmt 的列当成时间轴直接对齐到财年结束日,然后写「在 2025-09-30 用 2025 财年数据做决策」。实际公告通常在此之后数周,等于提前知道了未来。

正确做法

把「报告期」与「公告日」建两张表:报告期来自报表列,公告日来自 earnings_dates 或公司公告。回测时按公告日做 as-of join,并留一个缓冲(例如公告次日开盘才可用)。

一个务实的最小防线

在数据管道里记录每个报表快照的抓取时间。只要你有「某天抓到的报表长什么样」的历史,就能重放 point-in-time 视图;否则事后无法还原。这一点比任何公式都重要。

本站的诚实说明:官方没有提供 point-in-time 财务报表接口,income_stmt 返回的是「你抓取那一刻的 Yale 数据库」视图。因此这项风险只能由使用方自己用归档流程解决,不能指望库来解决。
yfinance · Options

期权链:14 个字段逐个说明

官方没有给出字段级文档,下表按本站实测列名与常见语义整理,标注了本站未验证的部分。

字段含义(按名称与官方数据惯例)本站实测观察
contractSymbol合约代码每行唯一,可用于去重
lastTradeDate最后成交时间未成交合约可能为空或很旧,用它判断新旧
strike行权价与 inTheMoney 一起用于筛选
lastPrice / bid / ask最新价 / 买价 / 卖价同一到期日实测 lastPrice 无缺失,但 bid/ask 可能为 0(无报价)
change / percentChange相对前收的变化与股票行情字段同名但口径为合约价
volume / openInterest当日成交量 / 持仓量实测 0.2.58 一次 71 行 calls 中 11 行 volume 为 0(未成交)
impliedVolatility隐含波动率(小数形式)做曲面/期限结构前先按流动性过滤,否则噪声极大
inTheMoney是否价内布尔型,可直接用于分组统计
contractSize / currency合约乘数 / 计价货币跨市场比较前必须核对货币与乘数
使用边界:本站没有实测期权链的历史归档与复权处理,也不对隐含波动率的计算质量下结论;上表只描述字段与实测到的缺失情况。
yfinance · Recipe

财务与期权怎么取数:最小可用代码片段

把重试、类型转换、排序、流动性过滤一次性写进去,避免每次调用都要重想一遍。

import yfinance as yf
import pandas as pd

t = yf.Ticker("AAPL")

# 1) 报表:空表按失败处理,转数值,按公告日另表管理
def get_stmt(name, tries=2):
    for _ in range(tries):
        df = getattr(t, name)
        if df is not None and not df.empty:
            return df.apply(pd.to_numeric, errors="coerce")
    print("[warn] empty after retry:", name)
    return pd.DataFrame()

bs  = get_stmt("balance_sheet")
cf  = get_stmt("cashflow")
qis = get_stmt("quarterly_income_stmt")
print("shapes:", bs.shape, cf.shape, qis.shape)

# 2) 财报公告时间:默认顺序不是时间递增,先排序
ed = t.get_earnings_dates(limit=12)
if ed is not None and not ed.empty:
    ed = ed.sort_index()
    print(ed.head(3)[["EPS Estimate", "Reported EPS"]])

# 3) 期权链:按流动性过滤后再看隐含波动率
if t.options:
    chain = t.option_chain(t.options[0])
    calls = chain.calls
    liq = calls[(calls["volume"].fillna(0) > 0) & (calls["bid"] > 0)]
    print("calls:", len(calls), "liquid:", len(liq))
    print("moneyness:", calls["inTheMoney"].value_counts().to_dict())
yfinance · Limits

财务与期权数据有哪些边界:能做什么、不能做什么

把「能取到」和「能直接用」分开写。

你想做的事yfinance 能提供什么缺口在哪本站态度
对比两家公司财务报表字段齐全(实测资产负债表单表 69 行)会计口径差异、并表范围不在库的处理范围内可以取数,口径自己对齐
做估值模型拿到三大报表的原始数字没有单位统一、没有 TTM 自动计算(需用 trailing 口径自行核对)可作为数据源之一
回测里用财务因子提供报表数值没有 point-in-time 视图必须自建归档流程
分析期权曲面当前快照 14 个字段无历史期权数据;流动性差的合约字段为空可做当日研究,历史研究需自采
判断公司是否「没有某指标」info 字段以空值表示空值既可能是「没有」也可能是「本次未取到」需要二次来源核对,不能只看空值
FAQ

yfinance 常见问题

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

yfinance 财务报表的列是什么日期?是公告日吗?

不是公告日,是报告期结束日。本站实测 AAPL 的年报列是 2025-09-30 / 2024-09-30 / 2023-09-30 / 2022-09-30,季报列是季度结束日(如 2026-06-30)。做回测时,如果直接把某财年的数据用在财年结束当天,就用了当时还没公告的信息——这是典型的 point-in-time 错误。财报的实际公告时间要另外用 earnings_dates 或 calendar 获取。

为什么报表里的数字不能直接算?

因为实测列的 dtype 是 object 而不是 float。直接做算术或比较可能得到意外结果,建议先 df.astype(float) 或 pd.to_numeric(errors="coerce")。另外要注意单位:不同项目可能以「元」或「百万」为单位,官方不做口径换算。

报表会不会返回空表?

会。本站实测在 0.2.58 环境下 income_stmt 返回过 (0, 0) 的空表(耗时约 21 秒后返回),而同一轮里 balance_sheet(69 行 × 4 列)、cashflow(51 行 × 4 列)、quarterly_income_stmt(33 行 × 5 列)都正常。处理方式:把空表当成「本次取数失败」而不是「这家公司没有财报」,做重试并记录,必要时用 get_income_stmt(freq="quarterly") 换口径再试。

yfinance 期权链有哪些字段?为什么有些合约没有成交量?

本站实测 calls 与 puts 各 14 列:contractSymbol, lastTradeDate, strike, lastPrice, bid, ask, change, percentChange, volume, openInterest, impliedVolatility, inTheMoney, contractSize, currency。流动性差的合约当天没有成交,volume 就会是空值(本站 0.2.58 一次实测中 71 行 calls 里有 11 行 volume 为 0)。用 impliedVolatility 做曲面之前,先按 volume/openInterest 过滤。

期权有历史数据吗?

本页覆盖的 option_chain(expiry) 返回的是当前快照,不是历史序列。要做历史期权分析,必须自己按日抓取并落盘归档——这一点官方文档没有承诺任何历史期权服务。本站没有实测历史归档方案,因此不对「用什么方式归档最合适」下结论。

fast_info 和 info 有什么区别?

fast_info 是一个轻量对象,本站实测暴露 20 个键(currency、dayHigh、dayLow、exchange、fiftyDayAverage、lastPrice、lastVolume、marketCap、open、previousClose、quoteType、regularMarketPreviousClose、shares、tenDayAverageVolume、threeMonthAverageVolume、timezone、twoHundredDayAverage、yearChange、yearHigh、yearLow);info 返回完整字典(字段数随标的与时间变化)。注意 info 在部分网络环境下会超时——本站实测就遇到过,见故障排查页。

下一步:ticker 与市场覆盖

财务与期权主要围绕美股;如果你要取 A 股、港股、指数或 ETF,下一站是 ticker 后缀与市场日历的差异。