第三章:因子构建原理——因子定义、计算逻辑与标准化方法
好,咱们进入正题。因子构建,说白了就是把你的投资逻辑变成一串可计算的数字。我见过太多人上来就写代码,结果算出来的东西自己都解释不了。嗯,这章咱们就把这事掰扯清楚。
3.1 因子定义:你的逻辑得能说人话
因子定义,不是拍脑袋想个名字就完事了。你得回答三个问题:
- 这个因子想捕捉什么? 比如低溢价、高动量、还是正股弹性?
- 它的经济含义是什么? 为什么这个指标能预测未来收益?
- 它的边界在哪? 什么情况下会失效?
举个例子。我个人习惯把「转股溢价率」作为核心因子之一。它的定义很简单:
转股溢价率 = (转债价格 / 转股价值) - 1
但光有公式不够。你得知道:溢价率低,说明转债跟正股走得近,进攻性强;溢价率高,说明债性重,防守好。我在项目中遇到过,有人把溢价率直接当反向因子用,结果在牛市里亏得底掉——因为低溢价品种一旦正股暴跌,转债跌得比谁都狠。
核心原则:因子定义必须可解释、可复现、可验证。如果你自己都说不清这个因子在测什么,那它大概率是个伪因子。
3.2 因子计算逻辑:别让细节坑了你
定义清楚了,接下来就是怎么算。这里头门道不少,我挑几个关键点说说。
3.2.1 数据对齐
转债数据有个特点:价格是日频的,但转股价、下修条款这些是事件驱动的。你算因子时,必须保证数据时间戳对齐。我曾经犯过一个错——用当天的收盘价去匹配前一天的转股价,结果算出来的溢价率全是错的。
避坑指南:所有因子计算,务必使用「当日可用信息」原则。也就是说,你算T日的因子,只能用T-1日及之前的数据。千万别偷看未来。
3.2.2 特殊事件处理
转债有下修、强赎、回售这些特殊事件。因子计算时,这些事件会直接影响价格和转股价值。我的做法是:
- 下修日:重新计算转股价值,因子值会跳变
- 强赎公告后:溢价率通常会快速收敛,因子失效
- 回售期:债底保护增强,纯债价值因子权重需要调整
你想想看,如果不对这些事件做标记,你的因子回测结果会包含大量「假信号」。
3.2.3 代码示例:一个简单的溢价率因子
下面是我常用的计算逻辑,用Python写的。注意看注释部分——那些都是踩过的坑。
import pandas as pd
import numpy as np
def calc_premium_factor(cb_price, conversion_value, trade_date):
"""
计算转股溢价率因子
cb_price: 转债收盘价
conversion_value: 转股价值
trade_date: 交易日
"""
# 防止除零
conversion_value = np.where(conversion_value > 0, conversion_value, np.nan)
# 计算溢价率
premium = (cb_price / conversion_value) - 1
# 剔除异常值:溢价率超过100%或为负的极端情况
premium = np.where((premium > -0.1) & (premium < 1.0), premium, np.nan)
# 返回DataFrame
result = pd.DataFrame({
'trade_date': trade_date,
'premium_rate': premium
})
return result
个人经验:因子计算时,一定要做异常值过滤。我见过有人算出的溢价率是-500%,原因是数据源里转股价值字段填错了。这种脏数据不处理,后面所有分析都是白搭。
3.3 因子标准化:让不同因子站在同一起跑线
因子算出来了,但不同因子的量纲不一样。比如溢价率是百分比,换手率是倍数,纯债价值是元。直接拿它们做比较或组合,就像拿苹果比橘子。所以,标准化是必须的。
常用的方法有两种:Z-score和分位数。我分别说说。
3.3.1 Z-score标准化
Z-score的核心思想是:把因子值转换成「偏离均值多少个标准差」。公式很简单:
Z = (X - μ) / σ
其中μ是均值,σ是标准差。标准化后,因子均值为0,标准差为1。
优点:保留了原始数据的分布形态,对极端值敏感。
缺点:如果数据有厚尾或异常值,Z-score会被拉偏。
我记得有一次做多因子组合,用Z-score标准化后,有个因子因为某天出现极端值,导致整个组合权重失衡。后来我改用截尾Z-score——先剔除上下1%的极端值,再算均值和标准差。
实战建议:Z-score适合因子值近似正态分布的情况。如果你的因子是偏态分布(比如换手率),建议先做对数变换,再标准化。
3.3.2 分位数标准化
分位数标准化,说白了就是把因子值映射到0到1之间。常用的有两种:
- 排名分位数:按因子值排序,取排名百分比。比如排名前10%的,分位数为0.9。
- 等分分位数:把数据分成N等份,每份赋一个离散值。比如分成5组,每组赋值为1到5。
分位数标准化的好处是:对异常值不敏感。不管你的因子值多大或多小,最终都落在0到1之间。但代价是丢失了因子值之间的相对距离信息。
举个例子。两个转债的溢价率分别是5%和10%,Z-score会保留「相差5个百分点」这个信息;但分位数只告诉你「一个在30%分位,一个在60%分位」,具体差多少不知道。
3.3.3 怎么选?
我个人的经验是:
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 因子值分布对称,无极端值 | Z-score | 保留更多信息 |
| 因子值有厚尾或异常值 | 截尾Z-score或分位数 | 避免极端值干扰 |
| 多因子组合,需要等权 | 分位数 | 各因子贡献均衡 |
| 因子本身是排名类指标 | 分位数 | 保持原始含义 |
一个小技巧:如果你不确定用哪种,可以两种都试试,然后看哪个在回测中表现更稳定。我经常这么干——反正代码写一次,跑两个版本不费事。
3.4 本章知识体系总览
下面这张图,把因子构建的整个流程串起来了。从定义到计算,再到标准化,每一步都有坑,每一步都有技巧。
嗯,这张图基本把本章的核心逻辑都串起来了。从定义到计算再到标准化,每一步都有讲究。你写代码的时候,脑子里得有这张图——知道自己现在在哪一步,下一步要做什么。
最后说一句:因子构建不是一锤子买卖。你可能会反复调整定义、优化计算逻辑、尝试不同的标准化方法。这很正常。我做了这么多年,每个因子至少迭代过三版以上。别怕改,怕的是不改。
公众号:蓝海数据掘金营,微信deep3321