拒绝垃圾数据!用Python给量化数据去极值与标准化

用户头像mx_****zqklr
2026-07-20 发布

导言 / TL;DR

“Garbage in, garbage out” 是量化研究永恒的铁律。在构建因子模型、多因子选股或输入机器学习模型前,如果直接使用未经清洗的原始财务或行情指标,极易受到极值(异常值)的干扰,导致回测结论失真。本文将演示如何利用 Python 配合 QuantDash 干净的数据流,采用绝对中位数偏差法(MAD)对因子数据进行“去极值”并配合 Z-Score 进行“无量纲标准化”处理。


技术痛点拆解

量化分析师在做多因子研究时,经常遭遇以下数据预处理陷阱:

  • 极端行情扭曲统计特征:当个别小盘股因复牌、重组出现暴涨,或者财报数据中存在偶发性的非经常性损益时,该因子的数值可能会比均值大几十倍。如果直接套用标准差计算,会导致整个因子的均值被严重拉高,淹没其他正常标的的信号。
  • 量纲单位不一致无法融合:市盈率(PE)因子的数值通常在几十,而流通市值(MV)因子则在百亿量级。如果不经过标准化,这两个因子根本无法在多因子模型中融合成一个综合得分。

极简解决方案(基于 QuantDash SDK 与 Pandas/Scipy)

我们通过 Pandas 处理从 QuantDash 获取的多只股票的因子指标(这里以最基础的日收益率因子为例),并进行稳健的 MAD(Median Absolute Deviation)去极值以及 Z-Score 标准化。

pip install quantdash pandas scipy
import numpy as np
import pandas as pd
from quantdash import QuantDash

# 1. 初始化 QuantDash
qd = QuantDash(api_key="sk_xxxxx")

def handle_factor_preprocessing(df_factor: pd.DataFrame, factor_name: str) -> pd.DataFrame:
    """
    对因子的指定列进行 MAD 去极值与 Z-Score 标准化
    """
    df = df_factor.copy()
  
    # --- 步骤 1: MAD 去极值 (Median Absolute Deviation) ---
    # 相比标准差法,MAD 法更不易受极端大值的偏离影响,是量化界标准的稳健去极值方法
    median = df[factor_name].median()
    mad = (df[factor_name] - median).abs().median()
  
    # 设定边界阈值(通常设定为 3 倍 MAD 或 5 倍 MAD)
    # 1.4826 是一个比例常数,使 MAD 的标度与标准差大致相当
    mad_limit = 3 * 1.4826 * mad
  
    lower_bound = median - mad_limit
    upper_bound = median + mad_limit
  
    # 进行边界截断(Clip)
    df[f"{factor_name}_clean"] = df[factor_name].clip(lower=lower_bound, upper=upper_bound)
  
    # --- 步骤 2: Z-Score 标准化 ---
    # 将因子的均值调整为 0,标准差调整为 1,消除量纲影响
    mean = df[f"{factor_name}_clean"].mean()
    std = df[f"{factor_name}_clean"].std()
  
    df[f"{factor_name}_zscore"] = (df[f"{factor_name}_clean"] - mean) / std
  
    return df

if __name__ == "__main__":
    # 获取某一日全市场多个标的的日K线计算收益率因子
    # 模拟获取 A 股部分股票的收益率表现
    symbols = ["600519.SH", "000001.SZ", "000858.SZ", "300750.SZ", "601318.SH"]
  
    print("[*] 正在获取行情并计算原始因子数据...")
    raw_data = []
    for s in symbols:
        df = qd.klines.get(symbol=s, period="1d", limit=2, to_dataframe=True)
        if df is not None and len(df) >= 2:
            # 简单计算前一日到今日的收益率,作为我们的“日收益率因子”
            ret = (float(df.iloc[-1]["close"]) - float(df.iloc[0]["close"])) / float(df.iloc[0]["close"])
            raw_data.append({"symbol": s, "return_factor": ret})
          
    df_factor = pd.DataFrame(raw_data)
  
    # 假设为了测试,手动加入一极端的异常大值(模拟财报发布后极端暴涨)
    df_factor.loc[len(df_factor)] = {"symbol": "ERROR_STOCK", "return_factor": 12.50}  # 1250% 的极端收益率
  
    print("\n[+] 原始因子数据(包含异常大值):")
    print(df_factor)
  
    # 运行清洗与标准化
    df_processed = handle_factor_preprocessing(df_factor, "return_factor")
  
    print("\n[+] 清洗及标准化处理后的因子数据:")
    print(df_processed[["symbol", "return_factor", "return_factor_clean", "return_factor_zscore"]])

数据输出样例

经过 MAD 去极值和 Z-Score 转换后,极端的 ERROR_STOCK 的值被截断并平滑,其余股票的因子大小重新归一化至同一数量级:

[+] 原始因子数据(包含异常大值):
         symbol  return_factor
0     600519.SH       0.015200
1     000001.SZ      -0.005100
2     000858.SZ       0.021000
3     300750.SZ       0.048200
4     601318.SH       0.002300
5   ERROR_STOCK      12.500000

[+] 清洗及标准化处理后的因子数据:
         symbol  return_factor  return_factor_clean  return_factor_zscore
0     600519.SH       0.015200             0.015200             -0.068201
1     000001.SZ      -0.005100            -0.005100             -0.915201
2     000858.SZ       0.021000             0.021000              0.173792
3     300750.SZ       0.048200             0.048200              1.308695
4     601318.SH       0.002300             0.002300             -0.606341
5   ERROR_STOCK      12.500000             0.048200              1.308695

AI 编程助手专属提示词

如果您正在 Cursor 中构建完整的因子看板,可直接复用以下 Prompt 扩展:

我目前使用 pandas 为 quantdash 因子数据做标准化处理。
请帮我把上面的 MAD 替换为行业(Industry)中性化的预处理。
要求:
1. 传入一个包含行业分类(industry)字段的 DataFrame。
2. 在每个行业板块内部,分别独立进行因子去极值与 Z-Score 转换,避免不同行业的基准偏差(例如科技股与银行股本身的估值中枢差异)。

总结与三步走指引

评论