跳转到内容

清洗与异常值

异常值可能是错误(价格多一个零、小数点错位),也可能是真实的极端事件(熔断日、闪崩、退市清算)。把前者修掉是清洗,把后者修掉是删数据——而极端事件恰恰是风险度量最需要的数据。分层的处理策略由此而来。

确定性错误有明确特征,用规则识别:

  • 价格 ≤0\le 0、成交量 <0< 0;
  • 当日价格变动超过涨跌停/熔断机制允许的范围(如 A 股单日 ±10% 之外,见A 股:T+1、涨跌停与融券);
  • 最高价 < 最低价、开盘价偏离上一收盘超过机制允许的范围;
  • 与相邻交易日的价格水平跳变超过若干倍日波动率。

识别出错误后用前值或邻日收盘替换,并记录替换日志。

第二层:真实极端值,用稳健统计

Section titled “第二层:真实极端值,用稳健统计”

无法归为错误的极端值,保留数据、限制其影响:

  • 缩尾(winsorize):把超过分位点的值截到分位点值,通常取 1%/99% 或 3 倍 MAD——保留观测但压低杠杆;
  • MAD 标准化:中位数绝对偏差对极端值稳健,MAD=median(∣x−median(x)∣)\mathrm{MAD} = \mathrm{median}(|x - \mathrm{median}(x)|),稳健 z 分数 =0.6745 x/MAD= 0.6745\,x / \mathrm{MAD};
  • 截面标准化前先缩尾:z 分数对极端值极敏感,一个错位价格能污染整个截面的标准化结果。
cleaning.py
import numpy as np
import pandas as pd
def winsorize_zscore(x: pd.Series, n_mad: float = 3.0) -> pd.Series:
"""稳健缩尾 + 标准化:先按 MAD 截尾,再标准化"""
med = x.median()
mad = (x - med).abs().median() * 1.4826
upper, lower = med + n_mad * mad, med - n_mad * mad
clipped = x.clip(lower, upper)
return (clipped - clipped.mean()) / clipped.std()

清洗结果的纪律:

  • 原始列只读,清洗结果写到新列或新表;
  • 每一步清洗(替换、缩尾、剔除)都记录日志与参数,保证可复现,见复现性:版本、种子与环境;
  • 回测报告能追溯到"这一天的价格被替换过"。
  • 错误识别用规则(阈值、机制边界),极端值处理用稳健统计;
  • 缩尾参数(分位点、MAD 倍数)写入配置并固定,不逐次调;
  • 横截面因子计算前,先缩尾再标准化。
  • 缩尾(winsorize)比截尾(trim)更常用,通常取 1%/99% 或 3 倍 MAD
  • 横截面标准化前先缩尾
  • 永远保留原始列,清洗结果写到新列