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

因子计算与处理,说白了就是把原始行情数据变成能指导交易的信号。这一步做不好,后面回测再漂亮也是白搭。我见过太多人在这上面栽跟头——因子算出来回测曲线完美,一上实盘就崩。嗯,咱们今天就把这块彻底讲透。

核心逻辑:原始数据 → 因子定义 → 因子计算 → 标准化 → 中性化 → 有效性检验 → 共线性处理 → 可用因子

4.1 因子定义与计算

因子定义是第一步,也是最容易被忽视的一步。我个人习惯把因子分成三类:

  • 动量因子:过去N天的收益率、乖离率等
  • 基本面因子:PE、PB、ROE等财务指标
  • 量价因子:换手率、波动率、资金流等

举个例子,一个简单的动量因子计算:

def calc_momentum_factor(df, window=20):
    """
    计算动量因子:过去window天的累计收益率
    """
    df['momentum'] = df['close'].pct_change(window)
    return df

我在项目中遇到过一个问题:直接用pct_change算出来的因子,在停牌股票上会出bug。所以实际使用时,我会先做复权处理,再剔除停牌日的数据。

我的经验:因子计算时,一定要考虑极端值。比如某天股票涨停,pct_change算出来是10%,但第二天跌停,因子值就剧烈波动。我一般会做截尾处理,把上下1%的极端值拉回来。

4.2 因子标准化

标准化是为了让不同量纲的因子可以放在一起比较。常用的有两种方法:

4.2.1 Z-score标准化

Z-score是最常用的方法,公式很简单:

def zscore_standardize(factor_series):
    """
    Z-score标准化
    """
    mean = factor_series.mean()
    std = factor_series.std()
    return (factor_series - mean) / std

但要注意,Z-score假设数据服从正态分布。实际中很多因子是偏态的,比如换手率因子。这时候用Z-score效果就不好。我建议先做对数变换,再标准化。

4.2.2 分位数标准化

分位数标准化对异常值更鲁棒。说白了就是把数据映射到0到1之间:

def quantile_standardize(factor_series, n_quantiles=5):
    """
    分位数标准化:将因子分为n个等级
    """
    return pd.qcut(factor_series, n_quantiles, labels=False)

避坑指南:我曾经在实盘中发现,用Z-score标准化的因子在极端行情下会失效。因为市场波动剧烈时,标准差变得很大,导致因子值被压缩到很小范围。后来我改用分位数标准化,效果稳定多了。

4.3 因子中性化

因子中性化是为了剔除市场风格的影响。你想想看,如果某个因子在市值大的股票上表现好,那到底是因子有效,还是市值风格在起作用?

4.3.1 市值中性化

做法很简单:对因子做回归,剔除市值的影响。

def market_cap_neutralize(factor, market_cap):
    """
    市值中性化:回归残差作为中性化后的因子
    """
    from sklearn.linear_model import LinearRegression
    model = LinearRegression()
    model.fit(market_cap.values.reshape(-1, 1), factor.values)
    residual = factor.values - model.predict(market_cap.values.reshape(-1, 1))
    return residual

4.3.2 行业中性化

行业中性化更直接:在每个行业内做标准化。这样不同行业的股票之间就可以直接比较了。

def industry_neutralize(factor, industry_labels):
    """
    行业中性化:每个行业内做Z-score
    """
    neutralized = factor.groupby(industry_labels).transform(
        lambda x: (x - x.mean()) / x.std()
    )
    return neutralized

我的建议:实际中我通常同时做市值和行业中性化。先做行业中性化,再做市值中性化。顺序不能反,因为行业本身就和市值相关。

4.4 因子IC/IR分析

因子有效性怎么衡量?IC和IR是核心指标。

指标 含义 计算方式 经验阈值
IC 因子值与未来收益的相关性 Spearman秩相关系数 |IC| > 0.02 算有效
IR IC的稳定性 IC均值 / IC标准差 IR > 0.5 算稳定
def calc_ic_ir(factor_series, future_return_series):
    """
    计算IC和IR
    """
    ic = factor_series.corr(future_return_series, method='spearman')
    # 滚动计算IC序列
    ic_series = factor_series.rolling(20).corr(future_return_series, method='spearman')
    ir = ic_series.mean() / ic_series.std()
    return ic, ir

我记得有一次,一个因子IC值高达0.08,但IR只有0.2。这说明因子时灵时不灵,不稳定。这种因子我一般不会用在实盘里。

4.5 因子共线性处理

共线性是因子处理的大坑。多个因子高度相关,会导致模型不稳定,回测和实盘表现差异巨大。

4.5.1 如何检测共线性

最常用的是VIF(方差膨胀因子):

def calc_vif(factor_matrix):
    """
    计算每个因子的VIF值
    VIF > 10 表示存在严重共线性
    """
    from statsmodels.stats.outliers_influence import variance_inflation_factor
    vif_data = pd.DataFrame()
    vif_data['factor'] = factor_matrix.columns
    vif_data['VIF'] = [variance_inflation_factor(factor_matrix.values, i) 
                       for i in range(factor_matrix.shape[1])]
    return vif_data

4.5.2 如何处理共线性

我常用的方法有三种:

  • 删除法:保留IC最高的因子,删除其他相关因子
  • PCA降维:把相关因子合成主成分
  • 正则化:用Lasso回归自动筛选因子

避坑指南:我曾经在实盘里吃过共线性的亏。回测时两个动量因子相关性0.9,但回测曲线很漂亮。结果一上实盘,两个因子互相抵消,策略直接失效。从那以后,我要求所有因子的VIF必须小于5。

知识体系总览

下面这张图展示了因子处理的全流程,我建议你保存下来,做策略时对照着看:

因子计算与处理全流程 原始行情数据 因子定义与计算 标准化 中性化 IC/IR有效性分析 共线性处理(VIF/PCA) 每一步都直接影响最终策略的实盘表现,切勿跳过

因子处理这块,说白了就是「去伪存真」的过程。把噪音去掉,把真实信号留下来。我做了这么多年量化,最大的体会就是:因子处理比因子挖掘更重要。一个处理得当的普通因子,往往比一个处理粗糙的「神奇因子」更靠谱。

核心要点回顾:

  • 标准化解决量纲问题,分位数法比Z-score更鲁棒
  • 中性化剔除风格干扰,市值和行业都要做
  • IC看相关性,IR看稳定性,两者缺一不可
  • 共线性是实盘杀手,VIF必须控制在5以下

专注资料整理