第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个核心特征,效果反而更好。

常用的特征选择方法:

  1. 方差过滤:去掉方差太小的特征,这些特征基本没信息量
  2. 相关性分析:去掉高度相关的特征,避免多重共线性
  3. 特征重要性:用随机森林或XGBoost评估特征重要性
  4. 递归特征消除:逐步删除最不重要的特征
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%,那反而会引入噪声。

知识体系总览

下面这张图,是我整理的数据预处理与特征工程的核心流程。你可以把它当作一个检查清单,每次做项目时对照着来。

数据预处理与特征工程核心流程 原始数据 数据清洗 缺失值处理 标准化与归一化 特征选择 降维 高质量特征数据

这张图展示了数据从原始状态到高质量特征数据的完整流程。每一步都环环相扣,跳过任何一步都可能影响最终模型的效果。

总结一下:数据预处理不是可有可无的步骤,而是量化交易模型的基石。我见过太多人急着调参、换模型,却忽略了数据质量。记住一句话:好的数据胜过好的模型。

专注资料整理