3. 单因子分析基础:因子定义、因子值计算、分组回测框架搭建

好,咱们进入正题。单因子分析,说白了就是回答一个问题:这个因子到底能不能赚钱?

我刚开始做量化那会儿,总觉得因子越多越好。后来被市场狠狠教育了几次,才明白——一个靠谱的单因子分析框架,比一百个花哨的因子都重要。今天我就把这块的实操经验掰开揉碎讲给你听。

3.1 因子定义:别小看这一步

因子定义,就是给因子一个明确的数学表达式。比如经典的动量因子:

Momentum = (close_price_t - close_price_{t-20}) / close_price_{t-20}

嗯,看起来很简单对吧?但这里有个坑——定义不清晰,后面全白干

我个人习惯,定义因子时一定要明确三点:

  • 计算窗口:用过去多少天的数据?20天?60天?
  • 价格选择:用收盘价?还是用VWAP(成交量加权均价)?
  • 处理方式:要不要做对数变换?要不要去极值?
⚠️ 我曾经踩过的坑: 定义动量因子时用了复权价格,结果回测表现特别好。后来才发现,复权价格包含了分红送股的信息,这相当于把未来的信息泄露到了因子计算中。从此以后,我定义因子一律用不复权价格

这里我给出一个标准的因子定义模板,你直接套用就行:

字段 说明 示例
因子名称 英文标识,建议用大写 MOM_20D
因子描述 一句话说清楚因子含义 过去20个交易日的累计收益率
计算公式 明确的数学表达式 (P_t - P_{t-20}) / P_{t-20}
数据来源 用哪个数据库的哪个字段 日行情表.close_price
处理规则 去极值、中性化等 MAD去极值,市值行业中性化

3.2 因子值计算:代码怎么写才靠谱

定义好了,接下来就是算。你想想看,A股有5000多只股票,每天都要算一遍因子值,这代码效率必须得跟上。

我个人推荐用 pandas + numpy 做向量化计算,别用循环。来看个例子:

import pandas as pd
import numpy as np

def calc_momentum_factor(df, window=20):
    """
    计算动量因子
    df: 包含 'close' 列的DataFrame,索引为日期
    """
    # 向量化计算,一行搞定
    df['MOM'] = df['close'].pct_change(periods=window)
    return df

# 使用示例
price_data = pd.DataFrame({
    'close': [10, 10.5, 11, 10.8, 11.2, ...]
}, index=pd.date_range('2024-01-01', periods=100))

result = calc_momentum_factor(price_data)
print(result.head())
💡 小技巧: 计算因子值时,我习惯把结果存成 宽表格式(日期为行,股票代码为列)。这样后续做截面分析、分组回测时,数据操作会方便很多。

这里要注意一个细节:因子值的频率要和回测频率一致。如果你做的是月度调仓,那就按月计算因子值;如果是周度调仓,就按周算。别搞混了。

3.3 分组回测框架:搭建你的因子检验流水线

因子值算出来了,怎么知道它好不好?分组回测是最直观的方法。

核心逻辑很简单:

  1. 每个月(或每周)按因子值大小把股票分成N组
  2. 每组等权重或市值加权买入
  3. 持有到下个调仓日,计算收益
  4. 重复以上步骤,得到每组的历史收益曲线

如果因子有效,那么分组收益应该是单调的——第1组(因子值最大)收益最高,第N组(因子值最小)收益最低。

下面是我自己用的一个分组回测框架,你直接拿去改改就能用:

import pandas as pd
import numpy as np

def group_backtest(factor_df, return_df, n_groups=5, rebalance_freq='M'):
    """
    分组回测框架
    factor_df: 因子值DataFrame (日期×股票)
    return_df: 收益率DataFrame (日期×股票)
    n_groups: 分组数
    rebalance_freq: 调仓频率 ('M'=月度, 'W'=周度)
    """
    # 获取调仓日期
    rebalance_dates = pd.date_range(
        start=factor_df.index[0], 
        end=factor_df.index[-1], 
        freq=rebalance_freq
    )
    
    group_returns = {i: [] for i in range(1, n_groups+1)}
    
    for date in rebalance_dates:
        if date not in factor_df.index:
            continue
            
        # 获取当期因子值
        current_factor = factor_df.loc[date].dropna()
        
        # 分组
        current_factor = current_factor.rank(pct=True)
        groups = pd.cut(current_factor, bins=n_groups, 
                        labels=range(1, n_groups+1))
        
        # 计算下一期收益
        next_date = factor_df.index[factor_df.index > date][0]
        next_returns = return_df.loc[next_date]
        
        # 记录每组收益
        for group in range(1, n_groups+1):
            stocks_in_group = groups[groups == group].index
            group_return = next_returns[stocks_in_group].mean()
            group_returns[group].append(group_return)
    
    # 计算累计收益
    result = pd.DataFrame(group_returns)
    result = (1 + result).cumprod()
    
    return result
📊 分组回测的核心指标:
  • 单调性:分组收益是否严格单调递增/递减
  • 多空收益:第1组收益 - 第N组收益,越大越好
  • IC值:因子值与下期收益的截面相关系数
  • IR值:IC的均值除以标准差,衡量因子稳定性

说到IC值,我再多提一嘴。IC值分两种:

  • Pearson IC:线性相关系数,对异常值敏感
  • Spearman Rank IC:秩相关系数,更稳健

我个人习惯用 Rank IC,因为因子值经常有极端值,用秩相关能避免被几个异常点带偏。

3.4 知识体系总览

说了这么多,我把整个单因子分析的流程画成了一张图,方便你理解:

单因子分析知识体系 1. 因子定义 数学表达式 计算窗口、价格选择 2. 因子值计算 向量化计算 去极值、中性化 3. 分组回测 分组、调仓、计算收益 单调性检验 核心评估指标 📈 单调性 分组收益是否单调 💰 多空收益 Top组 - Bottom组 📊 IC值 因子与收益的相关性 📉 IR值 IC均值/IC标准差 ⚠️ 常见陷阱(我踩过的坑) ❌ 未来函数 用未来数据算因子 ❌ 幸存者偏差 只算存续股票 ❌ 过拟合 参数调得太多
💡 我的经验: 刚开始做因子分析时,别追求复杂的模型。先把分组回测框架搭稳了,把IC计算、单调性检验这些基本功练扎实。我见过太多人一上来就搞机器学习因子,结果连基础的单因子检验都没通过,白费功夫。

好了,单因子分析的基础框架就讲到这里。记住一句话:因子定义要清晰,计算要高效,回测要严谨。把这三点做到位,你的因子研究就成功了一半。

专注资料整理