yfinanceFundsData完全指南:用 Python 抓取 ETF 与共同基金的持仓、费率与资产配置数据
【免费下载链接】yfinanceDownload market data from Yahoo! Finance's API项目地址: https://gitcode.com/GitHub_Trending/yf/yfinance
本指南围绕 yfinance 官方 API 参考文档 FundsData 类 展开,深入讲解如何通过Ticker.funds_data获取 ETF(交易所交易基金)与共同基金(Mutual Fund)的底层数据,包括基金描述、投资组合概况、运营费率、前十大持仓、债券评级与行业权重等。读完本文,你将掌握FundsData的全部公开属性、底层数据流(quoteSummaryAPI 与_fetch_and_parse解析管线)、返回的数据结构(dict/pd.DataFrame),并能直接参考 示例代码 与 单元测试 快速落地实战。
一、FundsData是什么:为 ETF / 共同基金定制的数据门面
yfinance 的Ticker类主要面向个股,但 ETF 与共同基金除了行情与历史价格外,还有一套特有的"基金数据":持仓明细、资产类别分布、行业权重、债券评级、费率结构等。这些数据在 Yahoo! Finance 上属于quoteSummary接口的不同模块,yfinance 将其封装为一个独立的公开类:
- 类全名:
yfinance.scrapers.funds.FundsData(见 funds.py) - 文档注册入口:
doc/source/reference/yfinance.funds_data.rst,属于 API Reference 的一部分(reference/index.rst) - 公开接入点:
Ticker.funds_data属性
从 ticker.py 可以看到,Ticker通过属性转发到基类:
@property def funds_data(self) -> FundsData: return self.get_funds_data()而get_funds_data()在 base.py 中实现了懒加载单例模式:
def get_funds_data(self) -> Optional[FundsData]: if not self._funds_data: self._funds_data = FundsData(self._data, self.ticker) return self._funds_data即:首次访问时创建FundsData实例,之后复用,避免重复初始化。构造FundsData只需要两个参数(funds.py):
| 参数 | 类型 | 说明 |
|---|---|---|
data | YfData | 负责发请求的底层数据对象(共享Ticker的会话与缓存) |
symbol | str | 基金代码,如"SPY"、"VTSAX" |
适用范围说明:
FundsData只对 ETF 与共同基金有效。对普通股票(如 AAPL)调用_fetch_and_parse()会抛出YFDataException——这一点在测试 test_ticker.py 中有明确验证:ticker.funds_data._fetch_and_parse()对 AAPL 会assertRaises(YFDataException)。
二、底层数据流:一次请求,四个quoteSummary模块
FundsData的源码注释明确列出了它查询的模块(funds.py):
Queried Modules:
quoteType,summaryProfile,fundProfile,topHoldings
2.1 请求构造
_fetch()方法(funds.py)负责组装 HTTP 请求:
def _fetch(self): modules = ','.join(["quoteType", "summaryProfile", "topHoldings", "fundProfile"]) params_dict = {"modules": modules, "corsDomain": "finance.yahoo.com", "symbol": self._symbol, "formatted": "false"} result = self._data.get_raw_json(_QUOTE_SUMMARY_URL_ + self._symbol, params=params_dict) return result关键细节:
- 请求端点:
_QUOTE_SUMMARY_URL_ = f"{_BASE_URL_}/v10/finance/quoteSummary/",其中_BASE_URL_在 const.py 中定义为https://query2.finance.yahoo.com; - 四个模块一次性拼入
modules参数,一次 HTTP 往返拿到全部基金数据; "formatted": "false"要求返回未格式化的原始数值(raw 值),方便后续解析与计算,这与后面_parse_raw_values的设计是配套的;- 请求通过
YfData.get_raw_json发出,从而复用了 yfinance 的会话管理、限流与缓存机制。
2.2 解析管线
_fetch_and_parse()(funds.py)是核心解析入口:
def _fetch_and_parse(self) -> None: result = self._fetch() try: data = result["quoteSummary"]["result"][0] # check quote type self._quote_type = data["quoteType"]["quoteType"] self._parse_description(data["summaryProfile"]) self._parse_top_holdings(data["topHoldings"]) self._parse_fund_profile(data["fundProfile"]) except KeyError: if not YfConfig.debug.hide_exceptions: raise raise YFDataException(f"{self._symbol}: No Fund data found.") except Exception as e: if not YfConfig.debug.hide_exceptions: raise logger = utils.get_yf_logger() logger.error(f"Failed to get fund data for '{self._symbol}' reason: {e}") ...要点解读:
- 先取 quoteType 做类型校验——确保目标确实是基金类标的;
- 三个解析器分工明确:
_parse_description(简介)、_parse_top_holdings(持仓族数据)、_parse_fund_profile(基金画像); - 异常处理双通道:
KeyError表示响应中没有基金数据(典型的非基金标的场景),统一转为YFDataException;其他异常则记录日志。两条路径都受全局配置YfConfig.debug.hide_exceptions控制,关闭该开关(默认关闭hide_exceptions即为False?)时会直接向上抛出原始异常,方便调试(详见 config.py)。
懒加载机制:所有公开属性(如
description、top_holdings)都遵循同一模式——缓存字段为None时触发一次_fetch_and_parse(),之后直接返回缓存结果。因此多次访问同一属性不会重复发请求,这也是测试里连续调用多个属性而只发生少量请求的原因。
三、公开属性全览:从简介到持仓的一站式接口
FundsData共暴露 10 个公开属性/方法,以下按主题分组,均可在 funds.py 中找到实现。
3.1 基金画像(fundProfile / summaryProfile)
quote_type()(funds.py) 返回字符串类型的基金类别,例如"ETF"或"MUTUALFUND"。注意它是一个方法而非属性,调用需写data.quote_type()。
description(funds.py) 返回基金的longBusinessSummary(长文业务简介),类型为str。底层取自summaryProfile模块。
fund_overview(funds.py) 返回Dict[str, Optional[str]],包含三个键(见_parse_fund_profile,funds.py):
| 键 | 含义 |
|---|---|
categoryName | 基金所属类别名称(如 Large Growth、High Yield Bond) |
family | 基金家族 / 发行公司(如 Vanguard、SPDR) |
legalType | 法律结构类型(如 Open Ended Investment Company) |
fund_operations(funds.py) 返回pd.DataFrame,对比基金自身与"同类平均(Category Average)"的运营指标(funds.py):
| 指标(index: Attributes) | 字段 |
|---|---|
| Annual Report Expense Ratio | feesExpensesInvestment.annualReportExpenseRatio |
| Annual Holdings Turnover | feesExpensesInvestment.annualHoldingsTurnover |
| Total Net Assets | feesExpensesInvestment.totalNetAssets |
DataFrame 的列为[Attributes, <symbol>, Category Average],行索引为指标名。其中基金侧数值取自feesExpensesInvestment,同类平均取自feesExpensesInvestmentCat。
3.2 持仓族数据(topHoldings)
asset_classes(funds.py) 返回Dict[str, float],表示基金资产的类别分布百分比(funds.py),键包括:
cashPosition(现金)stockPosition(股票)bondPosition(债券)preferredPosition(优先股)convertiblePosition(可转债)otherPosition(其他)
top_holdings(funds.py) 返回pd.DataFrame,行索引为Symbol,列含Name(持仓名称)与Holding Percent(持仓占比)。解析逻辑见 funds.py:
_holdings = data.get("holdings", []) for item in _holdings: _symbol.append(item["symbol"]) _name.append(item["holdingName"]) _holding_percent.append(item["holdingPercent"]) self._top_holdings = pd.DataFrame({ "Symbol": _symbol, "Name": _name, "Holding Percent": _holding_percent }).set_index("Symbol")equity_holdings(funds.py) 返回pd.DataFrame,行索引为Average,列为[Average, <symbol>, Category Average],六行估值指标(funds.py):
- Price/Earnings(市盈率)
- Price/Book(市净率)
- Price/Sales(市销率)
- Price/Cashflow(市现率)
- Median Market Cap(市值中位数)
- 3 Year Earnings Growth(三年盈利增长)
bond_holdings(funds.py) 返回pd.DataFrame,同样带同类平均对比,三行指标(funds.py):
- Duration(久期)
- Maturity(到期期限)
- Credit Quality(信用质量)
bond_ratings(funds.py) 返回Dict[str, float],债券评级分布。解析采用字典推导(funds.py):
self._bond_ratings = dict((key, d[key]) for d in data.get("bondRatings", []) for key in d)sector_weightings(funds.py) 返回Dict[str, float],行业权重分布(如 Technology、Health Care 等),解析方式与bond_ratings相同(funds.py)。
数值清洗的通用工具:
_parse_raw_values(data, default=None)(funds.py)专门处理 Yahoo 的{"raw": ..., "fmt": "..."}双字段结构——只取raw原始数值,若传入的不是 dict 则原样返回,缺字段时返回default(多数场景为pd.NA)。这正是"formatted": "false"请求模式下安全解析的关键。
3.3 各属性的返回类型速查表
| 属性 | 返回类型 | 数据来源模块 |
|---|---|---|
quote_type() | str | quoteType |
description | str | summaryProfile |
fund_overview | Dict[str, Optional[str]] | fundProfile |
fund_operations | pd.DataFrame | fundProfile |
asset_classes | Dict[str, float] | topHoldings |
top_holdings | pd.DataFrame | topHoldings |
equity_holdings | pd.DataFrame | topHoldings |
bond_holdings | pd.DataFrame | topHoldings |
bond_ratings | Dict[str, float] | topHoldings |
sector_weightings | Dict[str, float] | topHoldings |
四、实战:五分钟拉取 SPY 的完整基金画像
官方在 examples/funds_data.py 中给出了最简示例,文档 index.rst 的"Funds"一节也展示了同样的用法:
import yfinance as yf # 1. 拿到 FundsData 对象 spy = yf.Ticker('SPY') data = spy.funds_data # 2. 基金简介 data.description # 3. 运营信息 data.fund_overview # 类别 / 家族 / 法律类型 data.fund_operations # 费率、换手率、净资产(含同类平均) # 4. 持仓族信息 data.asset_classes # 资产类别分布 data.top_holdings # 前十大持仓 data.equity_holdings # 股票估值指标(含同类平均) data.bond_holdings # 债券特征(含同类平均) data.bond_ratings # 债券评级分布 data.sector_weightings # 行业权重几个实战要点:
- 兼容性判断:先用
data.quote_type()判断标的类型,避免对非基金标的误用(普通股票会触发YFDataException,见 tests/test_ticker.py 的TestTickerFundsData用例); - 测试覆盖的标的类型:仓库测试用 SPY(股票 ETF)、JNK(债券 ETF)、VTSAX(共同基金)三类标的验证了全部属性(test_ticker.py),说明
FundsData对权益 ETF、债券 ETF 与共同基金均有良好支持; - 无重复请求:得益于懒加载缓存,同一
FundsData实例上多次访问任一属性不会产生额外网络请求,可放心在循环/批量脚本中使用; - 同类平均对比:
fund_operations、equity_holdings、bond_holdings均同时返回基金自身值与 Category Average,可直接用于横向对比基金相对同类的性价比与估值水平。
五、注意事项与限制
- 不适用于普通股票:
FundsData面向 ETF 与共同基金;对个股调用会因响应缺少基金模块而抛出YFDataException; fundPerformance模块未实现:源码注释明确说明"fundPerformance module is not implemented as better data is queryable using history"(funds.py)——历史业绩表现应改用Ticker.history()等历史数据接口获取,不要期待FundsData提供业绩曲线;- 依赖网络可用性:所有数据来自 Yahoo! Finance 的
quoteSummary接口,字段可能随上游 API 变化而增减;解析逻辑对缺失字段做了兜底(default/pd.NA),但字段结构变化仍可能影响结果完整性; - 异常开关:
YfConfig.debug.hide_exceptions(见 config.py)决定解析异常是被隐藏并以YFDataException兜底,还是直接抛出,排查问题时可以临时调整该配置查看完整错误栈与响应日志。
六、延伸阅读
- 类实现源码:yfinance/scrapers/funds.py
- API 参考文档:doc/source/reference/yfinance.funds_data.rst
- 入门示例:doc/source/reference/examples/funds_data.py 与 doc/source/index.rst 的 Funds 小节
- 完整测试用例:tests/test_ticker.py 中的
TestTickerFundsData - 接入方式与懒加载实现:yfinance/base.py、yfinance/ticker.py
- 底层请求地址常量:yfinance/const.py
【免费下载链接】yfinanceDownload market data from Yahoo! Finance's API项目地址: https://gitcode.com/GitHub_Trending/yf/yfinance
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考