3、违约概率(PD)估计:历史违约率法、Merton模型、Logit/Probit回归模型

聊到信用风险,最核心的问题就一个:这个对手到底有多大概率会违约?

PD(Probability of Default)估计,说白了就是回答这个问题。我做了这么多年量化,见过太多人把PD当成一个静态数字来用,结果模型一上线就崩。其实PD估计是个动态过程,不同场景、不同数据条件下,方法选择天差地别。

今天咱们就掰开揉碎,聊聊三种最主流的PD估计方法:历史违约率法、Merton模型、Logit/Probit回归。这三种方法,分别对应了「没数据怎么办」、「有市场数据怎么办」、「有财务数据怎么办」三种场景。

核心逻辑图:三种方法适用场景与数据需求

PD估计三大方法:适用场景与数据需求 违约概率(PD)估计 历史违约率法 数据:历史违约记录 Merton模型 数据:股价、负债结构 Logit/Probit回归 数据:财务指标、宏观因子 适用:评级机构、银行内部 适用:上市公司、有市场数据 适用:非上市公司、多因子场景

3.1 历史违约率法:最朴素,也最容易被坑

这个方法,说白了就是「过去怎么违约的,未来就怎么违约」。你统计过去N年里,某个评级或某个行业里有多少家违约了,除一下总数,就是PD。

举个例子:

# 历史违约率计算示例
import pandas as pd

# 假设我们有5年数据,每年跟踪1000家BBB级公司
data = {
    'year': [2019, 2020, 2021, 2022, 2023],
    'total_firms': [1000, 1000, 1000, 1000, 1000],
    'defaults': [2, 5, 3, 8, 4]
}
df = pd.DataFrame(data)
df['pd'] = df['defaults'] / df['total_firms']
print(df)
# 平均PD
avg_pd = df['pd'].mean()
print(f"5年平均违约概率: {avg_pd:.4%}")

嗯,这里要注意。历史违约率法最大的坑是什么?样本偏差。我2015年给一家银行做内部评级系统时,他们用了10年的历史数据,结果发现违约率逐年下降。当时团队很开心,觉得风控做得好。我一看数据——不对啊,这10年里经济一直上行,根本没经历过衰退期。这种PD拿到经济下行期用,会死得很惨。

避坑指南:历史违约率法假设「过去=未来」,但信用周期是波动的。我建议至少覆盖一个完整的经济周期(7-10年),并且对极端年份做压力测试调整。

另外,样本量太小怎么办?比如你只跟踪了50家小企业,一年违约1家,PD就是2%。但第二年违约0家,PD变成0%。这波动也太大了。这时候可以用贝叶斯调整,引入先验分布来平滑估计。

3.2 Merton模型:把公司当成一个期权来定价

这个方法很有意思。Merton在1974年提出一个想法:公司的股权,本质上是一个看涨期权

你想想看,公司资产价值V,负债面值D(假设是零息债券,到期日T)。如果到期时V > D,股东还债后还有剩余;如果V < D,股东就违约,把公司扔给债权人。这不就是一个执行价为D的看涨期权吗?

那么违约概率就是:到期时资产价值V低于负债D的概率。

数学上,假设公司资产价值服从几何布朗运动:

dV = μV dt + σV dW

那么违约概率PD = N(-d2),其中:

d2 = [ln(V/D) + (r - σ²/2)T] / (σ√T)

N(·)是标准正态分布函数。这个d2,其实就是「距离违约的距离」,单位是标准差。

实际应用中,V和σ是观测不到的。我们只能看到股价E和股价波动率σ_E。所以需要解一个方程组:

import numpy as np
from scipy.stats import norm
from scipy.optimize import fsolve

def merton_pd(E, sigma_E, D, r, T):
    """
    用Merton模型估计违约概率
    E: 股权市值
    sigma_E: 股权波动率
    D: 负债面值
    r: 无风险利率
    T: 债务期限
    """
    def equations(vars):
        V, sigma_V = vars
        d1 = (np.log(V/D) + (r + 0.5*sigma_V**2)*T) / (sigma_V*np.sqrt(T))
        d2 = d1 - sigma_V*np.sqrt(T)
        # 两个方程:股权价值公式、波动率公式
        eq1 = V * norm.cdf(d1) - D * np.exp(-r*T) * norm.cdf(d2) - E
        eq2 = (V * norm.cdf(d1) / E) * sigma_V - sigma_E
        return [eq1, eq2]
    
    # 初始猜测:V ≈ E + D, sigma_V ≈ sigma_E * E/(E+D)
    V_init = E + D
    sigma_init = sigma_E * E / (E + D)
    V, sigma_V = fsolve(equations, [V_init, sigma_init])
    
    d2 = (np.log(V/D) + (r - 0.5*sigma_V**2)*T) / (sigma_V*np.sqrt(T))
    pd = norm.cdf(-d2)
    return pd, V, sigma_V

# 示例:某上市公司
pd, V, sigma_V = merton_pd(E=100e8, sigma_E=0.35, D=80e8, r=0.03, T=1)
print(f"Merton模型PD: {pd:.4%}")
print(f"隐含资产价值: {V:.2e}")
print(f"隐含资产波动率: {sigma_V:.2%}")

个人经验:Merton模型对上市公司特别好用,但要注意——它假设负债结构简单(只有一期零息债),现实中公司负债是分层的。我一般会把短期负债+50%长期负债作为D,效果还不错。

Merton模型的优点是什么?前瞻性。股价每天都在变,所以PD也每天更新,能快速反映市场情绪。缺点也很明显:只适用于上市公司,而且对资本结构假设太强。

3.3 Logit/Probit回归:把PD和财务指标挂钩

这个方法,说白了就是:用公司的财务指标来预测它会不会违约

为什么用Logit或Probit?因为违约是个0/1变量,线性回归会预测出负的概率或者大于1的概率,不合理。Logit和Probit通过一个非线性变换,把线性组合映射到[0,1]区间。

Logit模型的形式:

P(Y=1|X) = 1 / (1 + exp(-(β₀ + β₁X₁ + ... + βₖXₖ)))

Probit模型用标准正态分布的CDF代替Logistic函数:

P(Y=1|X) = Φ(β₀ + β₁X₁ + ... + βₖXₖ)

两者差别不大,Logit的尾部更厚一些,我个人习惯用Logit,因为解释起来方便——系数取指数就是odds ratio。

来看一个实际例子:

import pandas as pd
import statsmodels.api as sm

# 模拟数据:1000家公司,5个财务指标
np.random.seed(42)
n = 1000
data = pd.DataFrame({
    'leverage': np.random.uniform(0.1, 0.9, n),      # 资产负债率
    'liquidity': np.random.uniform(0.5, 2.5, n),     # 流动比率
    'profitability': np.random.uniform(-0.1, 0.2, n), # 资产收益率
    'size': np.random.uniform(10, 25, n),             # 总资产对数
    'coverage': np.random.uniform(0.5, 5.0, n)        # 利息覆盖倍数
})

# 生成违约标签(用真实逻辑模拟)
log_odds = (2*data['leverage'] - 1.5*data['liquidity'] 
            - 3*data['profitability'] - 0.1*data['size'] 
            - 0.5*data['coverage'] + 1)
prob = 1 / (1 + np.exp(-log_odds))
data['default'] = np.random.binomial(1, prob)

# 拟合Logit模型
X = data[['leverage', 'liquidity', 'profitability', 'size', 'coverage']]
X = sm.add_constant(X)
y = data['default']

model = sm.Logit(y, X)
result = model.fit()
print(result.summary())

# 预测PD
data['predicted_pd'] = result.predict(X)
print(f"预测PD范围: [{data['predicted_pd'].min():.4%}, {data['predicted_pd'].max():.4%}]")

关键点:Logit模型的好坏,90%取决于特征工程。我见过太多人直接把20个财务指标扔进去跑回归,结果模型过拟合得一塌糊涂。我建议先做单变量分析,筛选出5-8个有经济含义的指标,再考虑交互项和非线性项。

选哪些指标?我一般会从五个维度入手:

  • 杠杆维度:资产负债率、负债/EBITDA
  • 流动性维度:流动比率、速动比率
  • 盈利维度:ROA、ROE、毛利率
  • 规模维度:总资产对数、营业收入对数
  • 覆盖维度:利息覆盖倍数、现金流/总负债

我曾经给一家城商行做小微企业信用评分卡,一开始模型AUC只有0.65,怎么调都不行。后来发现,问题出在数据质量上——很多小微企业的财务报表是「做」出来的,不是「算」出来的。后来我们加入了税务数据水电费数据作为替代指标,AUC直接跳到0.82。嗯,数据比模型重要,这句话在信用风险领域尤其适用。

3.4 三种方法怎么选?

我个人的经验法则:

场景 推荐方法 理由
有长期历史违约数据 历史违约率法 简单透明,监管认可
上市公司,有市场数据 Merton模型 前瞻性强,每日更新
非上市公司,有财务数据 Logit/Probit回归 灵活,可加入多维度因子
需要监管合规(巴塞尔) 历史违约率法 + Logit 历史法做基准,Logit做区分

实际项目中,我很少只用一种方法。比如给一家股份制银行做内部评级体系时,我们用了Merton模型做上市公司PDLogit模型做非上市公司PD,然后用历史违约率法做校准——把模型输出的PD映射到历史平均违约水平上,保证长期均值合理。

一个小技巧:无论用哪种方法,最终PD都要做周期调整。我习惯用「穿越周期法」(Through-the-Cycle, TTC),把PD平滑掉短期波动,这样在信贷审批时更稳定,不会因为市场一波动就大幅调整信贷政策。

好了,三种方法都聊完了。记住一句话:没有最好的模型,只有最合适的模型。数据条件、业务场景、监管要求,这三者决定了你该选哪个。下次你遇到PD估计的问题,不妨先问问自己:我有什么数据?我要预测什么场景?监管认不认?想清楚这三个问题,方法自然就出来了。


公众号:蓝海资料掘金营,微信deep3321