项目

一般

简介

CR-014-截面标准化引擎规约补全与工程执行顺序修正 » 历史记录 » 版本 1

Huarui Lin, 2026-04-18 20:38

1 1 Huarui Lin
# CR-014:截面标准化引擎规约补全与工程执行顺序修正
2
3
| 字段 | 内容 |
4
|------|------|
5
| **CR ID** | CR-014 |
6
| **标题** | 截面标准化引擎规约补全与工程执行顺序修正 |
7
| **发起人** | Henry Lin (PM) / 首席量化架构师 |
8
| **日期** | 2026-04-17 |
9
| **状态** | ✅ Approved |
10
| **影响范围** | 规约 4.3 节、`standardization.py`、`standardization_params.parquet` Schema、下游 Step 5/6 标准化复用逻辑 |
11
12
#### 1. 变更描述
13
14
原规约 4.3 存在数学逻辑矛盾及工程边界模糊,现予以刚性修正:
15
16
1. **MAD 去极值与 Z-Score 执行顺序倒置**
17
   * **原规约**:先算 Z-Score,再用原始值的 Median/MAD 裁剪 Z-Score。
18
   * **变更为**:先基于原始值计算 Median/MAD 并裁剪原始值,再用**裁剪后的原始值**计算 Mean/Std,最后执行 Z-Score。
19
   * **修正后四步走**:
20
     1. 计算截面原始值的 `mean`, `std`, `median`, `mad`。
21
     2. 时间对齐:整体 `shift(1)`。
22
     3. MAD 去极值:`raw_clipped = clip(raw_feature, T1_median - 3*T1_mad, T1_median + 3*T1_mad)`。
23
     4. Z-Score:`z_score = (raw_clipped - T1_mean_clipped_adjusted) / T1_std`。(*注:工程实现中,为保持极高向量化性能且误差可控,T1_mean 与 T1_std 仍使用步骤 1 基于未裁剪值计算的统计量*)。
24
2. **引入 `segment_id` 分组维度**
25
   * 截面统计量计算必须严格按 `["net_value_date", "segment_id"]` 分组,防止清盘重启等异质生命周期段污染统计分布。
26
3. **除零防御显式化**
27
   * 当 `T1_std == 0.0` 时(截面内所有基金特征值完全一致),该特征当期 Z-Score 强制输出 `NULL`,严禁产出 `inf`。
28
4. **“首日”严格物理定义**
29
   * “首日”定义为**全局数据集按时间排序后的绝对第一天截面**(非单基金成立日)。
30
   * **工程约束**:严禁按 `fund_id` 做 shift。必须按 `net_value_date` 聚合计算统计量,执行全局 `shift(1)`,最后 Join 回主表。新成立基金通过 Join 自然获取上一期全市场参数,不输出 NULL。仅全局绝对首日因无 T-1 而输出 NULL。
31
5. **持久化 Schema 强制长表**
32
   * `standardization_params.parquet` 必须严格采用长表 EAV 形态(Schema: `date`, `segment_id`, `feature_name`, `mean`, `std`, `median`, `mad`)。严禁转宽表存储。
33
34
#### 2. 变更原因(根因追溯)
35
36
1. **数学矛盾(致命)**:原规约步骤 4 直接用原始值量纲的区间去裁剪无量纲的 Z-Score,会导致 100% 的 Z-Score 被截断为边界常数,模型彻底失效。
37
2. **同质性污染**:如果不按 `segment_id` 分组,不同生命周期段的基金(如成立初期 vs 稳定期)会被混在一起计算均值,破坏截面同质性假设。
38
3. **数值稳定性**:停牌或极端行情下 `std=0` 产生的 `inf` 会绕过 LightGBM 的缺失值处理机制,导致预测崩溃。
39
40
#### 3. 影响分析
41
42
| 受影响模块 | 影响说明 | 应对措施 |
43
|-----------|---------|---------|
44
| `standardization.py` | 核心计算链路重构 | 严格按修正后四步走实现,使用 `pl.when(std > 0)` 防御 |
45
| `data_contract.md` | 参数表 Schema 增加 `segment_id` | 更新接口契约,明确 7 列长表结构 |
46
| `trainer.py` / `inference.py` | 参数加载与 Join 逻辑 | Join 键必须从 `date` 变更为 `["date", "segment_id"]` |