4、因子计算与构建:因子定义、因子计算公式、因子标准化处理、中性化处理

好,咱们进入正题。因子计算这块,说白了就是把你的投资逻辑变成一串数字。我见过不少新手,逻辑讲得头头是道,一到写公式就懵了。别急,咱们一步步来。

4.1 因子定义——先想清楚你要抓什么

因子定义,就是明确你的选股逻辑。我个人习惯,在写代码之前,先用一句话把因子说清楚。比如:

  • 动量因子:过去N个月涨幅高的股票,未来继续涨的概率大。
  • 价值因子:市盈率低的股票,长期回报更好。
  • 质量因子:ROE高的公司,盈利能力更强。

嗯,这里要注意。因子定义不能太模糊。你想想看,如果我说「好公司因子」,那什么叫好公司?每个人标准不一样。所以定义要可量化、可复现。

核心原则:因子定义必须满足三个条件——可计算、可排序、可回测。

我在项目中遇到过一个问题:团队里两个人定义了同一个「成长因子」,一个用营收增速,一个用净利润增速。结果回测结果完全相反。你看,定义差一点,结果差十万八千里。

4.2 因子计算公式——把逻辑变成数学

定义清楚了,接下来就是写公式。这部分我建议你直接用Python,pandas和numpy基本够用。咱们看几个常见例子。

4.2.1 动量因子

# 过去20个交易日的累计收益率
import pandas as pd
import numpy as np

def momentum_factor(close_prices, window=20):
    """
    close_prices: DataFrame, 每列是一只股票,每行是一个交易日
    """
    ret = close_prices.pct_change(window)
    return ret

说白了,就是算一下过去一段时间涨了多少。但这里有个坑——你用什么频率?日频?周频?我建议用日频,信息更充分。

4.2.2 价值因子

# 市盈率倒数(E/P)
def ep_factor(market_cap, net_income):
    """
    market_cap: 总市值
    net_income: 净利润(TTM)
    """
    ep = net_income / market_cap
    return ep

为什么用E/P而不是P/E?因为P/E可能为负,排序时会出问题。E/P为负时,至少还能区分出亏损公司。这是我踩过的坑,分享给你。

4.2.3 质量因子

# ROE(净资产收益率)
def roe_factor(net_income, equity):
    roe = net_income / equity
    return roe

ROE这个指标,巴菲特很喜欢。但要注意,金融公司和普通公司的ROE含义不同。我曾经在回测时把银行股和制造业股混在一起算ROE因子,结果因子失效了。后来才意识到,行业不同,ROE的分布差异很大。

4.3 因子标准化处理——让不同因子能比较

因子算出来了,但不同因子的量纲不一样。比如动量因子可能是-0.3到0.5,市盈率因子可能是0.01到0.1。直接放一起比较?不行。所以要做标准化。

常用的方法有三种:

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

我个人最常用的是Z-score。为什么?因为它保留了因子的相对大小信息。排序标准化虽然稳健,但会丢失幅度信息。

def zscore_standardize(factor):
    """
    对因子进行Z-score标准化
    """
    mean = factor.mean()
    std = factor.std()
    return (factor - mean) / std

小技巧:标准化时,建议按截面(每个时间点)独立做。不要用全历史数据算均值和标准差,否则会有前视偏差。

4.4 中性化处理——剔除你不想要的风格

标准化之后,因子还是「脏」的。为什么?因为因子之间会互相影响。比如市值因子和动量因子,小盘股往往动量更强。如果你不做中性化,你以为是动量因子在起作用,其实可能是小盘股效应。

中性化处理,说白了就是「剔除干扰」。最常用的方法是回归法:

import statsmodels.api as sm

def neutralize_factor(factor, exposure):
    """
    factor: 待中性化的因子
    exposure: 需要剔除的风格暴露(如市值、行业)
    """
    X = sm.add_constant(exposure)
    model = sm.OLS(factor, X).fit()
    residual = model.resid
    return residual

你看,我把因子对市值和行业做回归,取残差。这个残差就是「剔除风格影响后的纯因子」。嗯,这里要注意,回归时记得加常数项。

我曾经犯过一个错:做市值中性化时,忘了加行业虚拟变量。结果因子在某个行业里特别有效,但换到另一个行业就失效了。后来加了行业中性化,因子表现才稳定下来。

警告:中性化不是万能的。过度中性化会剔除掉你想要的信息。比如你本来就想抓小盘股效应,那就不该做市值中性化。想清楚你的投资逻辑再动手。

4.5 知识体系总览

说了这么多,咱们用一张图把整个流程串起来。我画了个流程图,你看看。

因子计算与构建流程 1. 因子定义 明确投资逻辑 2. 因子计算 编写计算公式 3. 标准化处理 统一量纲 4. 中性化处理 剔除风格干扰 5. 输出纯因子 用于后续回测 关键注意事项 • 因子定义要可量化、可复现 • 计算时注意前视偏差 • 标准化按截面独立进行 • 中性化要结合投资逻辑,避免过度剔除

这张图把整个流程串起来了。从定义到计算,再到标准化和中性化,每一步都有它的意义。你想想看,如果跳过标准化,不同量纲的因子怎么比较?如果跳过中性化,你怎么知道因子收益是来自因子本身还是风格暴露?

好了,因子计算这块就聊到这儿。记住,好的因子不是算出来的,是设计出来的。多想想你的逻辑,再动手写代码。