第1章:数据预处理与特征工程
做量化交易这些年,我踩过最大的坑,就是数据没处理好就直接上模型。你想想看,垃圾数据进去,再牛的模型也是白搭。今天咱们就聊聊数据预处理和特征工程,这是整个量化交易模型的地基。
核心观点:数据预处理占整个量化项目60%以上的时间,但这是最值得投入的部分。
1.1 数据清洗——别让脏数据毁了你的模型
数据清洗,说白了就是给数据「洗澡」。我刚开始做量化时,从交易所拿到的分钟线数据,居然有重复的时间戳。嗯,这种坑我踩过不止一次。
常见的脏数据问题:
- 重复数据:同一时间戳出现多条记录
- 异常值:价格突然跳空、成交量异常放大
- 数据缺失:某些时间点没有交易数据
- 格式不一致:日期格式、小数位数不统一
我个人习惯用Pandas来处理,简单高效:
import pandas as pd
import numpy as np
# 加载数据
df = pd.read_csv('stock_data.csv', parse_dates=['timestamp'])
# 去除重复数据
df = df.drop_duplicates(subset=['timestamp'])
# 识别异常值(3倍标准差法)
def detect_outliers(series):
mean = series.mean()
std = series.std()
return (series < mean - 3*std) | (series > mean + 3*std)
# 标记异常值
df['is_outlier'] = detect_outliers(df['close'])
注意:我曾经在回测时发现收益曲线异常漂亮,后来才发现是数据里混入了未来数据。一定要检查时间戳是否严格递增。
1.2 缺失值处理——填还是不填?
缺失值处理是个技术活。我记得有个项目,因为简单填充了缺失值,导致模型在实盘时频繁报错。为什么会这样?因为填充方式选错了。
常见的处理方式:
| 方法 | 适用场景 | 我的建议 |
|---|---|---|
| 删除缺失行 | 缺失比例<5% | 简单粗暴,但会丢失信息 |
| 前向填充 | 时间序列数据 | 适合价格数据,用前一时刻的值 |
| 插值法 | 连续变化的数据 | 线性插值效果不错 |
| 模型预测 | 缺失比例较高 | 用KNN或回归模型填充 |
# 前向填充(我最常用的方法)
df['close'] = df['close'].fillna(method='ffill')
# 线性插值
df['volume'] = df['volume'].interpolate(method='linear')
# 用中位数填充(适合异常值较多的场景)
df['turnover'] = df['turnover'].fillna(df['turnover'].median())
小技巧:对于金融时间序列,我建议先用前向填充,再用后向填充处理开头的数据。这样能最大程度保留数据的时序特征。
1.3 标准化与归一化——让特征站在同一起跑线
标准化和归一化,很多人搞混。其实很简单:归一化是把数据缩放到[0,1]区间,标准化是让数据符合标准正态分布。
为什么需要做这个?你想想看,价格可能是几百块,成交量可能是几百万股,这两个特征直接丢进模型,模型会天然偏向数值大的特征。这显然不合理。
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# 标准化(Z-score)
scaler_std = StandardScaler()
df['close_std'] = scaler_std.fit_transform(df[['close']])
# 归一化(Min-Max)
scaler_mm = MinMaxScaler()
df['volume_norm'] = scaler_mm.fit_transform(df[['volume']])
我个人习惯:
- 做回归或分类任务时,用标准化
- 做神经网络时,用归一化
- 做聚类分析时,标准化更稳定
避坑指南:我曾经在回测时用了全量数据的均值和标准差做标准化,结果实盘时新数据超出范围,模型直接崩了。正确的做法是只用训练集计算参数,然后应用到测试集。
1.4 特征选择——少即是多
特征不是越多越好。我记得有个项目,我加了50多个技术指标,结果模型过拟合得一塌糊涂。后来砍到10个核心特征,效果反而更好。
常用的特征选择方法:
- 方差过滤:去掉方差太小的特征,这些特征基本没信息量
- 相关性分析:去掉高度相关的特征,避免多重共线性
- 特征重要性:用随机森林或XGBoost评估特征重要性
- 递归特征消除:逐步删除最不重要的特征
from sklearn.feature_selection import VarianceThreshold, SelectKBest
from sklearn.ensemble import RandomForestRegressor
# 方差过滤
selector = VarianceThreshold(threshold=0.01)
X_selected = selector.fit_transform(X)
# 随机森林特征重要性
rf = RandomForestRegressor()
rf.fit(X, y)
importance = pd.DataFrame({
'feature': X.columns,
'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)
1.5 降维——化繁为简的艺术
降维,说白了就是用更少的维度表达更多的信息。PCA(主成分分析)是我最常用的方法。
为什么要降维?
- 减少计算量,训练速度提升明显
- 去除噪声,提高模型泛化能力
- 可视化方便,可以画二维散点图看数据分布
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
# PCA降维到2维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# 查看解释方差比
print(f'解释方差比: {pca.explained_variance_ratio_}')
print(f'累计解释方差: {pca.explained_variance_ratio_.sum():.3f}')
我的经验:选择主成分数量时,看累计解释方差比达到80%-90%即可。不要追求100%,那反而会引入噪声。
知识体系总览
下面这张图,是我整理的数据预处理与特征工程的核心流程。你可以把它当作一个检查清单,每次做项目时对照着来。
这张图展示了数据从原始状态到高质量特征数据的完整流程。每一步都环环相扣,跳过任何一步都可能影响最终模型的效果。
总结一下:数据预处理不是可有可无的步骤,而是量化交易模型的基石。我见过太多人急着调参、换模型,却忽略了数据质量。记住一句话:好的数据胜过好的模型。