4、因子工程基础:因子定义、因子标准化、因子去极值、因子正交化

做策略的朋友都知道,因子是策略的灵魂。但说实话,很多人把因子想得太玄乎了。我刚开始做量化那会儿,也以为因子是什么高深莫测的数学公式。后来踩了不少坑才明白——因子其实就是你对市场某个特征的量化描述。

今天咱们就把因子工程这四步走一遍:定义、标准化、去极值、正交化。每一步我都会结合实战经验来讲,保证你听完就能用。

4.1 因子定义:把想法变成数字

因子定义,说白了就是回答一个问题:你想用什么样的数据,来描述市场的什么特征?

举个例子。你觉得“这只股票最近涨得猛”,这是个想法。但怎么量化?你可以用“过去5天的涨幅”,也可以用“过去20天的涨幅”。这就是因子定义。

我个人习惯把因子分成三类:

  • 价格因子:比如动量、反转、波动率
  • 基本面因子:比如市盈率、市净率、ROE
  • 另类因子:比如舆情情绪、资金流向

这里有个坑,我踩过好几次——因子定义要清晰、可复现。你不能说“我觉得这只股票人气高”,你得说“过去5天换手率的平均值”。

核心原则:因子定义必须满足“三可”——可量化、可计算、可回测。

来看一个简单的因子定义代码:

# 定义一个动量因子:过去5天收盘价的平均涨幅
def momentum_factor(df, window=5):
    df['momentum'] = df['close'].pct_change(window)
    return df

嗯,就这么简单。但别小看这一步,因子定义的好坏,直接决定了后面所有工作的质量。

4.2 因子标准化:让不同因子站在同一起跑线

你想想看,市盈率可能是几倍到几百倍,而换手率可能是0.1%到10%。这两个因子放在一起,量纲都不一样,怎么比较?

标准化就是解决这个问题的。我常用的方法有两种:

方法 公式 适用场景
Z-score标准化 (x - μ) / σ 因子近似正态分布时
Min-Max标准化 (x - min) / (max - min) 因子有明确边界时

我在项目中遇到过一个问题:用Z-score标准化时,如果因子有极端值,标准化后的结果会被严重扭曲。所以后来我养成了一个习惯——先做去极值,再做标准化。顺序很重要。

我的建议:做市策略中,我一般用Z-score标准化。因为它保留了因子之间的相对关系,对后续的多因子融合更友好。

# Z-score标准化
def zscore_standardize(df, factor_col):
    mean = df[factor_col].mean()
    std = df[factor_col].std()
    df[factor_col + '_zscore'] = (df[factor_col] - mean) / std
    return df

4.3 因子去极值:别让异常值毁了你的策略

为什么会需要去极值?我给你讲个真实案例。

有一次我在做回测,发现某个因子在某个时间点突然变得特别“有效”。我兴奋了半天,结果一查——原来是那天有一只股票因为乌龙指,价格瞬间跌了90%,导致因子值变成了一个巨大的负数。这个异常值,让整个回测结果都失真了。

去极值的方法,我常用这三种:

  • MAD法:基于中位数和绝对偏差,稳健性最好
  • 百分位法:直接截断上下1%或5%
  • 标准差法:超过3倍标准差的值被截断

我个人最推荐MAD法。为什么?因为它不受极端值本身的影响。你想想看,如果用标准差法,极端值会把标准差拉大,然后极端值反而变得“不极端”了——这是个悖论。

注意:去极值不是删除数据,而是把极端值“拉”回到一个合理的范围内。我曾经见过有人直接删除异常值,结果导致样本量不均匀,回测结果偏差很大。

# MAD法去极值
def mad_winsorize(df, factor_col, n=5):
    median = df[factor_col].median()
    mad = (df[factor_col] - median).abs().median()
    upper = median + n * mad
    lower = median - n * mad
    df[factor_col] = df[factor_col].clip(lower, upper)
    return df

4.4 因子正交化:消除因子间的“共线性”

这一步,很多人会忽略。但我告诉你,这一步做不好,你的多因子模型就是“一锅粥”。

什么叫共线性?简单说就是两个因子长得太像了。比如“过去5天涨幅”和“过去10天涨幅”,它们之间肯定高度相关。如果你把这两个因子都放进模型,它们会互相“抢功劳”,导致模型不稳定。

正交化的目的,就是让每个因子都“独立”出来,互不干扰。

我常用的方法是施密特正交化。它的核心思想是:先选一个基准因子,然后把其他因子中与基准因子相关的部分“剔除”掉。

核心逻辑:正交化后的因子,两两之间的相关系数为0。这样每个因子贡献的都是“增量信息”。

# 施密特正交化(简化版)
import numpy as np

def schmidt_orthogonalize(factors):
    # factors: 因子矩阵,每列是一个因子
    n = factors.shape[1]
    ortho = np.zeros_like(factors)
    ortho[:, 0] = factors[:, 0]
    
    for i in range(1, n):
        proj = np.zeros_like(factors[:, 0])
        for j in range(i):
            coef = np.dot(factors[:, i], ortho[:, j]) / np.dot(ortho[:, j], ortho[:, j])
            proj += coef * ortho[:, j]
        ortho[:, i] = factors[:, i] - proj
    
    return ortho

嗯,代码看起来有点数学味,但实际用起来很简单。你只需要记住:正交化之后,因子之间的相关性被消除了,但每个因子的“个性”保留了下来

4.5 本章知识体系

为了让你更直观地理解这四步的关系,我画了一张流程图:

因子工程四步流程 因子定义 把想法变成数字 去极值 剔除异常值 标准化 统一量纲 正交化 消除共线性 关键要点 • 因子定义要清晰、可复现 • 去极值推荐用MAD法,稳健性最好 • 标准化和去极值的顺序:先去极值,再标准化 • 正交化让每个因子贡献“增量信息”

这张图把四步流程串起来了。你从左边开始,一步步往右走。每一步都有它的意义,缺一不可。

4.6 实战中的一些体会

最后,我想分享几点实战中的体会:

  • 因子不是越多越好。我见过有人搞了上百个因子,结果正交化之后发现大部分都是冗余的。我的建议是:先精挑细选10-20个有逻辑支撑的因子,再慢慢扩展。
  • 去极值的参数要调。MAD法中的n值,我一般设5。但不同市场、不同因子,最优参数可能不一样。建议做一下参数敏感性分析。
  • 正交化不是万能的。它只能消除线性相关性,非线性相关性它管不了。如果你发现正交化后因子之间还有明显的非线性关系,那可能需要考虑更复杂的处理方法。

一个小技巧:做完因子工程后,建议画一张因子相关性热力图。如果正交化后大部分相关系数接近0,说明你的正交化做得不错。如果还有明显的相关性,那就得回头检查一下了。

好了,因子工程的基础就讲到这里。这四步是后续多因子信号融合的基石,每一步都值得你花时间去打磨。记住:好的因子工程,能让你的策略事半功倍;差的因子工程,再好的模型也救不了


公众号:蓝海资料掘金营,微信deep3321