数据获取与清洗:数据源选择、字段定义与预处理
做量化投资,第一步就是搞数据。没有数据,再牛的策略也是空中楼阁。
我个人习惯把数据获取和清洗看作整个系统的地基。地基不稳,后面全白搭。今天咱们就聊聊这块硬骨头怎么啃。
数据源选择:三大主流方案
市面上数据源不少,但真正适合个人投资者的,我重点推荐三个:Yahoo Finance、Tushare、AKShare。每个我都用过,各有各的脾气。
| 数据源 | 特点 | 适用场景 | 我的评价 |
|---|---|---|---|
| Yahoo Finance | 免费、全球市场、API稳定 | 美股、全球指数 | 老牌选手,但A股数据不全 |
| Tushare | 国内专业、数据全面、需积分 | A股、基金、期货 | 我最常用的,积分制有点烦 |
| AKShare | 开源免费、接口丰富、更新快 | A股、宏观、另类数据 | 后起之秀,社区活跃 |
我的建议:新手先用AKShare入门,零成本。做A股策略,Tushare是首选。做全球配置,Yahoo Finance绕不开。
数据字段定义:别小看这一步
拿到原始数据后,第一件事不是分析,而是搞清楚每个字段的含义。我在项目中遇到过,有人把复权因子当成了价格,回测结果漂亮得不行,实盘直接亏哭。
标准字段定义如下:
- open:开盘价,当天第一笔成交价
- high:最高价,当天最高成交价
- low:最低价,当天最低成交价
- close:收盘价,当天最后一笔成交价
- volume:成交量,当天成交的总股数
- amount:成交额,当天成交的总金额
- adj_factor:复权因子,用于计算复权价格
注意:不同数据源对字段命名可能不同。比如Yahoo Finance用"Adj Close"表示复权收盘价,Tushare用"adj_factor"加"close"计算。一定要先看文档。
缺失值处理:数据里的坑
现实中的数据,很少是完美的。停牌、节假日、数据源故障,都会导致缺失值。
我常用的处理方法:
- 向前填充(ffill):用上一个有效值填充。适合价格数据,因为停牌期间价格不变。
- 向后填充(bfill):用下一个有效值填充。适合某些指标数据。
- 插值法:线性插值或多项式插值。适合连续变化的数据。
- 直接删除:如果缺失比例很小,直接删掉那几行。
# 我常用的缺失值处理代码
import pandas as pd
# 向前填充
df['close'].fillna(method='ffill', inplace=True)
# 线性插值
df['volume'].interpolate(method='linear', inplace=True)
# 删除缺失行
df.dropna(subset=['open', 'high', 'low', 'close'], inplace=True)
避坑指南:我曾经在回测中用了向前填充处理成交量数据,结果停牌日成交量被填成了前一天的数值,导致策略误以为有交易。后来我改成用0填充成交量,才解决问题。
异常值检测:揪出捣乱的数据
数据里偶尔会有离谱的值。比如某只股票突然涨了1000%,或者成交量突然变成0。这些异常值会严重干扰策略。
我常用的检测方法:
- 3σ原则:超出均值±3倍标准差的值视为异常
- IQR方法:超出Q1-1.5*IQR或Q3+1.5*IQR的值视为异常
- 固定阈值:比如涨跌幅超过20%视为异常
# 用IQR方法检测异常值
Q1 = df['close'].quantile(0.25)
Q3 = df['close'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
anomalies = df[(df['close'] < lower_bound) | (df['close'] > upper_bound)]
print(f"发现 {len(anomalies)} 个异常值")
注意:不要机械地删除异常值。有些异常可能是真实的市场事件,比如分红、拆股。先检查原因,再决定怎么处理。
数据对齐:让不同数据步调一致
不同数据源、不同频率的数据,时间戳可能不一致。比如日线数据有的用"2024-01-01",有的用"2024/01/01"。更麻烦的是,不同股票的交易日期可能不同。
数据对齐的核心原则:
- 统一时间格式:全部转为datetime类型
- 统一频率:日线、周线、月线要明确
- 对齐交易日:用所有股票的共同交易日作为基准
# 数据对齐示例
# 假设有两只股票的数据,日期不完全一致
stock_a = pd.DataFrame({'date': ['2024-01-01', '2024-01-02', '2024-01-04'],
'close': [100, 101, 102]})
stock_b = pd.DataFrame({'date': ['2024-01-01', '2024-01-03', '2024-01-04'],
'close': [200, 201, 202]})
# 合并并对齐
merged = pd.merge(stock_a, stock_b, on='date', how='inner')
print(merged)
我的经验:对齐时用inner join(内连接)还是outer join(外连接),取决于你的策略。如果策略需要连续数据,用outer join然后填充缺失值。如果策略只关心共同交易日,用inner join更干净。
知识体系总览
下面这张图,是我整理的数据获取与清洗核心流程。你想想看,每一步都环环相扣。
嗯,到这里,数据获取与清洗的核心内容就讲完了。说白了,这一步就是给策略打好基础。数据干净了,后面的分析才能放心做。
总结一下:数据源选对,字段定义清,缺失值处理好,异常值揪出来,数据对齐准。这五步走完,你的数据就准备好了。