3. 坏账率定义与计算:逐笔坏账、日终坏账、滚动坏账率及高频场景下的特殊处理
在实时风控体系中,坏账率是衡量资产质量与模型效果的核心指标。高频行情场景下,传统T+1或月结坏账率无法满足秒级预警需求。本章将系统定义三种坏账率计算口径,并重点阐述高频场景下的特殊处理逻辑。
3.1 坏账率基础定义
坏账率(Bad Rate)指在特定时间窗口内,被认定为“坏账”的资产占总资产的比例。根据认定时点与计算口径不同,分为以下三种核心类型:
| 类型 | 定义 | 计算窗口 | 适用场景 |
|---|---|---|---|
| 逐笔坏账率 | 单笔订单在生命周期内被标记为坏账的比率 | 订单生命周期(如30天/90天) | 模型训练标签、单笔风险评估 |
| 日终坏账率 | 每日收盘后,当日所有存续订单中已确认为坏账的比例 | 自然日(T日收盘) | 日报、监管报送、日度风控 |
| 滚动坏账率 | 连续滚动窗口内(如近7天/30天)新增坏账金额与同期新增放款金额的比值 | 滚动时间窗(如7日、30日) | 实时监控、趋势预警、模型效果追踪 |
讲师提示: 在实时风控中,滚动坏账率是最常用的监控口径。它能够平滑单日波动,同时保持对趋势变化的敏感度,适合作为XGBoost哨兵模型的预警目标变量。
3.2 逐笔坏账率计算
逐笔坏账率是微观层面的基础指标,通常用于模型训练标签的生成。其计算逻辑如下:
- 定义坏账标准: 逾期超过特定天数(如M3+:逾期90天以上)或触发特定风险事件(如失联、欺诈确认)。
- 跟踪订单生命周期: 从放款日开始,持续跟踪至订单结清或达到坏账认定标准。
- 计算单笔标签: 若订单在生命周期内被认定为坏账,则标签为1;否则为0。
- 汇总统计: 逐笔坏账率 = 坏账订单数 / 总订单数 × 100%。
# 逐笔坏账率计算伪代码
def calculate_loan_bad_rate(loan_df, bad_definition_days=90):
"""
loan_df: 包含 loan_id, disbursed_date, status, overdue_days
bad_definition_days: 坏账认定逾期天数阈值
"""
loan_df['is_bad'] = (loan_df['overdue_days'] >= bad_definition_days) | \
(loan_df['status'] == 'bad_debt')
bad_count = loan_df['is_bad'].sum()
total_count = len(loan_df)
bad_rate = bad_count / total_count
return bad_rate
3.3 日终坏账率计算
日终坏账率是静态截面指标,反映某一时点的资产质量状况。计算步骤如下:
- 确定统计时点: 通常为每日收盘时间(如16:00)。
- 筛选存续订单: 所有未结清且未核销的订单。
- 认定坏账: 截至统计时点,逾期天数超过坏账阈值的订单。
- 计算比率: 日终坏账率 = 坏账余额 / 总存续余额 × 100%。
注意: 日终坏账率受“分母效应”影响较大。当新增放款量激增时,分母扩大可能导致坏账率被稀释,掩盖真实风险。因此,在高频行情场景下,日终坏账率通常作为辅助参考,而非主要预警指标。
3.4 滚动坏账率计算
滚动坏账率是高频场景下的核心指标,能够有效消除分母效应,反映近期放款质量。其计算公式为:
滚动坏账率 = (滚动窗口内新增坏账金额) / (滚动窗口内新增放款金额) × 100%
具体计算流程:
- 设定滚动窗口: 常见窗口为7天、15天、30天。
- 计算新增坏账: 在窗口期内,首次达到坏账认定标准的订单的放款金额。
- 计算新增放款: 在窗口期内,所有新增放款的金额。
- 滚动更新: 每次新数据到达,窗口向前滑动,重新计算。
# 滚动坏账率计算示例(Python)
def rolling_bad_rate(transactions_df, window_days=30):
"""
transactions_df: 包含 trans_date, loan_amount, is_bad (是否已认定为坏账)
"""
# 按日期排序
df = transactions_df.sort_values('trans_date')
# 计算滚动窗口内的新增放款总额
df['rolling_disbursed'] = df['loan_amount'].rolling(
window=window_days, min_periods=1).sum()
# 计算滚动窗口内的新增坏账总额
df['rolling_bad'] = df['loan_amount'][df['is_bad'] == 1].rolling(
window=window_days, min_periods=1).sum()
# 计算滚动坏账率
df['rolling_bad_rate'] = df['rolling_bad'] / df['rolling_disbursed']
return df
3.5 高频场景下的特殊处理
在毫秒级高频行情场景中,传统坏账率计算面临三大挑战:延迟认定、数据稀疏、计算时效。以下是针对性的特殊处理方案:
| 挑战 | 问题描述 | 高频处理方案 |
|---|---|---|
| 延迟认定 | 坏账认定需要等待逾期天数达到阈值(如90天),导致指标滞后 | 使用代理标签:如早期逾期率(DPD1+、DPD7+)作为坏账的近似替代,训练XGBoost模型预测最终坏账概率 |
| 数据稀疏 | 高频切片(如每分钟)内坏账事件极少,导致坏账率为0或剧烈波动 | 采用累积窗口与指数加权移动平均(EWMA),平滑稀疏数据,同时保留趋势信号 |
| 计算时效 | 全量数据重算滚动坏账率耗时较长,无法满足秒级响应 | 使用增量计算:维护窗口内放款总额与坏账总额的累加器,新数据到达时仅更新边界值,避免全量扫描 |
实战要点: 在XGBoost哨兵模型中,我们通常使用EWMA平滑后的滚动坏账率作为目标变量。平滑系数α一般设为0.1~0.3,在敏感度与稳定性之间取得平衡。同时,配合代理标签(如7天逾期率)进行模型训练,可提前30~60天发出预警信号。
3.6 高频坏账率计算架构
下图展示了高频行情场景下,坏账率计算的实时数据流与处理架构:
3.7 三种坏账率对比与选型建议
在实际项目中,应根据监控目标与数据频率选择合适的坏账率口径。下表总结了三种口径的对比:
| 维度 | 逐笔坏账率 | 日终坏账率 | 滚动坏账率 |
|---|---|---|---|
| 计算粒度 | 单笔订单 | 日频截面 | 滚动窗口 |
| 时效性 | 低(需等待生命周期结束) | 中(T+1) | 高(实时更新) |
| 平滑性 | 不适用 | 受分母效应影响大 | EWMA平滑后稳定 |
| 高频适用性 | 不适合 | 辅助参考 | 核心指标 |
| 模型训练用途 | 标签生成 | 离线评估 | 实时特征与目标变量 |
选型建议: 在《高频行情坏账率哨兵》课程项目中,我们采用滚动坏账率(30天窗口,EWMA平滑)作为XGBoost模型的实时监控目标。同时,使用逐笔坏账率生成训练标签,日终坏账率作为日报与监管报送口径。三者协同,构建完整的坏账率监控体系。