4. 单因子分析基础:因子定义、因子计算、因子暴露、因子收益率

做量化投资的朋友,几乎每天都要跟因子打交道。但说实话,很多人对因子的理解还停留在「拿个指标排序分组」的阶段。我刚开始做回测时也踩过这个坑——因子定义不清晰,计算逻辑有漏洞,最后出来的结果自己都不敢信。

这一章,咱们就把单因子分析的四个核心环节掰开揉碎讲清楚。你想想看,如果连因子是什么、怎么算、暴露度怎么衡量、收益率怎么归因都搞不明白,后面的多因子模型、组合优化就更别谈了。

核心要点: 单因子分析是因子投资体系的基石。因子定义决定研究方向,因子计算决定数据质量,因子暴露决定持仓权重,因子收益率决定策略表现。

4.1 因子定义:先把概念说清楚

因子,说白了就是能解释股票收益率差异的某种特征。比如市盈率、市净率、动量、波动率等等。但这里有个关键问题:同一个因子,不同人定义可能完全不同

举个例子,「低波动因子」。有人用过去60日收益率的标准差,有人用过去252日的日收益率波动率,还有人用贝塔值来近似。我在项目中遇到过,同一个因子用不同口径算,IC值能差出一倍多。

所以,因子定义必须包含三个要素:

  • 因子名称:清晰、无歧义,比如「BP因子」而不是「估值因子」
  • 计算公式:明确每个变量的来源和计算方式
  • 处理规则:缺失值怎么处理、极端值是否截尾、是否中性化
我的习惯: 每个因子都建一个「因子说明书」,把定义、计算步骤、处理规则全部写清楚。这样半年后回头看,自己还能看懂。

4.2 因子计算:从原始数据到因子值

因子计算是体力活,也是技术活。我见过太多人在这步翻车——数据没对齐、复权没处理好、行业分类用了不同版本……

标准的因子计算流程大致如下:

  1. 数据获取:行情数据、财务数据、另类数据
  2. 数据清洗:去重、对齐、处理缺失值
  3. 因子原始值计算:按公式算出每个股票每个时点的因子值
  4. 因子预处理:去极值、标准化、中性化
  5. 因子值存储:以面板数据格式存储,方便后续分析

这里我特别想强调一点:因子计算的时间点一定要跟交易时间对齐。比如你用财报数据算因子,必须考虑财报发布日期,而不是财报截止日。我曾经因为这个细节,回测结果跟实盘差了十万八千里。

# 一个简单的因子计算示例:BP因子(市净率倒数)
import pandas as pd
import numpy as np

def calc_bp_factor(market_cap, book_value):
    """
    计算BP因子
    market_cap: 总市值(万元)
    book_value: 净资产(万元)
    """
    # 避免除零
    bp = np.where(market_cap > 0, book_value / market_cap, np.nan)
    return bp

# 去极值处理(MAD方法)
def winsorize_mad(factor, n=5):
    median = np.nanmedian(factor)
    mad = np.nanmedian(np.abs(factor - median))
    upper = median + n * mad
    lower = median - n * mad
    return np.clip(factor, lower, upper)

# 标准化
def standardize(factor):
    return (factor - np.nanmean(factor)) / np.nanstd(factor)
注意: 因子计算时,一定要区分「截面标准化」和「时间序列标准化」。截面标准化是每个时点对所有股票做处理,时间序列标准化是对单只股票的历史值做处理。两者用途完全不同,千万别搞混。

4.3 因子暴露:你的组合到底暴露了多少因子?

因子暴露,简单说就是「某个股票在某个因子上的得分」。比如某只股票的BP因子暴露是0.8,意味着它的估值水平比市场平均低20%(标准化后的含义)。

但这里有个容易混淆的点:因子暴露可以是原始值,也可以是标准化后的值。我个人习惯用标准化后的Z-score,因为这样不同因子之间可以直接比较。

因子暴露的计算其实很简单:

# 计算因子暴露(标准化后的值)
def calc_factor_exposure(factor_raw):
    # 去极值
    factor_clean = winsorize_mad(factor_raw)
    # 标准化
    factor_exposure = standardize(factor_clean)
    return factor_exposure

嗯,这里要注意:因子暴露不是一成不变的。随着时间推移,股票的因子暴露会发生变化。所以我们在做回测时,通常每个月(或每季度)重新计算一次因子暴露。

实战经验: 因子暴露的稳定性很重要。如果一个因子的暴露度每个月都剧烈波动,那这个因子的可投资性就很差。我一般会看因子暴露的月相关系数,低于0.6的因子基本不会用。

4.4 因子收益率:到底赚了多少钱?

因子收益率,是衡量因子策略赚钱能力的核心指标。计算方式有很多种,最常用的是分组收益法回归法

分组收益法:把股票按因子暴露从高到低分成N组(通常是5组或10组),然后计算每组下一期的收益率。最高组减最低组的收益差,就是因子收益率。

回归法:用截面回归,把股票收益率对因子暴露做回归,回归系数就是因子收益率。这种方法更精确,但计算量也更大。

# 分组收益法计算因子收益率
def calc_factor_return(factor_exposure, stock_returns, n_groups=5):
    """
    factor_exposure: 当期因子暴露
    stock_returns: 下期股票收益率
    """
    # 按因子暴露分组
    groups = pd.qcut(factor_exposure, n_groups, labels=False)
    
    # 计算每组平均收益率
    group_returns = []
    for i in range(n_groups):
        mask = (groups == i)
        group_returns.append(stock_returns[mask].mean())
    
    # 因子收益率 = 最高组 - 最低组
    factor_return = group_returns[-1] - group_returns[0]
    return factor_return, group_returns
避坑指南: 我曾经用分组法算因子收益率,结果发现最高组和最低组的股票数量严重不均。后来才意识到,是因为因子暴露分布不均匀,用分位数分组时有些组股票太少。解决办法是用等权重分组,或者用十分位数。

4.5 知识体系总览

说了这么多,咱们用一张图把单因子分析的逻辑串起来:

单因子分析知识体系 因子定义 因子计算 因子暴露 因子收益率 • 因子名称与含义 • 计算公式明确 • 处理规则定义 • 因子说明书 • 数据获取与清洗 • 原始值计算 • 去极值/标准化 • 时间对齐 • 标准化Z-score • 截面/时序区分 • 暴露度稳定性 • 定期更新 • 分组收益法 • 截面回归法 • 多空组合收益 • 统计显著性 核心逻辑 因子定义 → 因子计算 → 因子暴露 → 因子收益率 每一步都影响最终结果,环环相扣 「定义不清,后面全白做」

4.6 几个容易踩的坑

最后,分享几个我亲身踩过的坑,希望能帮你省点时间:

  • 幸存者偏差:回测时只用了现在还存活的股票,退市的股票没包含。结果因子收益率虚高。解决办法是用全样本,包括已退市的。
  • 前视偏差:用到了未来数据。比如用季报数据时,没考虑财报发布日期。我建议所有因子计算都做严格的「时间戳对齐」。
  • 多重比较偏差:同时测试几百个因子,总有几个看起来显著。这其实是统计上的假阳性。解决方法是用Bonferroni校正,或者做样本外测试。
重要提醒: 单因子分析只是第一步。一个因子在历史上表现好,不代表未来也能赚钱。因子失效、因子拥挤、市场风格切换,都是我们需要持续关注的问题。

好了,单因子分析的基础框架就讲到这里。记住这四个环节:定义、计算、暴露、收益率。每一步都扎实了,后面的多因子模型才能站得住脚。

专注资料整理