在信贷风控、消费金融等领域,Vintage 分析是评估资产质量的核心方法。它通过追踪同一时期发放的贷款在不同账龄下的表现,帮助从业者判断风险趋势、优化风控策略。本文将从 Vintage 的基础概念出发,结合 Python 实战案例,手把手教你实现 Vintage 指标的自动化计算。
一、什么是 Vintage?
Vintage 直译是 “年份”,在金融领域特指同一时期(如同一月份)发放的贷款组合,随着时间推移(账龄增长)所表现出的风险特征。例如 “2024 年 1 月 Vintage” 即指 2024 年 1 月发放的所有贷款,在放款后第 1 个月、第 2 个月…… 第 N 个月的逾期率、不良率等指标。
核心价值:
- 消除不同时期放款政策差异对风险评估的干扰
- 客观对比不同批次贷款的长期表现
- 提前预警潜在的资产质量恶化趋势
二、Vintage 核心计算公式
Vintage 分析的核心是账龄(MOB,Month on Book) 和风险指标的对应关系,常用公式如下:
2.1 账龄(MOB)计算
账龄(MOB)= 观察月份 - 放款月份 + 1 |
例如:2024 年 1 月放款的贷款,在 2024 年 1 月观察时 MOB=1,2024 年 2 月观察时 MOB=2,以此类推。
2.2 关键风险指标
- 逾期率(M1+):某 Vintage 在第 MOB 月时,逾期 1 期及以上的贷款金额(或笔数)占总放款金额(或笔数)的比例
M1+逾期率 = (MOB月逾期1期及以上金额) / 该Vintage总放款金额 × 100% |
- 不良率:通常定义为逾期 90 天及以上(M3+)的贷款占比,公式类似逾期率,仅调整逾期天数阈值。
三、Python 计算 Vintage 的完整流程
3.1 数据准备
假设我们有一份贷款数据(loan_data.csv),包含以下字段:
- loan_id:贷款编号
- issue_date:放款日期(格式:YYYY-MM-DD)
- amount:放款金额
- overdue_days:截至观察日的逾期天数
- obs_date:观察日期(格式:YYYY-MM-DD)
用 pandas 读取数据:
import pandas as pd import numpy as np from datetime import datetime # 读取数据 df = pd.read_csv('loan_data.csv') # 转换日期格式 df['issue_date'] = pd.to_datetime(df['issue_date']) df['obs_date'] = pd.to_datetime(df['obs_date']) # 查看数据结构 print(df.head()) |
3.2 计算账龄(MOB)
通过放款日期和观察日期计算账龄:
# 提取放款年月和观察年月 df['issue_ym'] = df['issue_date'].dt.to_period('M') # 格式:2024-01 df['obs_ym'] = df['obs_date'].dt.to_period('M') # 计算账龄(MOB) df['mob'] = (df['obs_ym'] - df['issue_ym']).apply(lambda x: x.n + 1) # n为月份差,+1得到MOB # 过滤无效数据(观察日期早于放款日期的记录) df = df[df['mob'] >= 1] |
3.3 定义风险指标
根据逾期天数标记逾期状态,并计算各 Vintage 在不同 MOB 的指标:
# 标记逾期状态(M1+:逾期≥30天,M3+:逾期≥90天) df['is_m1'] = df['overdue_days'] >= 30 df['is_m3'] = df['overdue_days'] >= 90 # 按Vintage(放款年月)和MOB分组,计算指标 vintage_summary = df.groupby(['issue_ym', 'mob']).agg( total_amount=('amount', 'sum'), # 该Vintage在该MOB的总放款金额 m1_amount=('amount', lambda x: (x * df.loc[x.index, 'is_m1']).sum()), # M1+逾期金额 m3_amount=('amount', lambda x: (x * df.loc[x.index, 'is_m3']).sum()) # M3+逾期金额 ).reset_index() # 计算逾期率 vintage_summary['m1_rate'] = vintage_summary['m1_amount'] / vintage_summary['total_amount'] * 100 vintage_summary['m3_rate'] = vintage_summary['m3_amount'] / vintage_summary['total_amount'] * 100 print(vintage_summary.head()) |
3.4 生成 Vintage 矩阵
将结果转换为 “Vintage - 账龄” 矩阵,方便可视化分析:
# 构建M1+逾期率矩阵(行:Vintage,列:MOB) m1_matrix = vintage_summary.pivot( index='issue_ym', columns='mob', values='m1_rate' ).round(2) # 保留2位小数 print("M1+逾期率Vintage矩阵:") print(m1_matrix) |
输出示例:
mob 1 2 3 4 issue_ym 2024-01 1.20 2.50 3.80 4.20 2024-02 1.10 2.30 3.50 NaN 2024-03 1.05 2.10 NaN NaN |
(注:NaN 表示该 Vintage 尚未到达对应账龄,如 2024-03 的贷款在观察时最多到 MOB=2)
四、Vintage 可视化分析
用 matplotlib 绘制 Vintage 曲线,直观对比不同批次贷款的风险趋势:
import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(12, 6)) sns.set_style("whitegrid") # 绘制各Vintage的M1+逾期率曲线 for vintage in m1_matrix.index: mob_values = m1_matrix.loc[vintage].dropna().index rate_values = m1_matrix.loc[vintage].dropna().values plt.plot(mob_values, rate_values, marker='o', label=str(vintage)) plt.xlabel('账龄(MOB)') plt.ylabel('M1+逾期率(%)') plt.title('各Vintage逾期率趋势') plt.legend(title='放款年月') plt.grid(linestyle='--', alpha=0.7) plt.show() |
图表解读:
- 若某 Vintage 曲线陡峭上升,说明该批次贷款风险随账龄快速恶化
- 后期 Vintage(如 2024-03)的早期逾期率(MOB=1)低于前期,可能表明风控政策收紧
五、实战技巧与注意事项
5.1 数据清洗要点
- 剔除测试数据、无效放款(如金额为 0)
- 统一日期格式,避免因时区或格式错误导致 MOB 计算偏差
- 处理重复记录(同一贷款在多个观察日的记录需去重或按最新日期保留)
5.2 计算优化
- 大数据量时使用groupby+agg的向量化操作,替代循环遍历
- 对日期字段进行预处理(如转为年月周期),减少重复计算
- 用pivot_table替代pivot处理多指标矩阵生成
5.3 业务适配
- 根据行业特性调整逾期天数定义(如消费贷常用 M1=30 天,房贷可能用 M1=60 天)
- 对大额贷款可按金额加权计算指标,更贴合实际风险敞口
- 结合滚动率(Roll Rate)分析,判断逾期状态的迁移趋势
六、总结
Vintage 分析是信贷风险管理的 “透视镜”,而 Python 则是实现这一分析的高效工具。通过本文的步骤,你可以:
- 用 pandas 完成数据清洗与账龄计算
- 构建 Vintage 矩阵并生成风险指标
- 通过可视化直观对比不同批次贷款质量
掌握 Vintage 计算不仅能帮助企业监控资产质量,更能为产品迭代、风控策略调整提供数据支撑。实际应用中,建议结合业务场景灵活调整指标定义,并定期自动化运行计算流程,实现风险的动态追踪。