3、数据获取与准备:数据源选择(A股/美股)、Pandas基础、数据清洗与对齐、复权价格处理
做量化投资,有一句老话叫「垃圾进,垃圾出」。你策略再牛,模型再炫,数据源出了问题,一切都是白搭。我个人习惯把数据获取和清洗看作整个因子投资流程的「地基工程」——地基没打牢,后面盖的楼越高,塌得越快。
这一章,我们就来聊聊这个地基怎么打。我会从数据源的选择讲起,然后带你过一遍 Pandas 的基础操作,最后重点说说数据清洗、对齐和复权处理。嗯,这些都是我踩过无数坑之后总结出来的经验。
3.1 数据源选择:A股 vs 美股
选数据源,说白了就是选「食材」。你要做川菜,得买辣椒;你要做粤菜,得买鲜虾。A股和美股的数据特点完全不同,我分别说说。
A股数据源
- Wind(万得):国内机构标配,数据全,但贵。个人用户基本用不起。
- 聚宽(JoinQuant):回测平台自带数据,免费版够用,但导出有限制。
- Tushare:开源社区维护,免费但需要积分,数据质量参差不齐。
- AKShare:完全免费,接口丰富,适合个人研究。我最近比较喜欢用这个。
美股数据源
- Yahoo Finance(yfinance):免费,接口简单,适合快速获取。但数据有时会缺失,尤其是分红、拆股信息。
- Alpha Vantage:免费但有调用次数限制,一天500次,个人够用。
- Quandl:数据质量高,但很多数据集要付费。
- IB(盈透证券)API:实时数据,适合高频交易。我一般用这个做实盘。
你想想看,选数据源其实就是在「成本」和「质量」之间做权衡。我个人建议:初期研究用免费源(AKShare 或 yfinance),等策略成熟了再上付费数据。
3.2 Pandas 基础:你每天都要用的工具
Pandas 是 Python 量化投资的「瑞士军刀」。你几乎所有的数据操作都离不开它。我不打算讲全部 API,只挑几个最常用的说。
核心数据结构
- Series:一维数组,带标签。可以理解为 Excel 里的一列。
- DataFrame:二维表格,带行索引和列索引。这就是你每天打交道的「数据表」。
常用操作
import pandas as pd
import numpy as np
# 读取 CSV 文件
df = pd.read_csv('stock_data.csv', parse_dates=['date'], index_col='date')
# 查看前5行
print(df.head())
# 查看数据基本信息
print(df.info())
# 缺失值处理
df = df.dropna() # 删除缺失行
# 或者用前向填充
df = df.fillna(method='ffill')
# 选择列
close = df['close']
# 条件筛选
high_volume = df[df['volume'] > 1e7]
# 分组操作
grouped = df.groupby('stock_code').apply(lambda x: x.sort_index())
df.info() 是我最常用的调试命令——一眼就能看出数据有没有缺失、类型对不对。
3.3 数据清洗与对齐:别让脏数据毁了你的策略
数据清洗,说白了就是「去伪存真」。我从多个数据源拿到的数据,格式、频率、时间范围都不一样,必须对齐才能用。
常见的数据问题
- 缺失值:停牌日、节假日、数据源本身缺失。
- 异常值:价格突然跳变、成交量异常放大。
- 重复值:同一时间戳出现多条记录。
- 时间戳不对齐:A股和美股交易时间不同,跨市场策略尤其要注意。
清洗流程
# 1. 删除重复行
df = df.drop_duplicates()
# 2. 处理缺失值
# 对于价格数据,我一般用前向填充
df['close'] = df['close'].fillna(method='ffill')
# 对于成交量,如果缺失就填0
df['volume'] = df['volume'].fillna(0)
# 3. 检测异常值(3-sigma 方法)
mean = df['close'].mean()
std = df['close'].std()
df = df[(df['close'] > mean - 3*std) & (df['close'] < mean + 3*std)]
# 4. 对齐多个股票的时间索引
# 假设我们有两只股票的数据
stock_a = pd.read_csv('stock_a.csv', index_col='date', parse_dates=True)
stock_b = pd.read_csv('stock_b.csv', index_col='date', parse_dates=True)
# 取两个时间索引的交集
common_dates = stock_a.index.intersection(stock_b.index)
stock_a_aligned = stock_a.loc[common_dates]
stock_b_aligned = stock_b.loc[common_dates]
df.duplicated().sum() 检查重复值。
3.4 复权价格处理:还原真实的收益率
复权价格,是量化投资里最容易出错的地方之一。为什么?因为股票会分红、送股、拆股,这些事件会导致价格出现「虚假」的跳变。如果你直接用原始价格计算收益率,结果会严重失真。
三种价格类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
| 不复权 | 原始交易价格 | 技术分析、K线形态识别 |
| 前复权 | 调整历史价格,使当前价格不变 | 回测、因子计算(最常用) |
| 后复权 | 调整当前价格,使历史价格不变 | 长期趋势分析 |
我个人习惯用前复权数据做因子计算。为什么呢?因为前复权保证了「当前价格是真实的」,而历史价格被调整后,计算出来的收益率才是真实的。
复权计算逻辑
# 假设我们有一只股票的分红和拆股信息
# 复权因子 = (1 + 分红率) * 拆股比例
# 前复权价格 = 原始价格 * 复权因子
def calculate_forward_adjusted_price(price, dividend, split_ratio):
"""
计算前复权价格
price: 原始价格序列
dividend: 每股分红
split_ratio: 拆股比例(如 1:2 拆股,split_ratio=2)
"""
# 计算复权因子
adj_factor = (1 + dividend / price.shift(1)) * split_ratio
adj_factor = adj_factor.fillna(1)
# 累积复权因子
cum_adj_factor = adj_factor.cumprod()
# 前复权价格
forward_adj_price = price * cum_adj_factor
return forward_adj_price
# 使用示例
price = pd.Series([100, 102, 98, 105, 110])
dividend = pd.Series([0, 0, 2, 0, 0]) # 第3天分红2元
split_ratio = pd.Series([1, 1, 1, 2, 1]) # 第4天1:2拆股
adj_price = calculate_forward_adjusted_price(price, dividend, split_ratio)
print(adj_price)
yfinance.download(..., auto_adjust=True) 参数,它会自动返回调整后的价格。
3.5 本章知识体系
下面这张图,是我梳理的本章核心逻辑。你可以把它当作一个「数据准备路线图」。
这张图展示了从数据源选择到最终生成可用数据的完整流程。每一步都有坑,但只要你按照这个路线图走,基本不会出大问题。
- 数据源选择要权衡成本和需求,初期用免费源,后期上付费数据。
- Pandas 是数据操作的基础,重点掌握 DataFrame 的读取、筛选、分组和对齐。
- 数据清洗要处理缺失值、异常值、重复值和时间对齐。
- 复权价格处理是计算真实收益率的关键,前复权最常用。
好了,这一章的内容就到这里。数据准备好了,下一章我们就可以开始真正地计算因子了。记住,数据质量决定了你的策略能走多远——别在这个环节偷懒。