4、数据获取与清洗:数据来源(Wind、Tushare、Yahoo Finance)、数据清洗流程、处理缺失值与异常值、数据对齐与重采样
做量化的人都知道一句话:垃圾进,垃圾出。模型再牛,数据不行,一切都是白搭。今天我们就来聊聊数据获取与清洗这档子事。我个人习惯把这一步叫做「做饭前的洗菜切菜」——虽然不显眼,但决定了最终菜品的生死。
4.1 数据来源:三驾马车
目前国内做多因子研究,主流的数据源就三个:Wind、Tushare、Yahoo Finance。我挨个说说它们的脾气。
4.1.1 Wind(万得)
Wind 是机构标配。数据全、质量高、更新快。但价格不菲,个人用户基本用不起。我在私募那会儿,公司每年交几十万的 Wind 终端费,就为了拿那几根 K 线。如果你在机构工作,直接用 Wind API 就好。代码示例:
from WindPy import w
w.start()
# 获取平安银行日行情
data = w.wsd("000001.SZ", "open,high,low,close,volume", "2024-01-01", "2024-12-31")
print(data)
4.1.2 Tushare(通联数据)
Tushare 是个人玩家的福音。免费版够用,Pro 版也不贵。数据覆盖 A 股、基金、期货、宏观等。我建议新手从 Tushare 入手。不过要注意,Tushare 的积分制度有点绕,有些数据需要 2000 积分以上才能调取。代码示例:
import tushare as ts
ts.set_token('你的token')
pro = ts.pro_api()
# 获取日线行情
df = pro.daily(ts_code='000001.SZ', start_date='20240101', end_date='20241231')
print(df.head())
4.1.3 Yahoo Finance
做美股或者全球资产配置,Yahoo Finance 是首选。免费、接口简单。但有个坑——Yahoo 的 API 经常被墙,国内访问不稳定。我曾经半夜爬起来跑数据,结果发现 Yahoo 又连不上了,气得我直接换了数据源。代码示例:
import yfinance as yf
# 获取苹果公司数据
aapl = yf.download('AAPL', start='2024-01-01', end='2024-12-31')
print(aapl.head())
4.2 数据清洗流程:从脏数据到干净数据
数据拿到手,第一件事不是建模,而是清洗。我见过太多人直接拿原始数据跑模型,结果回测曲线漂亮得像假的一样——其实就是假的,因为数据里有未来函数。
清洗流程我总结为五步:
- 格式统一:日期格式、列名、数据类型全部标准化
- 缺失值处理:该填充的填充,该删除的删除
- 异常值检测:剔除明显错误的数据点
- 数据对齐:不同频率的数据对齐到同一时间轴
- 重采样:按需调整数据频率
下面我们一个个展开说。
4.3 处理缺失值与异常值
4.3.1 缺失值处理
缺失值在金融数据里太常见了。停牌、节假日、数据源漏传,都会导致缺失。处理方式有三种:
- 删除法:直接删掉有缺失的行。简单粗暴,但会损失信息。
- 填充法:用前值、后值、均值、中位数填充。我个人习惯用前值填充(ffill),因为金融数据有连续性。
- 插值法:线性插值、多项式插值等。适合少量缺失。
代码示例:
import pandas as pd
import numpy as np
# 模拟缺失数据
df = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=10, freq='D'),
'close': [100, 102, np.nan, 105, 107, np.nan, 110, 112, 115, np.nan]
})
# 前值填充
df['close_ffill'] = df['close'].ffill()
# 线性插值
df['close_interp'] = df['close'].interpolate(method='linear')
print(df)
4.3.2 异常值处理
异常值通常来自数据录入错误、极端行情或者除权除息未调整。检测方法有:
- 3σ 原则:超出均值±3倍标准差的数据视为异常
- IQR 方法:超出 Q1-1.5*IQR 或 Q3+1.5*IQR 的数据视为异常
- 百分比截尾:直接剔除上下 1% 或 5% 的数据
代码示例:
# 3σ 方法检测异常值
mean = df['close'].mean()
std = df['close'].std()
df['is_outlier_3sigma'] = (np.abs(df['close'] - mean) > 3 * std)
# IQR 方法
Q1 = df['close'].quantile(0.25)
Q3 = df['close'].quantile(0.75)
IQR = Q3 - Q1
df['is_outlier_iqr'] = (df['close'] < Q1 - 1.5 * IQR) | (df['close'] > Q3 + 1.5 * IQR)
print(df[df['is_outlier_3sigma'] | df['is_outlier_iqr']])
4.4 数据对齐与重采样
4.4.1 数据对齐
多因子模型需要多个数据源的数据对齐到同一时间轴。比如,你同时用日频的行情数据和周频的财务数据,必须对齐到日频或者周频。对齐方法:
- 左连接:以某个时间序列为主表,其他数据左连接上去
- 重索引:统一索引后,用 reindex 方法对齐
代码示例:
# 假设有两个数据框
price_df = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=5, freq='D'),
'close': [100, 102, 104, 103, 105]
})
factor_df = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=3, freq='2D'),
'pe': [15, 16, 15.5]
})
# 以 price_df 为主表,左连接 factor_df
aligned_df = price_df.merge(factor_df, on='date', how='left')
print(aligned_df)
4.4.2 重采样
重采样就是把数据从一种频率转换到另一种频率。比如把日频数据转成周频、月频。常用方法:
- 降采样:高频转低频,用 mean、sum、ohlc 等聚合函数
- 升采样:低频转高频,用 ffill、bfill、interpolate 填充
代码示例:
# 日频转周频
df.set_index('date', inplace=True)
weekly_df = df['close'].resample('W').last() # 取每周最后一个交易日收盘价
print(weekly_df)
# 日频转月频
monthly_df = df['close'].resample('M').mean() # 取每月均值
print(monthly_df)
resample('W-FRI') 来指定周五为周的最后一天。
4.5 本章知识体系
下面我用一张 SVG 图来总结本章的核心逻辑。这张图展示了从数据获取到清洗完成的完整流程,你一看就明白。
嗯,这张图把整个流程串起来了。从三个数据源出发,经过清洗流程的五步,最终得到干净可用的因子数据。你想想看,如果这一步没做好,后面模型再花哨也是白搭。
- 数据源选择:A 股用 Tushare,美股用 Yahoo,机构用 Wind
- 缺失值处理:优先前值填充,避免使用未来数据
- 异常值检测:3σ 和 IQR 方法结合使用,先检查数据逻辑
- 数据对齐:以主时间序列为基准,左连接或重索引
- 重采样:注意交易日历,避免包含非交易日
好了,数据获取与清洗就讲到这里。记住一句话:数据干净了,模型就成功了一半。下一章我们开始讲因子构建,那才是真正有意思的部分。