第三章 数据获取与预处理:用yfinance搞定股票数据
做配对交易,第一步就是拿数据。这一步看似简单,但坑不少。我见过太多人花大量时间建模,结果数据本身就有问题——那真是白忙活。
今天咱们就聊聊,怎么用Python把数据拿对、洗干净、对齐好。
3.1 为什么选yfinance?
市面上能拿股票数据的库不少,比如pandas-datareader、tushare、baostock。但我个人最常用的是yfinance。原因有三:
- 免费且稳定:雅虎财经的数据源,不需要API Key
- 覆盖全球:美股、港股、A股都能拿(A股代码加后缀.SS或.SZ)
- 接口简洁:几行代码就能拿到历史数据
3.2 安装与基本用法
先装库,这个不用多说:
pip install yfinance
然后咱们写个最简单的例子,拿茅台和五粮液的数据:
import yfinance as yf
import pandas as pd
# 定义股票代码
tickers = ["600519.SS", "000858.SZ"]
# 下载数据
data = yf.download(tickers, start="2020-01-01", end="2023-12-31")
# 看看数据结构
print(data.head())
print(data.columns)
运行后你会发现,返回的是一个多层列索引的DataFrame。列名是(Open, 600519.SS)、(Close, 600519.SS)这种形式。嗯,这里要注意,后续处理时需要把列拍平。
3.3 数据清洗——别让脏数据毁了你的模型
拿到的数据很少是完美的。我遇到过的情况包括:
- 停牌日数据缺失(NaN)
- 节假日不同步(美股和A股休市时间不一样)
- 除权除息导致价格跳空
- 数据源偶尔返回错误值
咱们一步步处理。
3.3.1 处理缺失值
最简单的做法是直接删除缺失行。但配对交易要求两只股票的时间戳完全对齐,所以不能简单dropna——你得先对齐。
# 只保留收盘价
close_prices = data['Close'].copy()
# 删除所有包含NaN的行(两只股票必须同时有数据)
clean_prices = close_prices.dropna()
print(f"原始数据行数: {len(close_prices)}")
print(f"清洗后行数: {len(clean_prices)}")
3.3.2 检查异常值
有时候数据源会返回离谱的值,比如某天价格突然涨了1000%。这种情况多半是数据错误。
# 计算日收益率
returns = clean_prices.pct_change()
# 找出收益率绝对值超过20%的日期
outliers = returns[returns.abs() > 0.2].dropna()
print(f"异常值数量: {len(outliers)}")
# 如果发现异常值,建议人工核实
if len(outliers) > 0:
print("异常日期:")
print(outliers)
3.4 数据对齐——配对交易的核心步骤
配对交易的本质是看两只股票的价格差是否稳定。如果时间戳不对齐,价差计算就是错的。
对齐其实就一句话:取两只股票都交易的日子。上面用dropna已经做到了。但这里有个细节——你用的是收盘价还是调整收盘价?
3.4.1 价格的选择:Adj Close vs Close
yfinance默认提供两列:Close(收盘价)和Adj Close(调整收盘价)。
我个人习惯用Adj Close。为什么?因为调整收盘价已经考虑了分红、拆股、送股等因素。如果你用原始收盘价,遇到除权除息日,价格会突然跳空,价差序列就会出现假信号。
# 推荐使用调整收盘价
adj_close = data['Adj Close'].copy()
clean_adj = adj_close.dropna()
# 对比一下
print("原始收盘价最后5行:")
print(data['Close'].tail())
print("\n调整收盘价最后5行:")
print(data['Adj Close'].tail())
3.5 收益率 vs 价格——到底用哪个?
这个问题在配对交易里经常被问到。我的答案是:看情况。
| 指标 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 价格 | 协整检验、价差交易 | 直观,直接反映价差 | 非平稳,需要检验 |
| 收益率 | 相关性分析、风险度量 | 平稳,统计性质好 | 丢失了价格水平信息 |
说白了,如果你做协整检验,必须用价格序列(因为协整本身就是研究非平稳序列的线性组合是否平稳)。如果你只是看两只股票的相关性,用收益率更合适。
我自己的流程是:
- 先用价格做协整检验,找到配对
- 再用收益率计算相关系数,确认相关性够高
- 最后用价格计算价差,制定交易信号
3.6 完整的数据预处理流程
把上面所有步骤串起来,写成一个函数:
def prepare_pair_data(ticker1, ticker2, start, end):
"""
获取并清洗配对交易数据
"""
# 1. 下载数据
data = yf.download([ticker1, ticker2], start=start, end=end)
# 2. 提取调整收盘价
prices = data['Adj Close'].copy()
# 3. 对齐时间戳(删除缺失值)
prices = prices.dropna()
# 4. 重命名列
prices.columns = [ticker1, ticker2]
# 5. 计算收益率
returns = prices.pct_change().dropna()
return prices, returns
# 使用示例
prices, returns = prepare_pair_data("600519.SS", "000858.SZ",
"2020-01-01", "2023-12-31")
print(prices.head())
print(returns.head())
3.7 本章知识体系
下面这张图总结了数据获取与预处理的完整流程:
这张图把整个流程串起来了。你从yfinance拿数据,然后清洗、对齐,最后根据你的目的选择价格还是收益率。记住:对齐是前提,价格是核心。
好了,数据准备这块就聊到这儿。数据干净了,后面做协整检验才有意义。下一章咱们正式进入协整的世界——看看怎么用统计方法找到真正能配对的股票。