4. 因子构建与处理:从原始数据到有效信号

好,咱们进入第四章。这一章,说白了就是量化选股的「炼金术」——怎么把一堆原始数据,变成能赚钱的信号。

我个人习惯把因子处理看作「食材准备」。你想想看,再好的厨师,拿到发臭的鱼肉也做不出好菜。因子处理也是一样,原始数据里全是噪音、缺失值、极端值,不处理干净,后面模型再牛也白搭。

4.1 因子定义与分类

先说说因子是什么。因子就是能解释股票收益差异的特征。我刚开始做量化时,以为因子越多越好,堆了上百个因子进去,结果过拟合得一塌糊涂。后来才明白,因子的质量远比数量重要

常见的因子分类,我一般分成四大类:

因子类别 典型因子 逻辑解释
估值因子 PE、PB、PS、PCF 便宜才是硬道理?低估值股票长期有超额收益
动量因子 过去1个月收益、过去12个月收益(剔除最近1个月) 强者恒强,还是均值回归?不同周期表现不同
质量因子 ROE、毛利率、资产负债率、盈利稳定性 好公司才能持续赚钱,但好公司不一定有好股价
ESG因子 E评分、S评分、G评分、ESG综合得分 负责任的公司,长期风险更低,但短期可能拖累收益

关键认知:没有哪个因子永远有效。估值因子在牛市跑不赢动量,质量因子在熊市更抗跌。ESG因子呢?我做过回测,它在市场下跌时确实能减少回撤,但上涨时弹性不足。

4.2 因子计算与标准化

因子计算这块,我踩过不少坑。举个例子,计算动量因子时,很多人直接用「过去12个月收益率」,但这样会引入「1个月反转效应」的干扰。我建议用12-1个月动量,即剔除最近1个月的数据。

# 计算动量因子(12-1个月动量)
import pandas as pd
import numpy as np

def calc_momentum(price_df, months=12, skip_months=1):
    """
    计算动量因子
    price_df: 日度价格数据,index为日期,columns为股票代码
    """
    # 计算月收益率
    monthly_ret = price_df.resample('M').last().pct_change()
    
    # 滚动求和,跳过最近skip_months个月
    momentum = monthly_ret.rolling(window=months).sum() - \
               monthly_ret.rolling(window=skip_months).sum()
    
    return momentum.shift(1)  # 避免未来信息

避坑指南:我曾经在计算因子时忘记shift(1),结果用到了当天的数据来预测当天的收益——这在回测里表现完美,实盘却一塌糊涂。记住:所有因子必须用历史数据计算,不能包含未来信息

标准化处理,我一般分三步走:

  1. 去极值:用MAD(中位数绝对偏差)或百分位法,把极端值拉回来。我习惯用3倍MAD。
  2. 缺失值处理:行业均值填充,或者直接剔除。如果某个因子缺失率超过30%,我建议直接放弃这个因子。
  3. 标准化:Z-score或Rank标准化。Rank标准化对异常值更鲁棒,我比较常用。
def standardize_factor(factor_series, method='rank'):
    """
    因子标准化
    method: 'zscore' 或 'rank'
    """
    if method == 'zscore':
        return (factor_series - factor_series.mean()) / factor_series.std()
    elif method == 'rank':
        # Rank标准化:将因子值排序后映射到[0,1]
        ranks = factor_series.rank()
        return (ranks - 1) / (len(ranks) - 1)

4.3 因子相关性分析

因子之间如果高度相关,就会带来多重共线性问题。我记得有一次,我同时用了PE和PB两个估值因子,结果模型训练出来,两个因子的系数一正一负,互相抵消——这就是典型的「因子拥挤」。

我一般会计算因子间的相关系数矩阵,设定一个阈值(比如0.7),超过这个阈值的因子只保留一个。

# 计算因子相关性矩阵
factor_corr = factor_df.corr()

# 找出高度相关的因子对
high_corr_pairs = []
for i in range(len(factor_corr.columns)):
    for j in range(i+1, len(factor_corr.columns)):
        if abs(factor_corr.iloc[i, j]) > 0.7:
            high_corr_pairs.append(
                (factor_corr.columns[i], factor_corr.columns[j], 
                 factor_corr.iloc[i, j])
            )

注意:相关性高不代表因子无效。比如ESG中的E和S评分,天然就有相关性(好公司通常各方面都好)。这时候可以用正交化处理,把共同部分剥离掉,保留各自独特的信息。

4.4 因子IC/IR分析

IC(信息系数)和IR(信息比率)是衡量因子有效性的核心指标。说白了,IC就是因子值和未来收益的相关系数,IR则是IC的均值除以标准差——衡量因子表现的稳定性。

我一般这样计算:

def calc_ic(factor_series, forward_return, method='spearman'):
    """
    计算IC值
    factor_series: 因子值
    forward_return: 未来N日收益
    method: 'spearman'(秩相关)或 'pearson'(线性相关)
    """
    if method == 'spearman':
        from scipy.stats import spearmanr
        ic, p_value = spearmanr(factor_series, forward_return)
    else:
        ic = factor_series.corr(forward_return)
    return ic

def calc_ir(ic_series):
    """
    计算IR值
    ic_series: 时间序列上的IC值
    """
    return ic_series.mean() / ic_series.std()

在实际项目中,我会按月计算IC,然后看IC的均值、标准差、正负比例。一个合格的因子,IC均值应该在0.02以上,IR大于0.5。如果IC经常正负交替,说明这个因子不稳定,需要谨慎使用。

我的经验:ESG因子的IC通常不高,可能在0.01-0.03之间,但它的IR往往不错——因为ESG因子的表现比较稳定,不会大起大落。所以ESG更适合作为「风控因子」而非「进攻因子」。

4.5 本章知识体系

下面这张图,是我梳理的因子构建与处理的核心流程。你可以把它当作操作手册:

因子构建与处理核心流程 原始数据 因子定义与分类 估值 | 动量 | 质量 | ESG 因子计算 去极值 → 缺失值处理 → 标准化 因子相关性分析 相关系数矩阵 | 多重共线性检测 IC/IR分析 因子有效性评估 | 稳定性检验 关键指标 • IC > 0.02 • IR > 0.5 • 因子相关性 < 0.7 • 缺失率 < 30% • 去极值:3倍MAD • 标准化:Rank优先 • 避免未来信息 • 正交化处理 • 行业中性化 • 市值中性化 • 滚动窗口计算

嗯,到这里,因子构建与处理的核心内容就讲完了。记住一句话:因子处理不是技术活,而是良心活——你偷的每一个懒,都会在回测结果里加倍还给你。

专注资料整理