All Articles
Credit Risk
Advanced
7 min readJuly 21, 2025

Modeling PDs Using the Vasicek Framework in Python: From Theory to Real-World Impact

3716
#Vasicek Model#PD Modeling#Credit Risk#IFRS 9#Stress Testing#Python#Basel III#Risk Management#Quantitative Finance#GCC Markets#Low Default Portfolio#scipy#Asset Correlation#Financial Modeling#Banking#Risk Analytics

Probability of Default (PD) is the cornerstone of credit risk modeling. Among various techniques, the Vasicek one-factor model stands out for its theoretical rigor and regulatory alignment (e.g., Basel II/III IRB). It links portfolio-level credit risk to macroeconomic and systemic factors, making it especially valuable for low-default portfolios. In this article, we'll break down how to compute Vasicek PDs in Python, interpret the results, and highlight real-world applications and what can go wrong when it's misused.


Step-by-Step Vasicek PD Estimation

Step 1: Theoretical Foundation

The Vasicek model assumes that a borrower defaults when a latent variable (representing creditworthiness) falls below a threshold. This latent variable is modeled as a combination of:

  • A systematic factor (macroeconomic driver)
  • An idiosyncratic component (borrower-specific)

Mathematically, the conditional PD is:

PD(Z) = Φ((Φ⁻¹(PD_avg) + √ρ · Z) / √(1 − ρ))

Where:

  • Φ is the standard normal CDF
  • ρ is asset correlation
  • Z is the systemic risk factor (e.g., percentile of macro scenario)

Step 2: Python Implementation

import numpy as np
from scipy.stats import norm

def vasicek_pd(pd_avg, rho, z_macro):
    """
    Compute Vasicek conditional PD under macroeconomic stress.

    pd_avg : float - long-run average PD
    rho : float - asset correlation
    z_macro : float - macro shock (e.g., -1.0 for mild recession)
    """
    inverse_cdf = norm.ppf(pd_avg)
    conditional_pd = norm.cdf((inverse_cdf + np.sqrt(rho) * z_macro) / np.sqrt(1 - rho))
    return conditional_pd

# Example:
pd_long_run = 0.02      # 2% average PD
rho = 0.15              # asset correlation
z_stress = -2.0         # macro shock (2 std deviations below mean)

vasicek_stressed_pd = vasicek_pd(pd_long_run, rho, z_stress)
print(f"Stressed PD under macro shock: {vasicek_stressed_pd:.4f}")

Step 3: Interpretation

  • The conditional PD increases during systemic downturns (as Z becomes negative), showing the procyclicality of credit risk.
  • Regulators prefer this model as it incorporates macroeconomic linkage and portfolio granularity.
  • A rising conditional PD signals need for higher provisions and capital buffers.

Real-World Application

  • IFRS 9 Stress Testing: Use Vasicek for forward-looking PDs under base, adverse, and severe scenarios.
  • Low-Default Portfolios: Retail or sovereign exposures with no meaningful default history benefit from this structural approach.
  • Credit Portfolio Risk Management: Vasicek helps simulate portfolio-wide default waves under correlated shocks.

Consequences of Doing It Wrong

  • Overestimating Correlation (ρ): Results in inflated PDs and excessive capital allocation, hurting profitability.
  • Ignoring Macro Factors (Z): Leads to static PDs that ignore economic cycles, a key compliance risk under IFRS 9 or IRB.
  • Poor Calibration: If long-run PDs are wrong or Z is not aligned with actual economic scenarios, model output becomes misleading.

Final Thoughts

Vasicek's model isn't just an academic exercise — it's a strategic tool. When calibrated carefully and integrated with macro scenarios, it helps financial institutions bridge quantitative rigor with economic realism. For the GCC region or frontier markets where defaults are rare but risks are real, Vasicek PD modeling is often the most viable choice for defensible risk measurement.