数据获取与清洗:打好因子分析的地基
做量化投资,有一句话我特别认同:垃圾进,垃圾出。你因子模型再漂亮,回测框架再炫酷,数据一塌糊涂,结果就是自欺欺人。
今天咱们就聊聊数据获取与清洗。这部分活儿看着不起眼,但我在项目里吃过不少亏。有一次,就因为某只股票停牌后数据没处理好,整个多因子组合的回测结果都偏了。嗯,从那以后,我对数据清洗再也不敢马虎。
1. 用pandas-datareader拉数据
我个人习惯用 pandas-datareader 来获取股票数据。它支持多个数据源,比如雅虎财经、FRED等。不过要注意,雅虎财经的接口偶尔会抽风,我建议你备个本地数据源。
import pandas as pd
import pandas_datareader.data as web
import datetime
# 设置时间范围
start = datetime.datetime(2020, 1, 1)
end = datetime.datetime(2023, 12, 31)
# 获取股票数据
ticker = 'AAPL'
df = web.DataReader(ticker, 'yahoo', start, end)
print(df.head())
你看,代码很简单。但实际项目中,你可能会同时拉几百只股票。这时候我建议用循环加异常处理,防止某只股票数据缺失导致整个程序崩溃。
time.sleep(1) 避免被服务器封IP。
2. 处理缺失值:别让空值毁了你的因子
数据拉下来后,第一件事就是检查缺失值。你想想看,如果某天某只股票停牌了,那天的价格就是NaN。直接扔掉?不一定。
# 检查缺失值
print(df.isnull().sum())
# 前向填充:用前一天的数据填充
df.fillna(method='ffill', inplace=True)
# 后向填充:用后一天的数据填充(一般用于开头缺失)
df.fillna(method='bfill', inplace=True)
为什么用前向填充?因为股票数据有连续性。今天停牌,明天复牌,价格通常跟前一天差不多。但要注意,如果连续停牌好几天,前向填充可能会失真。我遇到过一只股票停牌一个月,复牌后直接跳空,前向填充的数据完全不能用。
3. 去极值处理:MAD方法
因子数据里经常有极端值。比如某只股票突然暴涨,市盈率变成几千倍。这种数据如果不处理,会严重扭曲你的因子组合。
我常用的方法是MAD(Median Absolute Deviation)。它比标准差更稳健,不容易被极端值带偏。
import numpy as np
def mad_detection(series, n=3):
"""
MAD去极值方法
n: 阈值倍数,默认3倍
"""
median = series.median()
mad = np.median(np.abs(series - median))
# 计算MAD的修正系数
modified_z_scores = 0.6745 * (series - median) / mad
# 标记异常值
outliers = np.abs(modified_z_scores) > n
return outliers
# 示例:对市盈率因子去极值
pe_ratio = df['Close'] / df['Close'].shift(1) # 假设这是市盈率
outliers = mad_detection(pe_ratio.dropna())
# 将异常值替换为中位数
pe_ratio_clean = pe_ratio.copy()
pe_ratio_clean[outliers] = pe_ratio.median()
这里有个细节:MAD的阈值n怎么选?我一般用3倍。但如果你发现数据噪声很大,可以放宽到4倍。反之,如果因子本身很稳定,2.5倍也行。这个得根据实际情况调。
4. 数据清洗的整体流程
说了这么多,咱们把整个流程串起来。我画了个图,方便你理解。
这个流程看着简单,但每一步都有坑。我建议你每次处理完数据后,都做个简单的可视化检查。比如画个分布图,看看有没有异常值没处理干净。
5. 实战中的注意事项
最后,分享几个我在实战中总结的经验:
- 数据频率要统一:日频数据就用日频,别混入周频或月频。我见过有人把日频和月频数据混在一起算因子,结果相关性全乱了。
- 注意复权处理:股票有分红送股,价格会变化。我建议用后复权数据,这样历史价格才有可比性。
- 保存原始数据:清洗前先备份一份原始数据。万一清洗逻辑出问题,还能回滚。这个习惯救过我很多次。
- 批量处理时加日志:处理几百只股票时,加个日志记录每只股票的处理情况。哪只股票数据有问题,一目了然。
好了,数据获取与清洗这部分就聊到这儿。记住,数据是量化投资的基石。你花在清洗上的每一分钟,都会在后续的因子分析和回测中得到回报。