第四章:数据获取与清洗——用yfinance搞定美股股息数据

做股息策略,第一步就是拿数据。这事儿听起来简单,但实际坑不少。我见过太多人花大把时间在数据清洗上,结果发现源头数据就有问题。今天咱们就聊聊怎么用yfinance高效获取美股股息数据,以及我这些年踩过的坑。

4.1 为什么选yfinance?

市面上能拿美股数据的库不少,但yfinance是我个人最常用的。原因有三:

  • 免费——不用API Key,不用注册,直接pip install就能用
  • 数据全——股息、拆股、历史价格、财务报表,一个库搞定
  • 社区活跃——遇到问题基本都能在Stack Overflow找到答案

当然,它也有缺点。比如数据源是雅虎财经,偶尔会有延迟或缺失。我在做回测时就遇到过某只股票股息数据突然断档的情况,后来发现是雅虎那边的问题。所以我的建议是:yfinance适合做快速原型和策略验证,生产环境最好再配个备用数据源。

4.2 安装与基础用法

先装库,这个不用多说:

pip install yfinance

然后咱们试试获取一只股票的股息数据。拿可口可乐(KO)举例,这家公司连续分红几十年,是股息策略的经典标的。

import yfinance as yf

# 创建股票对象
ko = yf.Ticker("KO")

# 获取股息数据
dividends = ko.dividends
print(dividends.head())

输出大概长这样:

Date
1990-01-02    0.015
1990-04-02    0.015
1990-07-02    0.015
1990-10-01    0.015
1991-01-02    0.015
Name: Dividends, dtype: float64

嗯,数据格式很干净。但这里有个细节——dividends返回的是Series,索引是日期,值是每股股息金额。我刚开始用的时候以为它会返回DataFrame,结果直接报错,折腾了半天。

4.3 数据清洗——别让脏数据毁了你的策略

拿到原始数据只是第一步。真正的活儿在后头。我总结了几种常见的数据问题:

4.3.1 缺失值处理

有些股票可能某段时间没分红,或者数据源漏了。直接填充0还是用前值填充?这得看你的策略逻辑。

# 检查缺失值
print(dividends.isnull().sum())

# 用前值填充(假设股息稳定增长)
dividends_filled = dividends.fillna(method='ffill')

# 或者直接填充0(假设没分红)
dividends_zero = dividends.fillna(0)

我个人习惯用前值填充。因为成熟的美股公司很少突然取消分红,就算取消,也会提前公告。但如果你在做高频策略,那得小心——前值填充会引入未来信息。

4.3.2 异常值检测

有一次我在分析某只REITs的股息数据时,发现某个月的分红突然翻了10倍。一开始以为发现了宝藏,后来一查——原来是数据源把特殊分红和常规分红混在一起了。

# 简单异常值检测:超过3倍标准差就标记
mean_div = dividends.mean()
std_div = dividends.std()
outliers = dividends[(dividends - mean_div).abs() > 3 * std_div]
print(f"发现 {len(outliers)} 个异常值")

遇到这种情况,我建议先查一下公司公告。如果是特殊分红(比如出售资产后的一次性分红),最好单独处理,别让它影响你的策略参数。

4.3.3 日期对齐

股息数据是按除权日记录的,但价格数据是按交易日记录的。如果你要做股息再投资策略,必须把两者对齐。

# 获取价格数据
price = ko.history(period="max")

# 对齐股息和价格数据
combined = price[['Close']].join(dividends, how='left')
combined['Dividends'] = combined['Dividends'].fillna(0)

print(combined.head())

这里要注意:how='left'表示以价格数据为主,股息数据只补充到对应的日期。如果某天没分红,股息列就是0。

4.4 实战:批量获取多只股票的股息数据

做策略不能只看一只股票。咱们来写个函数,批量获取多只股票的股息数据:

def get_dividends_batch(tickers, start_date="2000-01-01"):
    """
    批量获取股息数据
    tickers: 股票代码列表
    start_date: 起始日期
    """
    div_data = {}
    
    for ticker in tickers:
        try:
            stock = yf.Ticker(ticker)
            divs = stock.dividends
            divs = divs[divs.index >= start_date]
            div_data[ticker] = divs
            print(f"{ticker}: 获取成功,共 {len(divs)} 条记录")
        except Exception as e:
            print(f"{ticker}: 获取失败 - {e}")
    
    return div_data

# 测试一下
tickers = ["KO", "PEP", "JNJ", "PG", "O"]
dividends_dict = get_dividends_batch(tickers)

这个函数我用了好几年,基本没出过问题。但有一点要注意——yfinance的请求频率有限制。如果你一次请求太多股票,可能会被临时封IP。我的经验是每次不超过20只,中间加个1秒延迟。

4.5 数据存储——别每次都重新下载

股息数据不会天天变。每次都重新下载既浪费时间,又可能触发限流。我建议存到本地:

import pandas as pd

# 合并所有股息数据
div_df = pd.DataFrame(dividends_dict)
div_df.to_csv("dividends_data.csv")

# 下次直接读取
div_df_loaded = pd.read_csv("dividends_data.csv", index_col=0, parse_dates=True)

嗯,这里有个小技巧——存成CSV时记得把日期列设为索引,读取时用parse_dates=True自动解析。不然你拿到的日期是字符串,后面处理起来很麻烦。

4.6 本章知识体系

说了这么多,咱们用一张图总结一下整个数据获取与清洗的流程:

股息数据获取与清洗流程 1. 数据获取 yfinance.Ticker().dividends 2. 数据清洗 缺失值·异常值·日期对齐 3. 数据存储 CSV/Parquet 数据清洗核心步骤 缺失值处理 ffill / 填充0 异常值检测 3σ原则 / 手动核查 日期对齐 join + how='left' 注意:特殊分红需单独处理,避免影响策略参数 批量获取:循环 + 延迟 + 异常处理

核心要点:

  • yfinance获取股息数据简单高效,但要注意数据源可靠性
  • 数据清洗是策略成败的关键,缺失值、异常值、日期对齐一个都不能少
  • 批量获取时注意限流,建议存本地避免重复下载

我的小建议:刚开始做股息策略时,别急着上复杂模型。先把数据流程跑通,用几只经典分红股(KO、JNJ、PG)练手。等数据清洗的代码稳定了,再扩展到全市场。这样出了问题也好排查。

注意:yfinance的数据是雅虎财经提供的,偶尔会有延迟。如果你做的是实盘策略,建议对比一下其他数据源(比如Alpha Vantage或Quandl)。另外,股息数据中的特殊分红(Special Dividend)一定要单独标记,别让它混进常规分红里。

好了,数据获取和清洗这块就聊到这儿。下一章咱们会深入分析股息率计算和分红稳定性评估,到时候这些清洗好的数据就能派上大用场了。

专注资料整理