项目

一般

简介

03-全链路企业级算力防线蓝图 » 历史记录 » 修订 2

修订 1 (Huarui Lin, 2026-04-24 09:26) → 修订 2/14 (Huarui Lin, 2026-04-24 09:29)

# # 全链路企业级算力防线蓝图 (严格 0-7 阶段版) 

 
 ## 第 0 阶段:项目脚手架与 CI 基线 

 
 本阶段是全链路的“宪法”,不写任何业务逻辑,仅确立物理约束与拦截规则。 

 
 ### Step 0.1: 企业级目录拓扑与依赖隔离 

 
 1.    **空间隔离**:建立 `src/core`(纯算子,零业务逻辑)、`src/pipelines/s2_to_s5`(离线训练流水线编排)、`src/api/s6_inference`(在线微服务)、`src/backtest`(回测对接层)、`configs`、`tests` 的强约束目录。`src/core` 内层严禁使用 `from .xxx import *`,4个核心模块必须建立独立子目录,且在各自的 `__init__.py` 中显式白名单导出,防止跨层逆向导入导致的 DRY 拓扑破坏。 
 2.    **依赖锁死**:在 `pyproject.toml` 中精确锁定 `polars`, `duckdb`, `lightgbm`, `numpy`, `scipy`, `fastapi`, `pydantic` 的大版本号,彻底封杀上游破坏性更新引发的静默精度漂移。【依赖纯净化红线】 严禁引入实验追踪库(如 mlflow、weights&biases)与超参搜索库(如 optuna、ray[tune])。规约 S5.2 已在拓扑层面彻底否决超参搜索,此类依赖仅会污染 Docker 镜像层与增加无用编译时间。 

 
 ### Step 0.2: 配置中心统一收敛 

 
 1.    将 D-1 基金类型白名单、MAD 裁剪系数 `1.4826` 与宽容度倍数 `5.0`、Spearman 阈值 `0.90` 全部沉淀于 `config.yaml`。 
 2.    建立 `core/config_loader.py`,启动时做 Pydantic 强校验,缺失配置直接 `sys.exit(1)` 阻断。 

 
 ### Step 0.3: CI/CD 红线拦截器部署 (强制门禁) 

 
 在 Git 流水线中植入四大静态/动态拦截器: 
 1.    **Pandas 零容忍**:基于 AST 语法树扫描,发现 `import pandas` 直接报错阻断。 
 2.    **Schema 对称差拦截**:拉起极小 Mock 流水线,使用 Pydantic 强校验 S2/S3/S4 产出的列名集合与硬编码集合的对称差,差集非空直接阻断。 
 3.    **底层算子金色单测**:使用预生成的精确 `.npy` 基线,比对底层 `lfilter` (EWM) 与 OLS 算子输出,浮点误差越过 `1e-7` 直接阻断。 
 4.    **工程治理正则扫描**:全局拦截魔法数字(如 `1.4826` 必须来自 config)、违规 API(如 `maintain_order=True`、非安全池内的 `partition_by`)。 

 
 --- 
 ## 第 1 阶段:S2 - 数据血缘与清洗拓扑 

 
 面对 2800 万行原始净值,走“DuckDB 粗筛压榨 -> Polars 流式微操 -> Out-of-core 物理落盘”降维路径。 

 
 ### Step 1.1: DuckDB 纯粗筛算子防火墙 

 
 1.    **底层转码**:Polars 读取原始 CSV,强制指定类型(`fund_id` 为 `Categorical`,日期为 `Date32`),落盘为原生 Parquet 数据湖。严禁 DuckDB 直接对抗 CSV。 
 2.    **D-1 全局物理下推**:在 DuckDB 内基于配置白名单过滤基础信息,提取合法 `fund_id` 集合。 
 3.    **周频无状态聚合**:利用唯一合法的 SQL 实体(基于 `1970-01-05` 基准的纯数学整数除法对齐周一),Inner Join 后按周取组内 `max(date)` 对应净值。粗筛产物导出为极小的 `s2_raw_weekly.parquet`,关停 DuckDB。 

 
 ### Step 1.2: Polars 外层 Fund 隔离与长缺失切断 

 
 1.    **长缺失切断判定**:按 `fund_id` 分组,计算相邻两行真实数据周日期差值。当差值绝对值 > 5 时,使用累加逻辑为每个连续物理块打上递增的 `segment_id`。 

 
 ### Step 1.3: 内层 Segment 绝对闭包与短缺失 5 步强制顺序 

 
 1.    **微型连续日历生成**:闭包内提取起止日期,利用纯整数差值生成无缺失周序列,Left Join 标记 `is_original`。 
 2.    **首行越界防御**:对净值列 `forward_fill()`,提取闭包首行,若为 NULL 立即抛出 `SegmentFirstRowNullError` 阻断流水线。 
 3.    **血缘切断重算收益率**:基于纯净净值序列向量化重算全量 `weekly_return`。 
 4.    **占位行收益率置 NULL**:执行掩码,将 `is_original=False` 的行收益率强制置为 NULL,彻底切断 0.0 幻影收益向下游 `rolling_std` 的传播。 

 
 ### Step 1.4: 内存尖峰拦截与按年 Out-of-core 落盘 

 
 1.    **LazyFrame 谓词下推**:年份循环内,在 Lazy 状态按年过滤,完美命中 Parquet Row Group 统计信息。 
 2.    **物理排序死锁约束落地**:链式调用 `.sort(by=["net_value_date", "fund_id"], maintain_order=False)`。 
 3.    **零内存膨胀落盘**:对接 `sink_parquet()`,强制注入 `row_group_size=100000`, `compression="zstd"`, `compression_level=7`。产出 `s2_YYYY.parquet`,循环内就地 `del` 释放。 

 
 --- 
 ## 第 2 阶段:S3 - 特征工程 

 
 全链路算力雪崩最高危区,走“按年安全池 -> 纯 NumPy 24 核微线程 -> 物理哨兵封堵”极限拓扑。 

 
 ### Step 2.1: S3 入口按年流式与局部安全池桥接 

 
 1.    单年数据全量拉入安全沙箱(< 3GB)。 
 2.    执行 `partition_by(["fund_id", "segment_id"], as_dict=True)`,零拷贝打散为字典视图传递给 C 层。 

 
 ### Step 2.2: 纯 NumPy C 算子微线程拓扑 

 
 1.    实例化 24 核 `ThreadPoolExecutor` 分发字典值。 
 2.    GIL 防线:内层函数第一步必须 `to_numpy().flatten()` 抽出纯连续内存数组。 

 
 ### Step 2.3: A/B/C 类特征与分母隔离防线 

 
 1.    **分母隔离**:所有 12/26/52 周窗口计算,入口判断 `len(arr) < window`,不满足直接输出 `None`。 
 2.    **A/B/C 类向量化**:使用 `cumsum` 截取法算均值偏离;`np.minimum(ret, 0)` 屏蔽正收益算下行波动;`cummax` 位移算最大回撤;使用 `is_start` 与 `cum_arr` 的 O(N) 骨架算连阴周数。 

 
 ### Step 2.4: OLS 矩阵击穿与 EWM 封堵 

 
 1.    **OLS 骨架**:提取 `np.log(nav)`,O(1) 公式算分母 `ss_xx`,纯 `np.dot` 算分子,斜率强制乘 `52` 年化。封杀 `lstsq`。 
 2.    **EWM 骨架**:转换为 `scipy.signal.lfilter` 分子分母系数,利用 C 递推短暂释放 GIL。 

 
 ### Step 2.5: D/E 类特征与 NULL 穿透 

 
 1.    **D 类定投**:分母强制修正为带窗口期的 `rolling_mean(nav, X)`,计算成本偏离与胜率。 
 2.    **E 类风险**:显式写出 `(mean_ret - weekly_rf) / std_ret` 结构。 
 3.    **Regime NULL**:上市不足 52 周导致分母为 NULL 时,直接输出 NULL,不做任何 `fill_null(0)` 替换,任由其触发哨兵。 

 
 ### Step 2.6: 物理哨兵生成与落盘 

 
 1.    横向非空判定生成 `is_feature_complete`。 
 2.    复用全局 `sink_parquet` 拓扑产出 `s3_YYYY.parquet`。 

 
 --- 
 ## 第 3 阶段:S4 - 标签生成 

 
 业务逻辑核心,防“毒特征污染”与“索引错位”。 

 
 ### Step 3.1: S4 绝对隔离加载 

 
 1.    `scan_parquet` 扫描 S2 产物,立即 `.select(["fund_id", "net_value_date", "segment_id", "cumulative_net_value"])`。严禁拉入 `weekly_return`。 

 
 ### Step 3.2: DCA 闭式向量化公式 

 
 1.    断言 `nv_array` 纯净且大于 0。 
 2.    倒数累加范式:`inv_nv -> cum_inv -> weeks -> returns`,全程无循环。 

 
 ### Step 3.3: 止盈边界与 0-based 转 1-based 拦截 

 
 1.    `np.where(returns >= 0.20)[0]` 命中检索。 
 2.    索引偏移修正:`label = int(hit_indices[0] + 1) if len > 0 else 0`。 
 3.    绝对边界断言:`assert 0 <= label <= 150`。 

 
 ### Step 3.4: 按年落盘 

 
 复用全局工具函数产出 `s4_YYYY.parquet`。 

 
 --- 
 ## 第 4 阶段:S5 - 标准化与 Spearman 降维 

 
 融合“截面数学推导”与“内存防爆累加”。 

 
 ### Step 4.1: 截面聚合与双形态参数物理时序隔离 

 
 1.    **纯横截面聚合**:按 `net_value_date` 分组计算 38 维的 `mean, std, median, mad`,推导 MAD 上下界,生成 `is_production_ready` 哨兵。 
 2.    **路径 A (训练宽表)**:宽表上 `.shift(1)` 实现 T-1 对齐,按日期排序落盘。 
 3.    **路径 B (推理长表)**:Unpivot 为长表,动态生成自然日历 `forward_fill` 至每日,按 `["feature_name", "date"]` 绝对物理排序落盘。 

 
 ### Step 4.2: 标准化 Apply 与 IEEE 754 拦截 

 
 1.    实体表 `.collect()` 后与几百行宽表参数 Join(零内存膨胀广播)。 
 2.    遍历 38 列构建 `when(std==0).then(None).otherwise(...)` 表达式,封杀除零产生 `Inf`。 

 
 ### Step 4.3: 训练 Join 三键死锁与毒样本截断 

 
 1.    三键强锁 Join:`["fund_id", "net_value_date", "segment_id"]`。 
 2.    哨兵覆写:`is_feature_complete == False` 的行,标签强制覆写为 `0`。 

 
 ### Step 4.4: Spearman 秩相关内存防爆防线 

 
 1.    按年流式加载标准化特征。 
 2.    截面内 `rankdata(method="average")` 锁定平均秩,算秩后立即减均值。 
 3.    Pearson 展开项 `np.dot` 联合累加 `sum_xy`,利用对称性减半算力。 
 4.    跨年累加后执行 `(matrix + matrix.T) / 2.0` 强制对称化截断。 
 5.    剔除 `>= 0.90` 的冗余特征,输出 `final_feature_list`。 

 
 --- 
 ## 第 5 阶段:S5.3 - 训练流水线 

 
 将降维后的纯净特征转化为 LightGBM LambdaRank 可消费的形态并进行闭环重训。 

 
 ### Step 5.1: LambdaRank 样本平衡重构 

 
 1.    **Group 划分**:全量表按 `net_value_date` 排序,每个日期即一个 Query Group。 
 2.    **差异化等距抽样**:正样本池(`label>0`)用 `np.linspace` 抽最多 50 条;负样本池抽最多 15 条。 
 3.    **相关性得分映射**:`label=0` 映射为 `0`,`label=1~150` 映射为 `151 - label`(第 1 周得 150 分)。 
 4.    **Group 边界构建**:记录每个截面抽样数,构建一维数组传给 LightGBM 的 `group` 参数。 

 
 ### Step 5.2: Time-Series 5 折评估防线 

 
 1.    时间切分:按 20% 步长划分 5 个验证区块,使用累积扩展窗口。 
 2.    **NDCG@10 绝对核心**:训练传入 `group_boundaries`,评估强制锁定 `NDCG@10` 指标,直接对齐 Top 10 榜单业务 KPI。 
 3.    **防穿透锁定**:5 折期间严禁再次剔除特征或修改任何超参,仅观察指标分布。 

 
 ### Step 5.3: 闭环全量重训与产物契约落盘 

 
 1.    使用全量抽样数据 + `final_feature_list` 进行最终无截断训练。 
 2.    **产物强制打包**:将 `model.txt` 与两张标准化参数表打入同一个带时间戳的发布目录(如 `artifacts/v_20231024/`)。 
 3.    **MD5 强校验**:生成模型文件的 MD5 摘要,作为后续 S6 加载前的防篡改门禁。 

 
 --- 
 ## 第 6 阶段:S6 - 推理服务 

 
 纯粹的“无状态数学计算引擎”,彻底剥离所有 I/O 职责。 

 
 ### Step 6.1: FastAPI 骨架与进程级全局缓存防抖 

 
 1.    **生命周期加载**:在 `lifespan` 启动钩子中,读取发布目录产物,加载 `_PARAMS_LONG_CACHE` (长表)、`_AVAIL_DATES_CACHE` (日期数组)、`_MODEL` (Booster)。 
 2.    **MD5 门禁校验**:加载模型前比对 MD5,不一致直接 `os._exit(1)` 拒绝启动。 

 
 ### Step 6.2: 两步走时间锚定防线 

 
 1.    **第一步锚定**:接收单维 `request_date`,与 `_AVAIL_DATES_CACHE` 执行极速 `asof_join` 寻找 `valid_date`。无效则短路返回空结构体。 
 2.    **第二步拉取**:基于 `final_feature_list` 构建左表,按 `["feature_name", "valid_date"]` 排序,与 `_PARAMS_LONG_CACHE` 执行双键 `asof_join`,后置过滤 `is_production_ready`。 

 
 ### Step 6.3: 2D 矩阵级向量化批处理内核 

 
 1.    **统一抽象**:无论单基金(N=1)还是批量(N=1000),统一接收 `(N, 38)` 原始特征 2D 矩阵。 
 2.    **复用 S5 内核**:调用 `core.standardization_math.apply_zscore`,利用 NumPy 广播一次性完成 N 行的 MAD 裁剪与 Z-Score 转换。 
 3.    **矩阵预测**:`_MODEL.predict(matrix)`,利用 C++ 底层极速输出得分。 

 
 ### Step 6.4: 接口物理隔离与归因红线 

 
 1.    **单基金接口**:允许调用 `predict_contrib` 返回 SHAP 归因明细。 
 2.    **批量接口**:架构级禁止引入 `predict_contrib` 代码,归因字段强制硬编码为空字符串 `""`。 

 
 --- 
 ## 第 7 阶段:端到端集成与打通 

 
 这是将离线数学模型转化为线上业务价值的最终闭环,解决“产物交接、预热、回测对接、极限压测”四大工程鸿沟。 

 
 ### Step 7.1: 产物自动化交接与灰度发布契约 

 
 1.    **CI/CD 打包流**:S5 训练流水线成功后,CI 自动将 `artifacts/v_xxx/` 打包为 Docker Image 或推送到 OSS/Minio。 
 2.    **S6 滚动更新**:K8s/ Docker Compose 执行滚动更新。新 Pod 启动时拉取最新产物进行 MD5 校验与预热,校验失败则 Pod 启动失败,旧 Pod 继续服役,实现无损回滚。 

 
 ### Step 7.2: S6 冷启动掩盖与 Health Check 强断言 

 
 1.    **就绪探针**:在 FastAPI 中实现 `/health` 接口。不仅返回 200,内部必须验证 `_MODEL is not None` 且 `_PARAMS_LONG_CACHE.shape[0] > 0`。未加载完毕前,K8s 绝不将流量打入该 Pod。 
 2.    **预热请求**:Pod 启动后,后台线程自动发起一次包含 10 只基金的矩阵预测请求,强制触发 LightGBM 内部树的 JIT 缓存预热,掩盖首次真实请求的微秒级延迟毛刺。 

 
 ### Step 7.3: 回测引擎标准化数据对接 

 
 1.    **定义回测接口契约**:S6 批量接口不直接服务 C 端,而是作为 BFF 层被回测引擎(如 Backtrader/Qlib)调用。 
 2.    **历史截面回放**:回测引擎按历史日期循环,将历史 T 日全市场基金原始特征组装为 `(N, 38)` 矩阵,调用 S6 获取 T 日得分排名。 
 3.    **严格按照 NDCG@10 验证**:回测引擎拿到 Top 10 池后,向后看 150 周计算真实定投收益率。对比回测出的“Top 10 平均达标率”与 S5 验证集的“NDCG@10 曲线”,两者必须呈现强正相关,否则证明存在数据穿越 Bug。 

 
 ### Step 7.4: 全链路极限压测与 cgroup 防雪崩验证 

 
 1.    **流量炮台构建**:使用 Locust 编写压测脚本,模拟 50 并发线程,每线程持续发送 N=1000 的批量推荐请求。 
 2.    **P99 延迟红线**:监控 S6 服务的 P99 响应时间,必须死锁在 `< 50ms` 以内。一旦超限,说明触犯了矩阵广播外的隐性锁或内存交换。 
 3.    **内存防雪崩观察**:在压测期间,通过 `docker stats` 严格监视 S6 容器的内存曲线。因为 S6 是纯矩阵运算无中间态膨胀,其内存曲线必须是一条绝对水平的直线。任何呈阶梯状上升的现象,都证明存在隐藏的内存泄漏或未触发全局缓存的灾难逻辑,必须阻断上线。