2. 回测系统概述:回测的目的、回测与实时监控的区别、回测数据源要求
回测系统是坏账率哨兵告警体系的核心验证环节。在将告警规则与阈值部署到生产环境之前,必须通过历史数据模拟运行,验证规则的有效性与稳定性。本章从回测的根本目的出发,厘清其与实时监控的本质差异,并明确回测数据源必须满足的硬性要求。
2.1 回测的核心目的
回测并非简单的“跑一遍历史数据”,而是为了回答以下三个关键问题:
- 规则有效性验证: 告警规则能否在坏账率恶化前准确触发?是否存在大量误报或漏报?
- 阈值合理性校准: 当前设定的阈值(如坏账率环比上升 20%)是否能在“足够早”与“不过于敏感”之间取得平衡?
- 策略稳定性评估: 规则在不同市场周期、不同客群结构下是否表现一致?是否存在过拟合风险?
核心原则: 回测的目标是 降低不确定性,而非证明规则完美。任何回测都无法完全预测未来,但可以显著减少“上线即失效”的风险。
2.2 回测与实时监控的区别
许多团队将回测视为“离线版的实时监控”,这是一个常见误区。两者在目标、数据流向、时效性要求上存在本质差异。下表从 6 个关键维度进行对比:
| 对比维度 | 回测系统 | 实时监控系统 |
|---|---|---|
| 核心目标 | 验证规则、调优阈值、评估策略 | 及时发现异常、触发告警、阻断风险 |
| 数据时间 | 历史数据(T-1 至 T-N) | 当前数据(T+0 或近实时) |
| 处理速度 | 允许分钟级至小时级批量计算 | 要求秒级或毫秒级流式处理 |
| 结果用途 | 规则迭代、阈值调整、上线决策 | 即时告警、工单生成、自动处置 |
| 容错要求 | 允许重跑、修正、对比多版本 | 要求高可用、低延迟、无单点故障 |
| 数据量级 | 全量历史数据(可能 TB 级) | 窗口内增量数据(通常 GB 级) |
实践建议: 回测系统与实时监控系统应 独立部署,但共享同一套规则引擎与指标计算逻辑。这样可以确保回测结果能够真实反映实时场景的表现,避免“回测通过、上线失效”的窘境。
2.3 回测数据源要求
回测结果的可靠性,高度依赖数据源的质量。低质量数据会导致“垃圾进、垃圾出”(Garbage In, Garbage Out)的后果。以下是回测数据源必须满足的 5 项硬性要求:
- 时间跨度充足: 至少覆盖 1 个完整业务周期(通常 12-24 个月),包含旺季、淡季、政策调整期等不同阶段。
- 字段完整性: 必须包含与告警规则相关的所有关键字段,如放款金额、逾期天数、还款记录、客群标签等。
- 数据一致性: 历史数据与当前生产数据在口径、计算逻辑上保持一致,避免因字段定义变更导致回测失真。
- 标签可追溯: 每笔资产在回测窗口内的最终状态(正常、逾期、核销)必须可追溯,支持多版本标签对比。
- 无未来信息泄露: 回测时只能使用“截至回测时间点”已知的信息,严禁使用未来数据(如使用 T+30 的还款结果来预测 T 日的坏账率)。
常见陷阱:未来信息泄露
在回测中,一个典型的错误是使用“当前已知的最终逾期状态”来标记历史数据。例如,在回测 2023 年 1 月的坏账率时,使用了 2023 年 6 月才确定的核销结果。这会导致回测结果过于乐观,无法反映真实的风险识别能力。
正确做法: 使用“滚动窗口”方式,每个回测时间点只使用该时间点之前已确定的状态数据。
在回测中,一个典型的错误是使用“当前已知的最终逾期状态”来标记历史数据。例如,在回测 2023 年 1 月的坏账率时,使用了 2023 年 6 月才确定的核销结果。这会导致回测结果过于乐观,无法反映真实的风险识别能力。
正确做法: 使用“滚动窗口”方式,每个回测时间点只使用该时间点之前已确定的状态数据。
2.4 回测数据准备示例
以下是一个简化的回测数据准备流程,使用 Python 伪代码展示如何避免未来信息泄露:
import pandas as pd
from datetime import datetime, timedelta
def prepare_backtest_data(raw_data, backtest_date):
"""
准备回测数据:只保留 backtest_date 之前已知的信息。
Parameters:
- raw_data: DataFrame,包含每笔资产的放款日期、逾期记录、最终状态
- backtest_date: datetime,回测截止日期
Returns:
- DataFrame,仅包含 backtest_date 前已知的状态
"""
# 只保留放款日期在 backtest_date 之前的资产
df = raw_data[raw_data['loan_date'] <= backtest_date].copy()
# 对于逾期记录,只保留逾期发生日期在 backtest_date 之前的记录
df['known_overdue'] = df.apply(
lambda row: row['overdue_days'] if row['overdue_date'] <= backtest_date else 0,
axis=1
)
# 最终状态:如果核销日期在 backtest_date 之后,则视为“未知”
df['known_status'] = df.apply(
lambda row: row['final_status'] if row['write_off_date'] <= backtest_date else 'pending',
axis=1
)
return df
# 示例调用
backtest_data = prepare_backtest_data(
raw_data=pd.read_parquet('loan_data.parquet'),
backtest_date=datetime(2023, 6, 30)
)
关键检查点: 在每次回测运行前,务必执行一次“未来信息泄露检查”。可以通过随机抽取 100 笔资产,人工核对回测使用的状态与当时实际可知的状态是否一致。这是回测质量保障的最后一道防线。