数据获取与清洗:金融数据源与预处理实战
做量化研究,第一步就是搞数据。这活儿看着简单,其实坑特别多。我见过不少新手,策略写得花里胡哨,结果数据源没选对,或者清洗不到位,回测曲线漂亮得不行,实盘直接翻车。今天咱们就把这块彻底捋清楚。
核心观点:数据质量决定了策略的天花板。再牛的模型,喂进去垃圾数据,出来的只能是垃圾结果。
金融数据源:三大主流选择
目前国内做量化,常用的数据源就三个:Tushare、Wind、Yahoo Finance。我个人的习惯是,根据场景选工具,不盲目追求“大而全”。
| 数据源 | 特点 | 适用场景 | 费用 |
|---|---|---|---|
| Tushare | 国内数据全,更新快,接口友好 | A股、期货、基金研究 | 部分免费,高级功能需积分 |
| Wind | 机构级,数据质量最高 | 专业机构、高频研究 | 年费较高 |
| Yahoo Finance | 免费,全球市场覆盖 | 美股、国际品种研究 | 免费 |
先说说Tushare。这是国内量化圈用得最多的开源数据平台。我个人特别喜欢它的pro版本,接口设计得很Pythonic。举个例子,获取贵州茅台的历史日线数据,几行代码就搞定:
import tushare as ts
# 设置token(需要去官网注册获取)
ts.set_token('your_token_here')
pro = ts.pro_api()
# 获取贵州茅台日线数据
df = pro.daily(ts_code='600519.SH', start_date='20230101', end_date='20231231')
print(df.head())
嗯,这里要注意。Tushare的token一定要保管好,别硬编码在代码里。我习惯用环境变量或者配置文件来管理。
再来看Wind。说实话,Wind的数据质量确实是最好的。我在机构做项目时,但凡涉及正式报告,必须用Wind的数据。但它的接口是C++风格的,用起来没那么顺手。不过Wind Python接口(WindPy)已经改善很多了:
from WindPy import w
w.start()
# 获取贵州茅台日线数据
data = w.wsd("600519.SH", "open,high,low,close,volume", "2023-01-01", "2023-12-31")
print(data.Data)
至于Yahoo Finance,适合做美股研究。用yfinance库就能轻松获取数据。不过要注意,Yahoo的数据偶尔会有延迟或者缺失,特别是小盘股。我曾经遇到过某只美股的数据突然断了一周,排查了半天才发现是Yahoo那边的问题。
import yfinance as yf
# 获取苹果公司数据
aapl = yf.download('AAPL', start='2023-01-01', end='2023-12-31')
print(aapl.head())
我的建议:初学者先用Tushare练手,免费且文档齐全。等做到实盘级别了,再考虑上Wind。Yahoo Finance适合做全球配置研究,但别完全依赖它。
数据清洗与预处理:避坑指南
数据拿到手,别急着用。清洗这步跳不过去。我刚开始做量化时,就吃过这个亏。回测跑得飞起,结果发现数据里有大量缺失值和异常值,策略信号全是假的。
缺失值处理
金融数据缺失很常见。停牌、节假日、数据源故障都可能导致缺失。处理方式有三种:
- 直接删除:适用于缺失比例很小的情况(比如<1%)
- 向前填充:用上一个有效值填充,适合价格数据
- 插值法:线性插值或时间加权插值,适合连续型指标
我个人习惯用向前填充处理价格数据。为什么?因为交易逻辑上,停牌期间的价格就是上一个交易日的收盘价。举个例子:
import pandas as pd
# 假设df是日线数据,包含缺失值
df['close'].fillna(method='ffill', inplace=True)
# 或者用插值法处理成交量
df['volume'].interpolate(method='linear', inplace=True)
注意:千万别对收益率数据做向前填充!收益率缺失意味着那天没有交易,应该用0填充或者直接删除。我曾经见过有人把停牌期间的收益率用前一天的数据填充,结果夏普比率算出来高得离谱。
异常值检测
异常值比缺失值更隐蔽。常见的检测方法有:
- 3σ原则:超出均值±3倍标准差的数据视为异常
- IQR方法:超出Q1-1.5*IQR或Q3+1.5*IQR的数据视为异常
- 百分比截尾:直接剔除前后1%的数据
我比较推荐IQR方法,因为它不受极端值影响。比如检测涨跌幅异常:
# 计算日收益率
df['return'] = df['close'].pct_change()
# IQR方法检测异常
Q1 = df['return'].quantile(0.25)
Q3 = df['return'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 标记异常值
df['outlier'] = (df['return'] < lower_bound) | (df['return'] > upper_bound)
复权处理
这个坑特别大。很多新手直接用不复权的价格数据做回测,结果策略信号完全被分红送股扭曲了。说白了,不复权的价格有缺口,技术指标会失真。
复权分为两种:
- 前复权:调整历史价格,使价格连续。适合回测使用
- 后复权:调整当前价格,适合分析真实收益
Tushare直接提供了复权因子,用起来很方便:
# 获取复权因子
adj_factor = pro.adj_factor(ts_code='600519.SH')
# 计算前复权价格
df['adj_close'] = df['close'] * df['adj_factor'] / adj_factor['adj_factor'].iloc[-1]
避坑指南:我曾经在回测中用了后复权数据,结果策略表现特别好。后来发现是因为后复权价格包含了分红收益,而我的策略逻辑根本没考虑分红。嗯,从那以后我统一用前复权做回测。
数据对齐与频率转换
做多因子模型时,不同数据源的频率可能不一样。有的数据是日频,有的是周频,还有的是分钟频。不对齐就乱套了。
数据对齐的核心原则是:以最低频率为准。比如你要做日频策略,那就把所有数据都对齐到日频。高频数据降频时,注意聚合方式:
- 价格类数据:取最后一个值
- 成交量类数据:求和
- 收益率类数据:累乘
Pandas的resample方法特别好用:
# 将分钟数据降频为日数据
daily_df = minute_df.resample('D').agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'
})
频率转换时还有个容易忽略的问题:不同市场的交易日历不一样。A股有春节、国庆长假,美股有感恩节、圣诞节。如果你同时做A股和美股,必须对齐交易日历。我一般会维护一个统一的交易日历表,把两个市场的共同交易日筛选出来。
# 假设有A股和美股两个DataFrame
common_dates = a_share_dates.intersection(us_dates)
a_share_aligned = a_share.loc[common_dates]
us_aligned = us_stock.loc[common_dates]
总结一下:数据获取与清洗是量化研究的基石。选对数据源,做好缺失值、异常值、复权处理,再对齐频率,后面的因子分析和策略生成才能站得住脚。别嫌麻烦,这一步省下的时间,后面会加倍还回来。
这张图把整个流程串起来了。从数据源选择开始,经过清洗预处理,再到对齐转换,最后得到可用的因子数据。每一步都有坑,但踩过去之后,你的策略就有了坚实的基础。