4. 流动性因子的构建方法:排序分组法、差值法、回归残差法
流动性因子,说白了就是衡量「资产好不好卖」的指标。你想想看,一个股票如果交易量稀薄、买卖价差巨大,那它本质上就带着流动性风险。在资产定价里,这种风险是需要被定价的。
我个人习惯把流动性因子的构建方法分成三大类:排序分组法、差值法、回归残差法。每种方法都有它的适用场景和坑。今天我就把这三种方法掰开揉碎了讲清楚。
4.1 排序分组法:最直观的做法
排序分组法,是业界最常用的方法。它的逻辑很简单:按流动性指标排序,然后分组,做多流动性好的,做空流动性差的。
具体步骤是这样的:
- 选指标:比如用 Amihud 非流动性指标、换手率、买卖价差等。
- 排序:每个月末,把所有股票按流动性指标排序。
- 分组:分成 10 组(或 5 组),流动性最差的放第 1 组,最好的放第 10 组。
- 构建多空组合:做多第 10 组,做空第 1 组。
嗯,这里要注意:分组数不是越大越好。我见过有人分 20 组,结果每组股票太少,组合波动大得吓人。我个人建议,A 股市场用 10 组就够用了。
核心公式:
流动性因子收益率 = R(流动性最好组) - R(流动性最差组)
这个差值,就是流动性溢价。
4.2 差值法:更精细的度量
差值法,其实是对排序分组法的一个改进。它不满足于简单的多空组合,而是把流动性差异量化成连续变量。
我在项目中遇到过一个问题:用排序分组法构建的因子,在极端行情下会失效。为什么?因为分组法只考虑了排名,没考虑组内差异。比如第 1 组里,最差的股票和次差的股票,流动性可能差了一个数量级,但分组法把它们同等对待了。
差值法的做法是:
- 计算每个股票的流动性指标 z-score。
- 用市值加权或等权,计算流动性最好 30% 和最差 30% 的加权平均流动性。
- 取两者的差值作为因子值。
我的经验:差值法对极端值非常敏感。我曾经用 Amihud 指标直接做差值,结果因为几个小盘股的流动性极差,把整个因子都带偏了。后来我加了 winsorize(缩尾处理),效果好了很多。
4.3 回归残差法:剥离其他因素的影响
回归残差法,是学术圈比较推崇的方法。它的核心思想是:流动性因子应该只包含流动性风险,不能混杂市值、波动率等其他因素。
具体做法:
- 把流动性指标(如换手率)作为因变量。
- 把市值、波动率、价格等作为自变量。
- 做横截面回归,取残差作为「纯净」的流动性因子。
# Python 伪代码示例
import statsmodels.api as sm
# 每个月做一次横截面回归
def compute_liquidity_residual(liquidity, market_cap, volatility):
X = sm.add_constant(pd.DataFrame({'mkt_cap': market_cap, 'vol': volatility}))
y = liquidity
model = sm.OLS(y, X).fit()
residual = model.resid
return residual
避坑指南:我曾经犯过一个错误——用全样本做回归,结果引入了未来信息。回归残差法必须用滚动窗口,每个月只使用当月数据做横截面回归。否则就是 look-ahead bias,后果很严重。
4.4 因子构建中的细节处理
构建因子时,细节决定成败。我总结了三个关键点:频率、权重、去极值。
4.4.1 频率:日频 vs 月频
流动性因子的重平衡频率,直接影响因子表现。
| 频率 | 优点 | 缺点 |
|---|---|---|
| 日频 | 捕捉短期流动性变化 | 交易成本高,容易过拟合 |
| 月频 | 稳健,交易成本低 | 可能错过短期机会 |
| 季频 | 更稳健,适合长期策略 | 流动性变化可能被平滑掉 |
我个人习惯用月频。为什么?因为流动性因子的换手率本来就高,如果用日频,交易成本会吃掉大部分收益。你想想看,一个月调一次仓,已经能捕捉到流动性变化的主要部分了。
4.4.2 权重:等权 vs 市值加权
权重选择,其实是在「代表性」和「可投资性」之间做权衡。
- 等权:每个股票权重相同,更能反映流动性因子的「纯」效果。但等权组合在实际交易中很难执行,小盘股占比过高。
- 市值加权:更贴近实际投资组合,但大市值股票的流动性好,会稀释因子的效果。
我记得有一次做回测,用等权构建的流动性因子年化收益 12%,换成市值加权后只剩 6%。差别就是这么大。所以,如果你做学术研究,用等权;如果你做实战策略,用市值加权或两者都试试。
4.4.3 去极值:MAD 法 vs 百分位法
流动性指标,尤其是 Amihud 和买卖价差,经常出现极端值。不去极值的话,一个异常值就能把整个因子带偏。
常用的去极值方法有两种:
- MAD 法(中位数绝对偏差):用中位数 ± 3 倍 MAD 作为截断点。这个方法对异常值更稳健。
- 百分位法:直接截断 1% 和 99% 分位数。简单粗暴,但效果不错。
# MAD 法去极值示例
def mad_winsorize(series, n=3):
median = series.median()
mad = (series - median).abs().median()
upper = median + n * mad
lower = median - n * mad
return series.clip(lower, upper)
我的建议:先用 MAD 法去极值,再用百分位法做二次检查。如果两种方法结果差异很大,说明数据质量可能有问题,需要回头检查原始数据。
4.5 本章知识体系
下面这张图,是我自己梳理的流动性因子构建流程。你可以把它当作一个检查清单。
这张图把整个流程串起来了。从数据准备到指标计算,再到三种构建方法的选择,最后生成可用的流动性因子。你写代码的时候,可以按这个流程一步步来。