数据预处理与特征工程:金融数据清洗、缺失值处理、标准化与归一化、技术指标特征构建

各位同学,咱们今天聊点实在的。做量化交易,很多人一上来就想着怎么调参数、怎么选模型。但我跟你说,真正决定你策略生死的,其实是数据预处理和特征工程这一关。我见过太多人,模型跑得飞起,结果一上实盘就崩,为什么?数据没洗干净。

说白了,金融数据就是一堆「脏活累活」。你想想看,行情数据里可能有停牌、有除权除息、有数据源断流,甚至还有交易所的乌龙指。这些不处理干净,模型学到的全是噪音。嗯,咱们今天就把这块硬骨头啃下来。

1. 金融数据清洗:把垃圾倒掉

数据清洗,我个人的习惯是「先看后洗」。拿到数据别急着写代码,先打开看一眼。用 df.info()df.describe() 扫一遍,心里有个数。

常见的脏数据有几种:

  • 重复数据:同一个时间戳出现两次,可能是数据源推送重复了。
  • 异常值:比如某只股票突然涨了1000%,大概率是除权没复权。
  • 时间戳错乱:非交易时间出现数据,或者时间顺序颠倒。

重点:金融数据的时间戳必须是严格递增的。我曾在项目中遇到过某数据源在收盘后还推送了「假数据」,导致策略在非交易时段开仓,亏了一笔冤枉钱。

清洗代码其实不复杂,但细节多。给你看个例子:

import pandas as pd
import numpy as np

# 假设 df 是原始行情数据
df = df.sort_values('timestamp')  # 先按时间排序
df = df.drop_duplicates(subset=['timestamp', 'symbol'])  # 去重
df = df[df['volume'] > 0]  # 剔除成交量为0的无效数据
df = df[df['close'] > 0]   # 剔除价格为0或负数的异常数据

小技巧:清洗时别用 inplace=True,我建议每次都生成新的 DataFrame。这样万一洗错了,还能回滚。

2. 缺失值处理:别让「空」坑了你

金融数据里缺失值太常见了。停牌、节假日、数据源断流,都会导致某段时间没有数据。怎么处理?

我一般分三种情况:

缺失比例 处理方法 适用场景
< 5% 前向填充(ffill)或插值 盘中短暂断流,用上一笔数据填充
5% - 20% 线性插值或中位数填充 日内数据有少量缺失,用前后值平均
> 20% 直接剔除该时间段或该特征 停牌期间,数据缺失太多,填充反而引入偏差

举个例子,停牌期间的数据怎么处理?我曾经犯过一个错:用前一天的收盘价填充停牌期间的所有数据。结果模型学到的规律全是「价格不变」,复牌后直接被打脸。

避坑指南:停牌数据不要用 ffill 填充超过3天。我建议直接标记为 NaN,然后在训练时用 mask 忽略掉。或者干脆把停牌期间的数据行删掉,别让模型「学空气」。

# 前向填充,最多填充3个缺失值
df['close'] = df['close'].fillna(method='ffill', limit=3)

# 超过3个缺失值,直接置为 NaN 并后续删除
df['close'] = df['close'].where(df['close'].notna() | (df['close'].isna().cumsum() <= 3))

3. 标准化与归一化:让数据「站在同一起跑线」

你想想看,茅台股价1800,工商银行股价5块。如果你直接把价格扔进模型,茅台的特征权重会被放大几百倍。模型会以为「价格高=重要」,这显然不对。

所以我们需要做标准化或归一化。说白了,就是把不同量纲的数据拉到同一个尺度上。

常用的方法就两种:

  • Min-Max 归一化:把数据缩放到 [0, 1] 区间。适合有明确上下界的特征,比如 RSI 指标。
  • Z-score 标准化:减去均值除以标准差,让数据符合标准正态分布。适合没有明确边界的特征,比如收益率。

我个人习惯:做回归或分类时用 Z-score,做神经网络时用 Min-Max。为什么?因为神经网络的激活函数(比如 sigmoid)在 0 附近梯度最大,归一化后训练更快。

from sklearn.preprocessing import StandardScaler, MinMaxScaler

# Z-score 标准化
scaler_std = StandardScaler()
df['return_z'] = scaler_std.fit_transform(df[['return']])

# Min-Max 归一化
scaler_mm = MinMaxScaler()
df['volume_norm'] = scaler_mm.fit_transform(df[['volume']])

注意:一定要先拆分训练集和测试集,再用训练集的参数去标准化测试集。千万别用全量数据 fit,否则会引入未来信息,造成「数据泄露」。我见过有人因为这个回测曲线漂亮得不像话,实盘直接崩了。

4. 技术指标特征构建:从价格中「挖」出规律

原始的价格和成交量,信息量其实很有限。真正有价值的是技术指标——它们是对原始数据的「二次加工」,能反映出趋势、动量、波动率等特征。

常用的技术指标特征,我列几个核心的:

指标类别 常用指标 构建逻辑
趋势类 MA、EMA、MACD 平滑价格,识别方向
动量类 RSI、KDJ、ROC 衡量涨跌速度,判断超买超卖
波动率类 ATR、布林带宽度 衡量价格波动幅度
成交量类 OBV、成交量加权均价 结合量价关系

构建特征时,我有个原则:不要堆砌指标。你想想看,把几十个指标全扔进去,模型会陷入「维度灾难」。我一般先选5-8个核心指标,然后做相关性分析,剔除高度相关的(比如 MA5 和 MA10 相关性通常很高,留一个就行)。

import talib

# 构建技术指标特征
df['ma5'] = talib.SMA(df['close'], timeperiod=5)
df['ma20'] = talib.SMA(df['close'], timeperiod=20)
df['rsi'] = talib.RSI(df['close'], timeperiod=14)
df['atr'] = talib.ATR(df['high'], df['low'], df['close'], timeperiod=14)

# 计算 MACD
df['macd'], df['macd_signal'], df['macd_hist'] = talib.MACD(
    df['close'], fastperiod=12, slowperiod=26, signalperiod=9
)

核心思路:特征构建不是越多越好,而是越「有效」越好。我习惯用「逻辑驱动」的方式:先想清楚我要捕捉什么市场规律(比如趋势跟随、均值回归),再针对性地构建特征。而不是先堆指标,再让模型自己找规律。

5. 知识体系总览

说了这么多,咱们用一张图把整个流程串起来。数据预处理和特征工程,本质上就是一条流水线:

数据预处理与特征工程流程 原始金融数据 数据清洗 去重·异常值·时间戳 缺失值处理 填充·插值·剔除 标准化/归一化 Z-score·Min-Max 技术指标特征构建 趋势·动量·波动率·成交量 特征筛选 相关性分析·降维 ✅ 干净可用的特征集

这张图就是咱们今天讲的核心流程。从原始数据开始,经过清洗、缺失值处理、标准化,再到特征构建和筛选,最终得到一份干净可用的特征集。每一步都环环相扣,少一步都不行。

好了,数据预处理和特征工程这块,咱们就聊到这儿。记住一句话:好的特征胜过复杂的模型。把数据洗干净、把特征做扎实,你的策略就已经赢了一半。


专注资料整理