4、数据获取与预处理:从零开始搭建你的数据管道

做统计套利,说白了就是跟数据打交道。你策略再牛,模型再花哨,数据一塌糊涂,结果就是白忙活。我见过太多人一上来就调参、跑回测,结果发现数据里全是坑——缺失值、异常点、时间戳对不齐……嗯,今天我们就从最基础的数据获取和预处理讲起。

4.1 用 yfinance 获取历史数据

我个人习惯用 yfinance 来拉数据。它免费、轻量,而且接口设计得很人性化。你想想看,几行代码就能拿到股票、ETF 甚至加密货币的历史行情,多方便。

先装包:

pip install yfinance

然后拉数据:

import yfinance as yf

# 获取两只股票的历史数据
tickers = ["AAPL", "MSFT"]
data = yf.download(tickers, start="2020-01-01", end="2023-12-31")
print(data.head())

这里有个细节要注意:yf.download() 返回的是 MultiIndex DataFrame,列名是 (Price, Ticker) 的形式。比如 ("Close", "AAPL") 表示苹果的收盘价。我个人习惯把它拆成单层索引,方便后续操作:

# 提取收盘价
close_prices = data["Close"]
print(close_prices.head())
小技巧: 如果你只需要收盘价,可以直接传 yf.download(tickers, start="...", end="...", auto_adjust=True),这样会自动调整复权价格,省去手动计算的麻烦。

4.2 数据清洗:缺失值与异常值处理

数据到手后,第一件事就是检查有没有缺失值。我在项目中遇到过好几次,因为某只股票停牌或者数据源问题,导致某几天的价格是 NaN。如果不处理,后面计算收益率、协方差矩阵时就会报错。

先看看缺失情况:

# 检查缺失值
print(close_prices.isnull().sum())

# 删除包含缺失值的行
clean_data = close_prices.dropna()

# 或者用前向填充(适合停牌情况)
filled_data = close_prices.fillna(method='ffill')

异常值处理更考验经验。我记得有一次,某只股票突然出现一个 -9999 的价格,明显是数据录入错误。怎么发现?用 Z-score 或者 IQR 方法:

import numpy as np

# 用 Z-score 检测异常值
z_scores = np.abs((clean_data - clean_data.mean()) / clean_data.std())
outliers = (z_scores > 3).any(axis=1)
print(f"发现 {outliers.sum()} 个异常日期")
避坑指南: 我曾经直接删除了异常值所在的行,结果导致时间序列不连续,后面做滞后计算时全乱了。建议用中位数或前后均值替换异常值,而不是直接删除。

4.3 重采样与对齐

做配对交易,两只股票的时间戳必须完全对齐。你想想看,如果 A 股票有 2023-01-01 的数据,而 B 股票没有,那这一天就没法用。更麻烦的是,不同交易所的节假日不同,比如美股和港股,休市日期不一样。

重采样可以解决频率不一致的问题:

# 将日数据重采样为周数据
weekly_data = clean_data.resample('W').last()

# 对齐:只保留两只股票都有的日期
aligned_data = clean_data.dropna()

这里有个坑:resample('W') 默认以周日为结束,但实际交易周是周一到周五。我建议用 resample('W-FRI') 来对齐到周五:

weekly_data = clean_data.resample('W-FRI').last()

4.4 数据可视化基础

光看数字不够直观。我习惯先画个图,看看两只股票的价格走势是否相似,有没有明显的背离。用 matplotlib 几行代码搞定:

import matplotlib.pyplot as plt

# 绘制价格走势
plt.figure(figsize=(12, 6))
plt.plot(aligned_data.index, aligned_data["AAPL"], label="AAPL")
plt.plot(aligned_data.index, aligned_data["MSFT"], label="MSFT")
plt.title("AAPL vs MSFT 价格走势")
plt.xlabel("日期")
plt.ylabel("价格")
plt.legend()
plt.grid(True)
plt.show()

还可以画价差图,看看两者是否长期保持稳定关系:

# 计算价差(假设已经做过标准化)
spread = aligned_data["AAPL"] - aligned_data["MSFT"]
plt.figure(figsize=(12, 4))
plt.plot(spread.index, spread, color='blue')
plt.axhline(y=spread.mean(), color='red', linestyle='--', label='均值')
plt.title("价差序列")
plt.legend()
plt.show()
核心要点: 数据预处理占整个量化项目 70% 的工作量。别嫌麻烦,把这一步做扎实了,后面的建模和回测才能站得住脚。

4.5 本章知识体系

下面这张图帮你理清数据预处理的完整流程:

数据获取与预处理流程 1. 数据获取 yfinance.download() 2. 数据清洗 缺失值/异常值处理 3. 重采样与对齐 resample() / dropna() 4. 数据可视化 matplotlib 绘图 常见问题与避坑 • 停牌导致缺失值 → 前向填充 • 异常价格 → Z-score 检测 • 节假日不对齐 → 重采样 数据预处理是统计套利的基础,花 70% 的时间在这里都不为过

嗯,数据预处理这块就讲到这里。记住,干净的、对齐的数据是配对交易成功的基石。别急着跑策略,先把数据管道搭好。


公众号:蓝海数据掘金营,微信 deep3321