数据获取与清洗:金融数据源与预处理实战
做量化交易,第一步就是搞数据。这事儿听起来简单,但我在项目里踩过的坑,十个手指头都数不过来。数据质量不过关,后面策略再牛也是白搭。今天咱们就聊聊数据获取和清洗那些事儿。
一、主流金融数据源怎么选?
市面上数据源不少,我挑三个最常用的说说。每个都有脾气,得顺着来。
| 数据源 | 特点 | 适合场景 | 我踩过的坑 |
|---|---|---|---|
| Yahoo Finance | 免费、全球股票/ETF/指数 | 回测、研究、美股为主 | 数据偶尔断更,节假日处理不统一 |
| Wind | 收费、国内最全、机构级 | 实盘、A股/期货/债券 | 接口文档写得像天书,第一次用差点崩溃 |
| Tushare | 免费+付费、社区活跃 | 个人研究、A股/基金 | 积分限制多,高频调用会被限流 |
Yahoo Finance 实战
我个人习惯用 yfinance 库,一行代码就能拉数据。但要注意,它返回的是调整后的价格,不是原始数据。
import yfinance as yf
# 获取贵州茅台数据
maotai = yf.download('600519.SS', start='2023-01-01', end='2023-12-31')
print(maotai.head())
小技巧: Yahoo Finance 对 A 股代码要加后缀,上海是 .SS,深圳是 .SZ。我刚开始不知道,查了半天没数据,后来才发现是代码写错了。
Wind 实战
Wind 在机构里是标配。接口叫 w.wsd,参数多到让人头大。我建议你先把常用参数记下来,别想着一次全记住。
from WindPy import w
w.start()
# 获取日线数据
data = w.wsd("000300.SH", "close,volume", "2023-01-01", "2023-12-31")
print(data.Data)
注意: Wind 需要先启动客户端,否则连不上。我吃过这个亏,代码跑半天报错,结果发现 Wind 没打开。
Tushare 实战
Tushare 对个人开发者很友好。注册后拿到 token,就能调数据了。不过免费版有积分限制,一天拉太多会被限流。
import tushare as ts
# 设置 token
ts.set_token('你的token')
pro = ts.pro_api()
# 获取日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231')
print(df.head())
二、数据清洗:脏数据怎么处理?
数据拉下来,别急着用。先看看有没有脏数据。我见过最离谱的一次,某只股票某天收盘价是 0.01 元,明显是录入错误。
缺失值处理
金融数据里,缺失值很常见。停牌、节假日、数据源问题都会导致缺失。处理方法有三种:
- 直接删除: 缺失不多时,直接 dropna()。简单粗暴,但会损失样本。
- 向前填充: 用上一个交易日的数据填充。适合停牌场景,因为停牌期间价格不变。
- 插值法: 用前后数据做线性插值。适合短时间缺失,比如日内数据。
import pandas as pd
# 向前填充(推荐用于股票数据)
df['close'].fillna(method='ffill', inplace=True)
# 线性插值(适合日内数据)
df['close'].interpolate(method='linear', inplace=True)
核心原则: 股票数据用向前填充,期货数据用插值法。为什么?因为股票停牌期间价格不变,期货没有停牌概念。
异常值处理
异常值怎么发现?我一般用两种方法:
- 3σ 原则: 超出均值±3倍标准差的数据,视为异常。
- IQR 方法: 超出 Q1-1.5IQR 或 Q3+1.5IQR 的数据,视为异常。
# 3σ 方法
mean = df['close'].mean()
std = df['close'].std()
df = df[(df['close'] > mean - 3*std) & (df['close'] < mean + 3*std)]
# IQR 方法
Q1 = df['close'].quantile(0.25)
Q3 = df['close'].quantile(0.75)
IQR = Q3 - Q1
df = df[(df['close'] > Q1 - 1.5*IQR) & (df['close'] < Q3 + 1.5*IQR)]
注意: 别一看到异常值就删。我曾经把某只股票涨停的数据当异常删了,结果回测结果完全不对。先看看是不是正常波动。
复权处理
复权是个大坑。股票分红、送股后,价格会跳空。不复权的话,技术指标全乱套。我建议用后复权做回测,前复权做分析。
- 前复权: 调整历史价格,让价格连续。适合看历史走势。
- 后复权: 调整当前价格,让收益计算准确。适合回测。
# 使用 yfinance 获取复权数据
df = yf.download('600519.SS', auto_adjust=True) # 自动复权
# 手动复权(以 Tushare 为例)
# Tushare 提供复权因子,自己算
df['adj_close'] = df['close'] * df['adj_factor']
三、数据对齐与频率转换
做多资产策略时,不同股票的交易时间不一样。A 股 9:30 开盘,港股 9:00 开盘。不对齐的话,回测结果会失真。
数据对齐
我常用的方法是按日期对齐,用 merge 或 join。注意用外连接,别漏了数据。
# 对齐两只股票的数据
stock1 = yf.download('600519.SS', start='2023-01-01', end='2023-12-31')
stock2 = yf.download('000858.SZ', start='2023-01-01', end='2023-12-31')
# 按日期对齐
aligned = pd.merge(stock1['Close'], stock2['Close'],
left_index=True, right_index=True, how='outer')
aligned.dropna(inplace=True) # 删除缺失日期
频率转换
有时候拿到的数据是日线,但策略需要周线或月线。用 resample 就能搞定。
# 日线转周线
weekly = df['close'].resample('W').last() # 取每周最后一个交易日
# 日线转月线
monthly = df['close'].resample('M').last() # 取每月最后一个交易日
# 日线转15分钟线(需要先有分钟数据)
min15 = df['close'].resample('15T').ohlc() # 生成OHLC数据
经验之谈: 频率转换时,注意用
last() 还是 mean()。回测用 last(),分析用 mean()。别搞混了。
四、知识体系总览
下面这张图,是我做数据清洗时脑子里过的流程。你照着走,基本不会出大问题。
这张图把整个流程串起来了。从数据源选择开始,到最终拿到干净数据。每一步都有坑,但按这个流程走,能省不少事。
总结一下: 数据获取与清洗,占了我量化项目 60% 的时间。别嫌麻烦,数据干净了,策略才能跑得稳。我见过太多人,策略写得漂亮,结果数据有问题,回测结果全是假的。
好了,数据这块就聊到这儿。记住一句话:垃圾进,垃圾出。数据质量决定策略上限,别在这上面偷懒。