3. 数据清洗与预处理:缺失值处理、异常值检测、时间对齐、复权处理

各位,咱们直接进入正题。

量化交易里有一句老话:「垃圾进,垃圾出」。你策略再牛,模型再花哨,原始数据一塌糊涂,结果就是白忙活。我见过太多人,花几周调参,最后发现是数据没洗干净——那种感觉,就像你跑完马拉松发现鞋里全是沙子。

数据清洗与预处理,说白了就是给原始数据「洗澡」。洗掉脏东西,对齐时间,处理异常。今天咱们就聊聊这四件事:缺失值、异常值、时间对齐、复权处理。

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

行情数据、财务数据,很少有完美的。停牌、节假日、数据源抽风,都会产生缺失值。我个人习惯,拿到数据第一件事就是跑个 isnull().sum(),看看哪些列在「偷懒」。

核心原则: 缺失值处理没有银弹。取决于你的策略类型和缺失比例。

常见处理方法

  • 直接删除:缺失比例小于5%,且随机分布。简单粗暴,但小心丢失信息。
  • 向前填充(ffill):用上一个非空值填充。适合分钟级行情,比如某分钟没成交,用上一笔价格。
  • 向后填充(bfill):用下一个非空值填充。适合收盘后补数据。
  • 插值法:线性插值、多项式插值。适合连续型数据,比如收益率曲线。
  • 模型预测:用其他特征预测缺失值。适合财务数据,比如用营收预测净利润。
# 我个人常用的快速处理
import pandas as pd

# 向前填充,限制最多填充3个连续缺失
df['close'].fillna(method='ffill', limit=3, inplace=True)

# 如果连续缺失超过3个,直接删除
df = df[df['close'].notna()]
避坑指南: 我曾经在回测时用了全局均值填充,结果策略在震荡市表现完美,一到趋势行情就崩。为什么?因为均值填充抹平了波动,信号全失真了。记住:填充方式会影响分布

3.2 异常值检测:揪出「捣乱分子」

异常值,就是那些明显偏离正常范围的数据点。比如某股票突然涨了1000倍,或者成交量少了个零。你想想看,这种数据要是喂给模型,它不学坏才怪。

我常用的三种检测方法

方法 适用场景 缺点
3σ原则 正态分布的数据 对极端值敏感,容易误判
箱线图(IQR) 非正态分布,稳健 对尾部数据不敏感
孤立森林 高维数据,非线性 计算量大,需要调参
# 箱线图法,我最爱用,因为稳健
def detect_outliers_iqr(data, column):
    Q1 = data[column].quantile(0.25)
    Q3 = data[column].quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    return data[(data[column] < lower) | (data[column] > upper)]

# 找到异常后,我一般先标记,不急着删
df['is_outlier'] = detect_outliers_iqr(df, 'volume')
注意: 异常值不一定是错误。比如财报发布前的异常放量,可能是内幕交易,也可能是正常预期。我建议先标记,再结合业务逻辑判断。别一棍子打死。

3.3 时间对齐:让不同频率的数据「同步」

量化交易里,数据源往往是多频率的。日线行情、分钟级交易、季度财报、每秒的舆情数据……它们的时间戳不一样。时间对齐,就是把这些数据拉到同一个时间轴上。

嗯,这里要注意:对齐方向决定了信息泄露风险

  • 向前对齐(past对齐):用过去的数据填充当前。比如用昨天的收盘价作为今天的特征。安全,不会引入未来信息。
  • 向后对齐(future对齐):用未来的数据填充当前。比如用明天的收益率作为今天的标签。回测时容易「偷看未来」,要小心。
# 将分钟数据对齐到日线
# 用每日最后一笔数据作为当日代表
daily_data = minute_data.resample('D').last()

# 将财务数据对齐到交易日
# 财报发布后,下一个交易日才可用
financial_data.index = financial_data.index + pd.Timedelta(days=1)
df = df.join(financial_data, how='left')
我的习惯: 对齐时,我总会在代码里加一个 shift(1),确保只用历史数据。哪怕回测时少了一天数据,也比未来函数强。你想想看,实盘时你能拿到明天的数据吗?

3.4 复权处理:还原真实的交易逻辑

股票会分红、送股、配股。这些事件会导致价格出现「断崖」或「跳空」。如果不复权,你的技术指标全是错的。

说白了,复权就是消除这些事件的影响,让价格连续。

前复权 vs 后复权

类型 原理 适用场景
前复权 调整历史价格,保持当前价格不变 回测、技术分析(看历史形态)
后复权 调整当前价格,保持历史价格不变 计算真实收益率、长期持有分析
重要: 回测时一定要用前复权数据。因为你的策略是在「当前」时刻做决策,历史价格应该反映当时的真实成本。
# 使用akshare获取前复权数据
import akshare as ak

# 获取前复权日线
df = ak.stock_zh_a_hist(symbol="000001", 
                        period="daily", 
                        start_date="20200101", 
                        end_date="20231231", 
                        adjust="qfq")  # qfq = 前复权

# 检查复权效果
# 如果复权后价格出现负数,说明除权除息太频繁,需要重新处理
if df['close'].min() < 0:
    print("警告:复权后出现负价格,请检查数据源")
避坑指南: 我曾经用后复权数据做回测,结果策略在分红日附近表现异常。为什么?因为后复权会改变当前价格,导致信号计算错误。记住:回测用前复权,分析用后复权

知识体系总览

下面这张图,是我自己梳理的数据清洗流程。每次做新策略前,我都会对着它走一遍。

数据清洗与预处理流程 原始数据 缺失值处理(删除/填充/插值) 异常值检测(3σ/IQR/孤立森林) 时间对齐(向前/向后/重采样) 复权处理(前复权/后复权)

最后说一句:数据清洗没有「标准答案」。每个市场、每个品种、每个策略,都有自己的脾气。我的建议是——先理解数据,再动手清洗。别上来就一顿操作,结果把有用的信息也洗掉了。

总结: 缺失值要「对症下药」,异常值要「先标记后处理」,时间对齐要「防未来函数」,复权要「前复权回测」。这四步走完,你的数据才算「能打」。

专注资料整理