halo 的技术博客

返回

如果你读过 Amihud (2002) 的非流动性溢价,会以为「流动性」是个股层面的事:某只股票不好交易,长期就该给你更高收益。这个直觉对,但只说了一半。

另一半更危险:流动性自己会「传染」。2008 年和 2020 年 3 月,不是某几只股票突然难卖,而是整个市场一起难卖——所有股票的买卖价差同时拉开、冲击成本同时上升、谁都找不到对手方。这种「系统性流动性」不是个股特异风险的加总,而是一个共同因子:它在某一刻让所有资产一起变贵或变便宜,而且越到危机越被放大。

结论先放这:用 PCA 在(股票 × 时间)的 Amihud ILLIQ 矩阵上分解,第一主成分(PC1)单独解释了 67.8% 的截面方差——也就是说,个股流动性的绝大多数波动,追根溯源是同一个系统性因子在动。更关键的是危机放大:常态期个股流动性的截面两两相关性只有 5.5%,危机期直接飙到 35.6%(放大 5.5 倍);按对共同因子暴露排序的组合,其流动性溢价从常态的 −2.5% 被危机放大到 +11.0%。

PCA 解释方差:PC1 单独解释 67.8% 的截面流动性方差

这张图是整篇文章的「指纹」:十条主成分里,PC1 一根柱子就占了近七成,剩下的九条加起来才三成。如果流动性纯粹是个股特异的,PCA 应该是平坦的「长尾」;实际它是「一根独大」。这本身就是「流动性存在共同因子」最干净的证据。

一、Amihud ILLIQ 与「共同因子」的直觉#

先复习 Amihud 非流动性指标(ILLIQ):

ILLIQ_d = |r_d| / (Price_d × Volume_d)      # 单位美元成交额引发的价格变动
ILLIQ_m = mean_d(ILLIQ_d) × 1e9             # 月度聚合,×1e9 便于读数
plaintext

直觉是:花同样的钱,价格动得越多,越不流动。注意 ILLIQ 是「不流动性」——数值越大越难交易。

现在问一个问题:如果我拿 60 只股票、120 个月的 ILLIQ,排成一个 60×120 的矩阵(每行一只股票、每列一个月份),这些数字的波动到底有几成是「大家共同经历的事」?

把这个问题翻译成因子模型语言:

log(ILLIQ_i,t) = b_i · L_t + u_i,t
plaintext
  • L_t系统性流动性共同因子(全市场在时刻 t 的「不流动性水平」,所有股票共享)
  • b_i 是股票 i 对共同因子的载荷(暴露度,截面异质)
  • u_i,t 是个股特异的流动性噪声

如果 L_t 真的存在且重要,那么「所有股票在某一月的 ILLIQ 同步起伏」应该非常显著——这正是 PCA 能抓到的结构。

系统性流动性因子:常态温和、危机窗口系统性飙升

图上那段被阴影标出的,就是注入的「危机窗口」:共同因子从常态的 0.5 一路飙升到 23 的量级。这正是 2008/2020 那种「所有资产一起缺流动性」的瞬间。

二、PCA 抽出共同因子:它解释了多少截面方差#

PCA 的做法是:把每个月截面(60 只股票的 ILLIQ)中心化,算协方差矩阵,求特征分解,按特征值从大到小排。第一主成分的方向,就是「能解释最多截面方差」的那一个共同运动模式——它就是我们要的流动性共同因子。

def extract_common_factor(illiq):
    """在 ILLIQ 矩阵上做 PCA,抽第一主成分 = 流动性共同因子。"""
    log_illiq = np.log(illiq)                  # 右偏太严重,取对数
    X = log_illiq.T                            # (T, N):每行一个时间截面
    Xc = X - X.mean(axis=0)
    Cov = (Xc.T @ Xc) / (Xc.shape[0] - 1)      # (N, N) 协方差矩阵
    eigval, eigvec = np.linalg.eigh(Cov)
    order = np.argsort(eigval)[::-1]
    eigval, eigvec = eigval[order], eigvec[:, order]
    evr = eigval / eigval.sum()                # 解释方差比(严格归一化)
    pc1 = eigvec[:, 0]                         # 各股票在 PC1 上的载荷
    common_factor = Xc @ pc1                   # 共同因子的时间序列
    return evr, pc1, common_factor
python

跑出来(N=60, T=120):

主成分解释方差占比
PC1(共同因子)67.8%
PC21.8%
PC31.4%
PC4~PC10合计 ~6.0%

PC1 一根就吃掉了近七成的截面方差。换句话说:当你看到某只股票这个月突然变难卖,有 68% 的概率,不是它自己出了什么事,而是「整个市场的流动性水位」在动,它只是被带着走。这就是「流动性共同因子」存在的铁证。

而且这个抽出来的 PC1,和注入的系统性因子 L_t 相关系数高达 0.995——说明 PCA 几乎完美还原了我们埋进去的那个共同因子,没有从噪声里「造」出假结构。

三、危机放大:共同因子把个股拧成一股绳#

共同因子最危险的地方,不是它「存在」,而是它在危机时被放大。常态期,个股流动性主要受自己特有的买卖盘、自己特有的新闻驱动,彼此不太相关;危机期,所有人都在抢流动性、所有人都在去杠杆,结果所有股票的流动性同步恶化——截面相关性飙升。

def avg_pairwise_corr(log_illiq, idx):
    """算给定时间索引上的截面平均两两相关性。"""
    sub = log_illiq[:, idx]
    c = np.corrcoef(sub)
    iu = np.triu_indices(c.shape[0], 1)
    return np.nanmean(c[iu])

# 常态期 vs 危机期
calm_idx   = [t for t in range(T) if not any(a <= t < b for a, b in crises)]
crisis_idx = [t for t in range(T) if any(a <= t < b for a, b in crises)]
corr_calm   = avg_pairwise_corr(log_illiq, calm_idx)
corr_crisis = avg_pairwise_corr(log_illiq, crisis_idx)
python

危机放大机制:流动性同步性 5.5% → 35.6%

结果触目惊心:常态期截面平均相关性只有 5.5%,危机期直接跳到 35.6%——放大了 5.5 倍。这意味着危机时,你以为自己持有了 60 只「分散」的股票,实际上它们的流动性风险高度同步:一旦要砍仓,所有头寸会同时变难卖、同时冲击成本飙升。分散化在流动性维度上失效了。

这正是 2008 年许多「分散化」组合崩得比预想更惨的微观机制之一:相关性在最需要分散的时候反而最高。

四、溢价的危机放大:高暴露组合在危机期赚得更多(也更疼)#

共同因子不是免费午餐,它是有价格的。逻辑是:承担「系统性缺流动性时你也缺」的风险,长期该拿补偿。而且因为危机时共同因子被放大,这种补偿在危机期会被不成比例地放大——高暴露组合危机会更疼,但拉长时间,危机期的稀缺性补偿也更高。

def common_factor_premium(pc1, ret, crises, T):
    """按共同因子暴露排序分 5 组,看高暴露−低暴露组合的溢价(常态 vs 危机)。"""
    spread_cal, spread_cr = [], []
    for t in range(T - 1):
        order = np.argsort(pc1)
        q = np.array_split(order, 5)
        r_low  = ret[q[0], t + 1].mean()
        r_high = ret[q[4], t + 1].mean()
        if any(a <= t < b for a, b in crises):
            spread_cr.append(r_high - r_low)
        else:
            spread_cal.append(r_high - r_low)
    return np.mean(spread_cal) * 12 * 100, np.mean(spread_cr) * 12 * 100
python

危机放大溢价:共同因子暴露溢价 −2.5% → +11.0%

跑出来的数字印证了「危机放大」:

时期高暴露−低暴露组合 年化溢价
常态期−2.5%(不稀缺时不值钱)
危机期+11.0%(稀缺性补偿被显著放大)

常态期,共同因子暴露高不值钱(甚至略负,因为平时大家都不缺流动性);但一到危机,高暴露股票因为「危机时最缺流动性、被迫在最低点斩仓」,反而拿到最高的稀缺性补偿——+11.0% 对 −2.5%,差了 13.5 个百分点。这就是流动性溢价的「危机放大」指纹:平时看不见,危机时集中兑现。

五、对实战的三层含义#

把上面的机制翻译成能用的结论:

1. 流动性不是个股风险,是系统性风险的一部分。 风控里如果你只盯「单只股票冲击成本」,会系统性低估组合层面的流动性风险——因为危机时冲击成本是相关的。正确做法是把流动性共同因子当成和市值、估值并列的风险维度,测组合的「流动性 Beta」。

2. 危机时的分散化是假的。 相关性从 5.5% 跳到 35.6% 意味着:平时用相关性矩阵做的风险预算,危机时会彻底失效。压力测试不能只用历史相关性,要叠加「相关性飙升情景」(correlation breakdown stress test)。

3. 流动性溢价策略要在危机前就埋伏。 高暴露组合常态 −2.5%、危机 +11.0%——如果等危机来了才追,正好接刀。真正的流动性溢价 alpha,来自在常态期以合理价格持有高暴露资产、用长期视角赚危机放大的补偿,而不是危机时去赌反弹。

六、五类真实陷阱(别把模拟当圣杯)#

陷阱 1:PCA 的 PC1 方向是符号模糊的。 eigh 返回的特征向量可以整体取反,不影响方差解释。如果你的「共同因子」画出来和危机阴影反着走,先检查符号(本文用 corrcoef 与注入的 L_t 对齐方向)。不处理符号,图会画出「流动性越好危机越严重」的鬼故事。

陷阱 2:用原始 ILLIQ 直接 PCA 会被极端值绑架。 ILLIQ 右偏极严重(小盘股能差大盘股几个数量级),不取对数直接 PCA,PC1 会变成「谁最极端」而不是「共同因子」。本文 np.log(illiq) 这一步不是美化,是必须的。

陷阱 3:截面相关性在少量股票下噪声极大。 N=60 还算稳,若你只用 10 只股票算「危机相关性 35.6%」,标准误能到 ±10%。结论要在足够宽的 universe 上才站得住。

陷阱 4:危机窗口是注入的,真实数据里要事后标注。 模拟里危机位置已知,实战里你分不清「现在是不是危机」。真正的流动性择时要用 VIX、买卖价差宽度、TAQ 成交量等实时代理变量,而不是等事后才知道。

陷阱 5:把共同因子当成可交易的「因子多空」会踩空。 高暴露−低暴露组合的 +11.0% 是危机期均值,但危机期本身就伴随巨大回撤;单纯做多高暴露股票,你的本金可能在拿到补偿前先归零。流动性溢价是「风险补偿」不是「免费 alpha」,必须用能扛过危机的仓位去赚。

七、小结#

流动性共同因子不是学术玩具。它告诉我们三件事:

  1. 个股流动性的近七成波动,来自同一个系统性因子——流动性首先是市场层面的事,其次才是个股层面的事;
  2. 危机把共同因子放大 5.5 倍——分散化在流动性维度上会失效,相关性会飙升;
  3. 流动性溢价在危机期被放大到 +11.0%——平时看不见的补偿,危机时集中兑现,但前提是你能活着熬到兑现。

下次有人跟你说「我选了 60 只低相关的股票,流动性风险分散了」,你可以回一句:常态期是的,危机期它们的流动性相关性会从 5.5% 跳到 35.6%——你分散的是收益,没分散的是流动性风险

流动性共同因子:Amihud 溢价的系统性来源与危机放大
https://blog.halo26812.eu.org/blog/liquidity-common-factor
Author halo
Published at 2026年7月15日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨