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