3. 数据获取与处理:让ETF数据乖乖听话
做量化投资,数据就是你的弹药库。没有数据,再好的策略也是空中楼阁。这一章,咱们就聊聊怎么把ETF的历史数据搞到手,再把它收拾得服服帖帖。
说实话,我刚开始做量化那会儿,花在数据清洗上的时间比写策略还多。你想想看,拿到的数据经常是缺胳膊少腿的,不处理干净,策略跑出来的结果你敢信吗?
核心观点:数据质量决定了策略的天花板。垃圾数据进,垃圾策略出。
3.1 获取ETF历史行情数据
获取数据的方式有很多,我挑几个最常用的说说。
3.1.1 从Tushare获取数据
Tushare是国内用得最多的金融数据接口之一。我个人习惯用它来拉日线数据,稳定且字段齐全。
import tushare as ts
# 设置token(需要去官网注册)
ts.set_token('你的token')
pro = ts.pro_api()
# 获取沪深300ETF(510300.SH)的历史日线数据
df = pro.fund_daily(
ts_code='510300.SH',
start_date='20200101',
end_date='20231231',
fields='trade_date,open,high,low,close,vol'
)
print(df.head())
这里要注意,Tushare的token需要去官网申请。我记得第一次用的时候,忘了设置token,结果一直报错,折腾了半天才发现问题。
3.1.2 从AKShare获取数据
AKShare是另一个好用的开源库,完全免费,数据源覆盖广。我比较喜欢它的ETF实时行情接口。
import akshare as ak
# 获取ETF实时行情
etf_spot = ak.fund_etf_spot_em()
print(etf_spot.head())
# 获取历史数据
etf_hist = ak.fund_etf_hist_em(
symbol="510300",
period="daily",
start_date="20200101",
end_date="20231231",
adjust="qfq" # 前复权
)
print(etf_hist.head())
小技巧:AKShare的接口名经常更新,建议定期查看官方文档。我一般每季度检查一次,避免接口失效影响策略运行。
3.1.3 从本地CSV文件读取
有时候数据源不稳定,我会把数据存到本地CSV。这样跑策略时不用每次都去拉数据,速度快很多。
import pandas as pd
# 从CSV读取数据
df = pd.read_csv('etf_510300.csv', parse_dates=['trade_date'])
print(df.info())
3.2 数据清洗与预处理
拿到原始数据后,第一件事就是清洗。我见过太多人直接拿原始数据跑策略,结果回测曲线漂亮得不行,实盘一跑就崩。为什么?数据里有坑啊。
3.2.1 处理缺失值
ETF数据偶尔会有缺失,比如停牌日。处理方式要看情况:
- 前向填充:用上一个交易日的数据填充,适合价格数据
- 删除缺失行:如果缺失不多,直接删掉
- 插值法:用前后数据做线性插值
# 前向填充
df['close'].fillna(method='ffill', inplace=True)
# 删除缺失行
df.dropna(subset=['close'], inplace=True)
# 线性插值
df['close'].interpolate(method='linear', inplace=True)
注意:千万不要用均值填充价格数据!我曾经犯过这个错,结果策略信号全乱了。价格序列有趋势性,均值填充会破坏这种结构。
3.2.2 处理异常值
异常值通常来自数据录入错误或极端行情。我一般用两种方法检测:
- 3σ原则:超过均值±3倍标准差的值视为异常
- IQR方法:超过Q1-1.5*IQR或Q3+1.5*IQR的值视为异常
# 用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
# 标记异常值
df['is_outlier'] = (df['close'] < lower_bound) | (df['close'] > upper_bound)
# 查看异常值
print(df[df['is_outlier']])
3.2.3 复权处理
ETF有分红、拆合,这些都会影响价格连续性。做量化必须用复权数据。
- 前复权:调整历史价格,保持当前价格不变
- 后复权:调整当前价格,保持历史价格不变
我个人习惯用前复权,因为这样计算收益率时更直观。AKShare的adjust="qfq"参数就是前复权。
3.3 数据可视化基础
数据可视化不是为了好看,是为了发现问题。一张图胜过千行数据,这话一点不假。
3.3.1 绘制K线图
K线图是ETF分析的基础。我用mplfinance库,简单好用。
import mplfinance as mpf
# 准备数据
df_plot = df.set_index('trade_date')
df_plot = df_plot[['open', 'high', 'low', 'close', 'vol']]
# 绘制K线图
mpf.plot(
df_plot,
type='candle',
volume=True,
style='charles',
title='沪深300ETF K线图',
ylabel='价格',
ylabel_lower='成交量'
)
3.3.2 绘制均线
均线是最常用的技术指标。我一般会叠加5日、20日、60日均线。
# 计算均线
df['ma5'] = df['close'].rolling(window=5).mean()
df['ma20'] = df['close'].rolling(window=20).mean()
df['ma60'] = df['close'].rolling(window=60).mean()
# 绘制
mpf.plot(
df_plot,
type='candle',
mav=(5, 20, 60),
volume=True,
style='charles',
title='沪深300ETF 均线图'
)
3.3.3 绘制收益率分布
了解收益率的分布特征,对风险管理很重要。
import matplotlib.pyplot as plt
# 计算日收益率
df['return'] = df['close'].pct_change()
# 绘制直方图
plt.figure(figsize=(10, 6))
plt.hist(df['return'].dropna(), bins=50, alpha=0.7, color='steelblue')
plt.title('日收益率分布')
plt.xlabel('收益率')
plt.ylabel('频次')
plt.grid(True, alpha=0.3)
plt.show()
3.4 常用数据源介绍
选数据源,说白了就是看三点:数据质量、更新频率、费用。我整理了一个对比表:
| 数据源 | 特点 | 费用 | 适用场景 |
|---|---|---|---|
| Tushare | 数据全面,接口稳定 | 部分免费,高级功能收费 | 专业量化研究 |
| AKShare | 完全免费,更新快 | 免费 | 个人学习和研究 |
| 东方财富 | 实时行情,数据量大 | 免费 | 实时监控 |
| Wind | 机构级数据,质量最高 | 收费(较贵) | 专业机构 |
| 本地CSV | 离线可用,速度快 | 免费 | 策略回测 |
我的建议:刚开始做量化,用AKShare就够了。等策略成熟了,再考虑Tushare或Wind。别一上来就花冤枉钱。
3.5 本章知识体系
下面这张图,把本章的核心逻辑串起来了。你一看就明白:
这张图把整个流程串起来了。从数据源开始,到获取数据,再到清洗处理,最后可视化展示。每一步都环环相扣,缺一不可。
好了,数据这块就聊到这儿。记住一句话:数据是你的朋友,不是你的敌人。花时间把数据处理好,后面的策略开发会顺畅很多。