第一章:金融数据获取与预处理
做量化投资,第一步就是搞数据。没有数据,再牛的模型也是空中楼阁。我个人习惯用 yfinance 来拿股票历史数据,这玩意儿免费、方便,对A股和美股都支持得不错。今天咱们就聊聊怎么把数据拿到手,洗干净,然后算出日收益率。
1.1 为什么数据清洗这么重要?
你想想看,从交易所拿到的原始数据,那叫一个「脏」。缺失值、停牌、除权除息……各种坑。我曾经在做一个波动率模型时,因为没处理停牌日的数据,结果回测曲线漂亮得不像话,实盘直接翻车。嗯,从那以后,我每次拿到数据第一件事就是检查有没有坑。
数据清洗说白了就三件事:
- 处理缺失值:停牌日、节假日的数据是NaN,得填上或者删掉
- 对齐时间轴:多只股票的交易日期必须一致
- 检查异常值:比如某天涨了1000%,那肯定是数据出错了
核心原则:宁可少用数据,也不要用错数据。一个错误的数据点,可能让你的整个模型崩盘。
1.2 用yfinance获取数据
先装包:
pip install yfinance pandas numpy
然后写代码。我习惯把获取和清洗封装成一个函数,这样后面30个章节都能复用。
import yfinance as yf
import pandas as pd
import numpy as np
def fetch_stock_data(tickers, start_date, end_date):
"""
获取多只股票的历史数据
tickers: 股票代码列表,比如 ['AAPL', 'MSFT', 'GOOGL']
start_date, end_date: 'YYYY-MM-DD' 格式
"""
data = yf.download(tickers, start=start_date, end=end_date, auto_adjust=True)
# auto_adjust=True 会自动处理除权除息,省心不少
return data['Close'] # 我们只需要收盘价
小技巧:auto_adjust=True 这个参数我强烈建议打开。它会把历史价格统一调整为复权价,避免除权除息造成的价格跳空。我在做回测时吃过这个亏,后来就一直开着。
1.3 数据清洗实战
拿到数据后,第一件事就是看有没有缺失值。我一般这么干:
def clean_data(df):
"""
清洗数据:处理缺失值、对齐时间轴
"""
# 检查缺失比例
missing_ratio = df.isnull().sum() / len(df)
print("各股票缺失比例:")
print(missing_ratio)
# 删除缺失超过5%的股票
valid_cols = missing_ratio[missing_ratio < 0.05].index
df = df[valid_cols]
# 前向填充(用前一天的价格填充停牌日)
df = df.fillna(method='ffill')
# 删除仍然有缺失的行(比如最开始的几天)
df = df.dropna()
return df
为什么会用前向填充?因为停牌日虽然没交易,但你的持仓还在,用前一天的价格是合理的。我曾经试过用插值法,结果把停牌期间的波动给平滑掉了,模型完全失真。
1.4 计算日收益率
收益率是量化分析的基石。我们通常用对数收益率,因为它有可加性,而且更符合正态分布假设。
def calculate_returns(df):
"""
计算日收益率(对数收益率)
"""
# 对数收益率:ln(P_t / P_{t-1})
returns = np.log(df / df.shift(1))
# 删除第一行(NaN)
returns = returns.dropna()
return returns
注意:千万别用简单收益率(P_t / P_{t-1} - 1)做时间序列分析。对数收益率在时间上可加,而简单收益率不行。比如两天各涨10%,简单收益率算出来是21%,但对数收益率是19.5%,后者才是正确的复合收益率。
1.5 完整流程与可视化
把上面三步串起来,就是咱们第一章的核心流程。我画了个图,方便你理解:
1.6 完整代码示例
把上面所有函数整合起来,就是咱们第一章的完整代码:
import yfinance as yf
import pandas as pd
import numpy as np
def get_clean_returns(tickers, start, end):
"""
一站式获取并清洗数据,返回日收益率
"""
# 1. 获取数据
raw = yf.download(tickers, start=start, end=end, auto_adjust=True)
prices = raw['Close']
# 2. 清洗
prices = prices.dropna(axis=1, thresh=len(prices)*0.95) # 删除缺失过多的股票
prices = prices.fillna(method='ffill').dropna()
# 3. 计算收益率
returns = np.log(prices / prices.shift(1)).dropna()
return returns
# 使用示例
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META']
returns = get_clean_returns(tickers, '2020-01-01', '2024-12-31')
print(returns.head())
避坑指南:我曾经在获取A股数据时,发现 yfinance 对某些股票代码格式有要求。比如平安银行是 000001.SZ,不是 000001。多了一个后缀,数据就全乱了。所以,用之前先确认代码格式。
1.7 本章小结
咱们这一章干了三件事:拿数据、洗数据、算收益率。听起来简单,但每一步都有坑。数据清洗占了我整个量化项目至少30%的时间,真的不夸张。你想想看,如果数据是错的,后面那些漂亮的波动率模型、优化算法,全都是在垃圾上跳舞。
下一章咱们会基于这些干净的收益率数据,开始构建波动率控制组合。但在此之前,我建议你把今天这章代码跑一遍,看看自己拿到的数据长什么样。动手试试,比看十遍都管用。