3、违约概率(PD)估计:历史违约率法、Merton模型、Logit/Probit回归模型
聊到信用风险,最核心的问题就一个:这个对手到底有多大概率会违约?
PD(Probability of Default)估计,说白了就是回答这个问题。我做了这么多年量化,见过太多人把PD当成一个静态数字来用,结果模型一上线就崩。其实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模型做上市公司PD,Logit模型做非上市公司PD,然后用历史违约率法做校准——把模型输出的PD映射到历史平均违约水平上,保证长期均值合理。
一个小技巧:无论用哪种方法,最终PD都要做周期调整。我习惯用「穿越周期法」(Through-the-Cycle, TTC),把PD平滑掉短期波动,这样在信贷审批时更稳定,不会因为市场一波动就大幅调整信贷政策。
好了,三种方法都聊完了。记住一句话:没有最好的模型,只有最合适的模型。数据条件、业务场景、监管要求,这三者决定了你该选哪个。下次你遇到PD估计的问题,不妨先问问自己:我有什么数据?我要预测什么场景?监管认不认?想清楚这三个问题,方法自然就出来了。
公众号:蓝海资料掘金营,微信deep3321