量化因子漂移巡查:PSI与IC应用 - 第2节

2. 因子分布漂移基础:概率分布与统计距离概念

在量化因子巡查中,分布漂移是指因子的统计分布特征随时间或市场环境变化而发生系统性改变的现象。理解分布漂移需要从概率分布的基本概念出发,并借助统计距离来量化这种变化。

2.1 概率分布基础

概率分布描述随机变量取值的可能性。在因子分析中,我们通常关注两类分布:

分布类型 描述 因子示例
连续分布 变量可取任意实数值,由概率密度函数(PDF)描述 市盈率、动量因子值
离散分布 变量取有限或可数多个值,由概率质量函数(PMF)描述 行业分类、评级等级
经验分布 基于实际观测数据构建的分布 因子分位数分组后的实际频数

因子漂移巡查的核心是比较两个时间窗口下的分布:基准分布(如建模期)和当前分布(如监控期)。

关键概念:分布漂移 ≠ 数值变化。即使因子均值不变,方差或偏度的变化也可能导致分布形态改变,从而影响因子有效性。

2.2 统计距离:量化分布差异

统计距离(Statistical Distance)用于衡量两个概率分布之间的差异程度。常见的统计距离包括:

  • KL散度(Kullback-Leibler Divergence):衡量一个分布相对于另一个分布的信息损失,非对称。
  • JS散度(Jensen-Shannon Divergence):KL散度的对称化版本,值域为[0, 1]。
  • Wasserstein距离:基于最优传输理论,对分布形状变化更敏感。
  • PSI(群体稳定性指标):基于分箱的离散化统计距离,是量化因子巡查中最常用的指标。

下表对比了PSI与其他统计距离的差异:

指标 对称性 值域 对尾部敏感度 因子巡查适用性
PSI 对称 [0, +∞) 中等 ★★★★★
KL散度 非对称 [0, +∞) 高 ★★★☆☆
JS散度 对称 [0, 1] 中等 ★★★★☆
Wasserstein距离 对称 [0, +∞) 高 ★★★★☆
实践建议:PSI之所以成为因子巡查的首选,是因为它计算简单、结果可解释,且与风控领域的群体稳定性分析一脉相承。对于高频因子,可结合Wasserstein距离捕捉更细微的分布变化。
统计距离概念关系图 统计距离 概率分布 P (基准期分布) 概率分布 Q (当前期分布) KL散度(非对称) JS散度(对称) Wasserstein距离 PSI(群体稳定性指标) PSI 是离散化后的统计距离 适用于因子分箱后的分布比较

3. PSI(群体稳定性指标)的数学定义与公式推导

PSI(Population Stability Index)最初用于信用评分卡模型监控,后被引入量化因子巡查领域。它通过比较两个分布在各分箱中的占比差异,量化分布漂移程度。

3.1 PSI的数学定义

给定基准分布 \( P \) 和当前分布 \( Q \),将数据划分为 \( n \) 个分箱(bins),PSI定义为:

PSI = Σ_{i=1}^{n} (P_i - Q_i) × ln(P_i / Q_i)

其中:

  • \( P_i \):基准分布在第 \( i \) 个分箱中的样本占比
  • \( Q_i \):当前分布在第 \( i \) 个分箱中的样本占比
  • \( n \):分箱总数(通常为10或20)
注意:当 \( Q_i = 0 \) 时,\( ln(P_i / Q_i) \) 无定义。实际计算中需对零占比做平滑处理,常用方法为:\( Q_i = max(Q_i, \epsilon) \) 或 \( Q_i = Q_i + 1/N \)(N为总样本量)。

3.2 公式推导:从KL散度到PSI

PSI的数学本质是对称化的KL散度。推导过程如下:

  1. KL散度定义:\( D_{KL}(P||Q) = Σ P_i × ln(P_i / Q_i) \)
  2. 反向KL散度:\( D_{KL}(Q||P) = Σ Q_i × ln(Q_i / P_i) \)
  3. PSI的对称化:\( PSI = D_{KL}(P||Q) + D_{KL}(Q||P) \)

展开后得到:

PSI = Σ P_i × ln(P_i / Q_i) + Σ Q_i × ln(Q_i / P_i)
    = Σ (P_i - Q_i) × ln(P_i / Q_i)

因此,PSI本质上是两个KL散度的和,具有对称性,且值越大表示分布差异越大。

3.3 PSI的分箱策略

分箱方式直接影响PSI的计算结果。常用的分箱策略包括:

分箱方法 描述 适用场景 注意事项
等距分箱 按因子值等间隔划分 因子值分布均匀时 可能产生空箱
等频分箱 按基准分布等样本量划分 因子巡查最常用 当前分布可能不等频
分位数分箱 按指定分位数(如十分位数)划分 因子值偏态分布时 需保证分位数稳定
自定义分箱 根据业务逻辑设定边界 有先验知识时 需定期验证边界合理性
实践标准:量化因子巡查中,通常采用等频分箱(10箱),即按基准期因子值将样本均分为10组,然后计算当前期在各组中的占比。这种策略对异常值不敏感,且结果易于解释。

3.4 PSI的阈值判断标准

根据行业经验,PSI值的判断标准如下:

PSI值范围 漂移程度 建议操作
PSI < 0.1 无显著漂移 继续监控,无需干预
0.1 ≤ PSI < 0.25 轻度漂移 加强监控,分析漂移原因
PSI ≥ 0.25 显著漂移 触发预警,考虑因子调整或重构
经验法则:阈值并非绝对。对于高频因子或高波动市场,可适当放宽至0.15/0.3;对于低频因子或稳定市场,可收紧至0.05/0.2。建议结合历史PSI分布动态调整阈值。
PSI计算流程与阈值判断 步骤1 获取因子数据 步骤2 等频分箱(10箱) 步骤3 计算P_i和Q_i 步骤4 计算PSI PSI阈值判断 PSI < 0.1 无显著漂移 0.1 ≤ PSI < 0.25 轻度漂移 PSI ≥ 0.25 显著漂移 继续监控 加强监控,分析原因 触发预警,调整因子

3.5 PSI计算代码示例

以下Python代码展示了PSI的完整计算过程:

import numpy as np
import pandas as pd

def calculate_psi(base, current, bins=10, method='equal_freq'):
    """
    计算群体稳定性指标(PSI)
    
    Parameters:
    -----------
    base : array-like
        基准期因子值
    current : array-like
        当前期因子值
    bins : int
        分箱数量
    method : str
        分箱方法:'equal_freq'(等频)或 'equal_width'(等距)
    
    Returns:
    --------
    psi : float
        PSI值
    bin_info : DataFrame
        各分箱的详细信息
    """
    # 1. 基于基准期确定分箱边界
    if method == 'equal_freq':
        # 等频分箱:按分位数划分
        percentiles = np.linspace(0, 100, bins + 1)
        bin_edges = np.percentile(base, percentiles)
        # 确保边界唯一
        bin_edges = np.unique(bin_edges)
    else:
        # 等距分箱
        bin_edges = np.linspace(np.min(base), np.max(base), bins + 1)
    
    # 2. 计算基准期各箱占比
    base_counts, _ = np.histogram(base, bins=bin_edges)
    base_pct = base_counts / len(base)
    
    # 3. 计算当前期各箱占比
    current_counts, _ = np.histogram(current, bins=bin_edges)
    current_pct = current_counts / len(current)
    
    # 4. 平滑处理:避免除零或取对数零
    epsilon = 1e-6
    base_pct = np.clip(base_pct, epsilon, 1 - epsilon)
    current_pct = np.clip(current_pct, epsilon, 1 - epsilon)
    
    # 5. 计算PSI
    psi = np.sum((base_pct - current_pct) * np.log(base_pct / current_pct))
    
    # 6. 构建详细信息
    bin_info = pd.DataFrame({
        'bin_start': bin_edges[:-1],
        'bin_end': bin_edges[1:],
        'base_count': base_counts,
        'base_pct': base_pct,
        'current_count': current_counts,
        'current_pct': current_pct,
        'contribution': (base_pct - current_pct) * np.log(base_pct / current_pct)
    })
    
    return psi, bin_info

# 使用示例
np.random.seed(42)
base_data = np.random.normal(0, 1, 10000)      # 基准期:标准正态分布
current_data = np.random.normal(0.2, 1.2, 10000)  # 当前期:均值偏移+方差变化

psi_value, bin_df = calculate_psi(base_data, current_data, bins=10)
print(f"PSI值: {psi_value:.4f}")
print(f"漂移判断: {'显著漂移' if psi_value >= 0.25 else '轻度漂移' if psi_value >= 0.1 else '无显著漂移'}")
代码要点:
  • 等频分箱使用np.percentile确保基准期各箱样本量相等
  • np.clip用于平滑处理,避免对数计算异常
  • 返回的bin_df包含各箱贡献度,可用于定位漂移来源

3.6 PSI的局限性

尽管PSI是因子巡查的核心工具,但它并非万能:

  • 分箱敏感性:不同分箱策略可能导致PSI值差异,需统一标准
  • 忽略顺序信息:PSI不区分分箱间的顺序关系,相邻箱的漂移与跨箱漂移同等对待
  • 对尾部不敏感:极端值的漂移可能被大量中间样本稀释
  • 无法区分漂移类型:均值漂移、方差漂移、偏度漂移在PSI中混合呈现
补充建议:在实际巡查中,建议将PSI与分箱贡献度热力图结合使用,快速定位哪些分箱发生了显著漂移。同时,可配合IC(信息系数)分析漂移对因子预测能力的影响。