项目

一般

简介

行为

Sprint 2 规划会议纪要:数据基建与时序状态机护栏落地

会议日期:2026-04-15
项目名称:基金量化定投选基系统 (2026-04-11-01)
会议目标:锁定 M2 交付标准,对齐 DuckDB/Polars 混合架构边界,解决时序状态机边界逻辑冲突,确立实体机压测基线与 Redmine 强卡控验收标准。
参会角色:项目经理(Henry Lin)、算法/研发工程师、基础架构/运维工程师、数据资产管理员。


一、 会议确认项

  1. CONF-01 S2 绝对红线确立:全局严禁手写 import pandas(CI AST 拦截);严禁魔法数字(必须走 config/config.yaml 新增节点读取);DuckDB 严守粗筛边界(严禁执行滚动窗口逻辑)。
  2. CONF-02 FW-3 防火墙特例豁免决策:正式批准 data_loader.py 中的 process_timeseries() 阶段豁免执行 FW-3(按年份拆分提取)规则,允许一次性加载全量 Arrow 数据进行缺失值填充与截断状态机计算。
  3. CONF-03 DuckDB 显式列投影强制约束:在 S2-01 的 DuckDB SQL 中,严禁使用 SELECT *。对 fund_basic_info 必须硬编码 SELECT fund_id, fund_name, fund_type, create_date 进行显式投影。仅允许 fund_type 作为临时审计字段随 Arrow 流转,在最终落盘为 Parquet 前,必须通过 Polars 显式 select 将其彻底丢弃。
  4. CONF-04 data_audit.json Schema 零偏移确认:S2-04 产出的 data_audit.json 必须严格遵循《0002-数据基建》中定义的完整 JSON 结构(含 pipeline, input, filtering, cleaning, output 五大根节点),拒绝任何字段名自定义或结构增删。
  5. CONF-05 审计关键比率计算逻辑确认:明确 filtering.whitelist_retention_rate 的计算公式必须为:whitelist_retained_funds / whitelist_total_funds。其中分母 whitelist_total_funds 必须取 fund_basic_info 原始 CSV 的物理总行数,严禁使用去重或其他口径。若分母为 0,统一调用 calc_rate 工具函数返回 0.0。
  6. CONF-06 config.yaml 扩展节点确认:确认在 config/config.yaml 中新增两个合法节点:① data.raw_paths(字典结构,声明路径);② duckdb.memory_limit(字符串类型,默认 "40GB")。
  7. CONF-07 建仓期硬删与 segment_id 跨段逻辑决策:采用“选项 A”。建仓期(12周)剔除逻辑严格锚定并仅作用于 segment_id == 0,即使 segment_0 被完全物理抹除,后续 segment_1 及以后的数据段仍予保留,以所有段有效周数总和参与后续存活期过滤,坚决遵守 CR-001 段独立性原则。
  8. CONF-08 异常值置 NULL 的涨跌幅计算基准决策:采用“选项 B”。异常值(单周涨跌幅绝对值 > 50%)判定必须基于“本周原始有值且上周原始有值”的真实相邻周计算,由 ffill 填充产生的值不参与此异常值判定逻辑,防止长缺失恢复后的首周数据被误杀置 NULL。
  9. CONF-09 Parquet 物理存储格式强制约束:确认 data/processed/ 下产出的文件命名严格为 net_value_YYYY.parquet。PyArrow write_parquet() 必须显式指定 row_group_size=100000;写入模式必须为“覆盖写”,严禁追加。
  10. CONF-10 最终落盘 Schema 绝对锁定:产出 Parquet 必须且仅能包含 4 列:fund_id(String), net_value_date(Date), cumulative_net_value(Float64), segment_id(UInt32)。任何多余的临时计算列在落盘前必须被 Polars 显式 select 丢弃。
  11. CONF-11 DuckDB VIEW 重建机制确认create_duckdb_view() 函数严禁依赖任何持久化数据库状态。每次被调用时,必须动态扫描 data/processed/ 目录下的年份文件,拼接生成 v_net_value_processed VIEW。
  12. CONF-12 数据血缘 Hash 校验时机确认:确认 src/utils/hash.py 的调用时机必须在 Parquet 文件落盘完成后,按年份文件逐个计算 SHA-256,并将结果列表持久化至 data/metadata/processed_hash.json
  13. CONF-13 M2 内存压测执行环境决策:采用“选项 A(实体机裸跑测试)”。S2-04 的压测必须在 64GB 实体机宿主机上直接运行 Python 脚本,不施加 Docker cgroup 限制,以获取最真实的物理内存消耗峰值作为算力安全结论的最终背书。
  14. CONF-14 净值字段数据类型强制映射确认:本系统在全链路(DuckDB 抽取、Polars 计算、Parquet 落盘)必须且只能使用 Float64,严禁使用原生 Decimal 类型,以防止 Polars 滚动窗口性能劣化及内存溢出风险。
  15. CONF-15 Redmine Story 强卡控验收标准映射锁定:S2-01至S2-05的Redmine自定义字段必须严格按本次会议确认的配置扩展、SQL约束、状态机单测覆盖、审计Schema及压测内存指标逐条填入,作为PR合并的强阻断门禁。
  16. CONF-16 审计工具函数 calc_rate 交付归属决策:采用“选项 B”。calc_rate 工具函数必须作为 S2-04(审计日志产出)的交付物,与其唯一调用者绑定,严禁在 S2-01 阶段提前产出导致“悬空代码”。
  17. CONF-17 R-6 风险(白名单覆盖率异常)阻断级别决策:采用“选项 B(有条件放行)”。若 S2-04 压测真实触发 whitelist_retention_rate < 15% 阈值,M2 里程碑标记为“带风险通过”,不阻断 Step 3 开发。但必须在 Redmine 建立 High 级别 Bug 单拉起排查,若在下个 Sprint 回顾前未闭环,则触发熔断机制。

二、 会议待办项

编号 责任人 任务描述 截止时间 验收标准
TODO-01 研发工程师 S1 占位 Mock 测试代码物理销毁 S2-01 交付同步 必须在 S2-01 首个 PR 中同步物理删除 S1 遗留的占位测试代码,CI 全绿方可进入 Review。
TODO-02 研发工程师 CI 性能熔断预案触发监控 Sprint 2 持续 S2 引入真实 I/O 后,单次 CI 执行总耗时超 180 秒立即触发 Runner capacity 调优或流水线拆分。
TODO-03 研发工程师 产出特例豁免 ADR 文档 会后首个 PR 于 Gitea /docs/adr/ 提交 002-s2-full-arrow-exception.md,必须固化特例生效的精确物理边界、内存预估依据及下游禁用声明;同步更新架构基线文档。
TODO-04 研发工程师 产出时序状态机核心逻辑单测矩阵 S2-02 交付同步 tests/test_data_loader.py 必须覆盖 CONF-07 的“segment_0 被建仓期完全吞噬后 segment_1 仍保留”场景,以及 CONF-08 的“长缺失 ffill 恢复首周免于 >50% 误杀”场景。
TODO-05 研发工程师 实体机全量压测报告归档 S2-04 交付 压测日志必须包含 DuckDB 阶段峰值、全量 Arrow 转换峰值、Polars 状态机峰值三个维度的 peak_memory_gb,且总峰值必须 ≤ 40GB,日志文件上传至 Redmine S2-04 任务附件。
TODO-06 项目经理 Redmine 验收标准录入与派发 会后 4 小时内 S2-01 至 S2-05 任务卡片的自定义字段已按 CONF-15 逐条填入,并指派给研发。

三、 会议风险项

编号 风险描述 应对策略/演进路线 跟踪机制
RISK-01 白名单基金覆盖率(whitelist_retention_rate)在压测时可能异常跌破 15% 阈值(R-6 风险具象化)。 采用“有条件放行”策略。M2 不阻断,但立即建立 High 级别 Redmine Bug 单,PM 联动数据资产管理员排查上游数据质量或白名单规则。若下个 Sprint 回顾前未闭环,则熔断后续 Pipeline。 Redmine Bug 单状态跟踪,Sprint 回顾会专项汇报。

四、 会议架构决策记录(ADR)产出要求

基于本次会议确认的 CONF-02、CONF-07、CONF-08,要求研发工程师在会后 PR 中,于 Gitea 仓库 /docs/adr/ 目录下提交以下 ADR:

  1. 002-s2-full-arrow-exception.md:固化 FW-3 豁免的技术选型理由(预估内存安全边界、状态机行级处理特性)及严苛的下游禁用声明。
  2. 003-s2-timeseries-state-machine-decisions.md:固化为何建仓期剔除仅锚定 segment_0(遵守 CR-001 段独立性),以及为何异常值判定必须剥离 ffill 填充值(防止长缺失恢复首周误杀)。

五、 Sprint 2 交付物全景清单(已对齐)

以下为本次会议最终锁定需新增或重写的文件及核心约束简述(供 Redmine 录入与 PR 检查点对齐):

分类 文件路径 核心约束简述
业务参数 config/config.yaml 新增 data.raw_pathsduckdb.memory_limit 节点,零魔法数字。
核心源码 src/data_loader.py DuckDB 显式投影防偏移;Polars 全量 Arrow 状态机处理(含 CONF-07/CONF-08 逻辑);Parquet 4 列强锁落盘;VIEW 动态重建。
工具扩展 src/utils/tools.py (或同类) 包含 calc_rate 函数(分母为 0 时返回 0.0),仅随 S2-04 交付,禁止提前产出。
质量保障 tests/test_data_loader.py 覆盖 segment_0 吞噬后保留、长缺失免误杀等 5 个核心边界单测。
数据产出 data/processed/net_value_YYYY.parquet row_group_size=100000,覆盖写,绝对纯净的 4 列 Schema。
数据产出 data/metadata/data_audit.json 严格符合 5 大根节点 Schema,retention_rate 分母取物理总行数。
数据产出 data/metadata/processed_hash.json 落盘后即刻生成的各年份文件 SHA-256 校验字典。
技术真相 docs/data_contract.md 沉淀完整的数据流转 Schema 与类型映射契约。
技术真相 docs/adr/002-s2-full-arrow-exception.md 记录 FW-3 豁免决策。
技术真相 docs/adr/003-s2-timeseries-state-machine-decisions.md 记录状态机核心边界决策。

会议闭环状态:✅ 全部议程结束,无遗留阻塞项。研发团队可依据本纪要启动 Sprint 2 实施。

Huarui Lin 更新于 2 天 之前 · 1 修订