tradingSystem/docs/评审记录_2026-09-17.md

16 KiB
Raw Blame History

评审记录2026-09-17选股计划排序轴换质地提交 4d7ca55

评审对象:选股系统仓库 main 上的提交 4d7ca55开发者 2026-09-17 上午交付,交付时未推送)。基线 6138c01。 评审依据:docs/选股计划调整方案_2026-09-17.md 第十节"评审方式"。评审者只看证据。 写法遵守 .claude/output-styles/readable-chinese.md。本文件是四个仓库逐字同步的副本之一。


一、结论

有条件通过。 代码主体与规格书一致五个测试文件本地全绿四个开关设旧值时逐字回旧成立分数编码与三门槛一确认三风险都没有动。三处必改合成一个修复提交09-21 拉代码之前做完并复审。两条建议级不阻塞。一条拍板项。

三处必改各一句话。

  1. 装配路与快照路对"已启动成员"的主题限额判定不一致。同一天的计划,早上落盘的主榜前二十与接口读快照给出的前二十可能不是同一批。
  2. 计划里的 encoding 说明句在旧轴下也写"名次按质地带优先",与旧轴的实际次序相反。
  3. 交接文档一段没写,规格书工作包五第三条没有完成。

修复小包的规格写在《上线与协同阶段工作方案与规格书_2026-09-17》工作包甲。本记录只写核对结果与理由。


二、我亲手跑出的证据

本地单测(开发机,选股系统仓库)。 五个文件末行全部 ALL OKtest_plan_rank_axis.pytest_plan_quality_rank.pytest_card.pytest_pool_logic.pytest_plan_snapshot.py。四个开关设旧值再跑 test_plan_rank_axis.py,仍 ALL OK。七个改动的模块 py_compile 通过。开发者说只能在部署机跑的五个测试文件,我本机同样跑不起来,原因与他说的一致:缺 psycopgfastapi,或本机 Python 3.9 不支持联合类型注解。这是既有状况,不是本次引入。

一条要说明:test_plan_snapshot.py 里"拿真实快照逐行比对"那一例在本机是跳过的,因为本机没有快照文件。它只有在部署机上跑才真正生效。必改一正是它会查出的那件事。

旧路径等价(开发机)。 入池的 tiercandidate 两个分支从内联代码抽成了纯函数 select_plan_rows。我用随机合成的主榜行做了三百轮对照,每轮六种组合(两个来源乘三种档位白名单),新函数与旧内联逻辑的输出零差异。

部署机只读读数选股系统机器09-16 快照)。 仓库停在 3d3f317与规格书基线一致。.env 里有 POOL_TOP=50POOL_MAX=100CARD_REQUIRE_STARTED=0,没有 POOL_SOURCEPLAN_RANK_AXISPOOL_THEME_CAP,所以拉代码后默认值直接生效,与规格书第五节说的一致。

09-16 快照主榜全量五百三十二行。质地分布:好十六、中四十八、差一百三十四、无评析三百三十四。我按规格书的词典序在这份快照上模拟了一遍质地序,前二十里好十六、中四。前二十里有六行没有传导主题,正好落在"第六只起被限额顶掉"的位置,规划代理那条发现在真实数据上成立。前二十里"储能"主题五只,正好碰到限额边界。

"已启动成员"的行(证据由已动成员视图补上的行)全量八行,旧序名次八十二、三百五十三、三百九十、四百九十一、五百、五百二十、五百二十六、五百三十。旧序前二十里没有它们;模拟质地序前二十里有一行。这就是必改一在真实数据上的样子。


三、逐项核对

按规格书第十节的核对步骤逐条记。

  1. diff 对照附录己的文件清单。 十五个文件改动,全在清单内。factors.pyapi.py 没有出现在 git show --stat 里。清单里的"交接文档"没有改,见必改三。
  2. 本地跑测试文件。 见第二节。
  3. 四个开关设旧值时的逐字回旧断言是不是真断言。 是,但覆盖方式要说清。第一例只断言 _rerank 在旧轴下与 09-14 的 _rerank_by_quality 序列与分值逐字相同,它验的是分派。另外三个开关各有自己的旧路径断言:入池旧路径由我的三百轮对照补上,评析名单的 score 分支是第七例,卡内序关掉是第八例,主题限额例外关掉是第九例。四条合起来才是"四开关逐字回旧"。
  4. 主题限额的例外只在质地轴下生效。 是。装配时按 config.PLAN_RANK_AXIS 传入,快照路按行上的 rank_axis 推断。回退那天读旧快照仍按质地轴截取,这是对的,因为那份快照的序就是质地序。但两条路对"已启动成员"的判定不一致,见必改一。
  5. 卡内序、入池、评析名单三处的次级键与去重。 卡内序在判决与涨幅之间插入负的质地带,关掉时元组与旧实现逐字相同。入池 rank 源只取被指向的行、按新序截前五十,备用开关并进旧序强传导前五十、去重、受 POOL_MAX 兜底。评析名单两路并集去重后按新序名次排,score 分支按原始分。三处都有确定的末位键。
  6. _row 只加键。 是。加了 quality_bandrank_axispointed 三键,质地轴下再加 rank_oldscorescore_adjtier 的值与含义没有变。顶层 encoding 的值变了,见必改二。
  7. 分数编码。 factors.py 一字未动。组内分从原始分反解,档位用整除,与编码一致,我逐个区间验过。
  8. 旧序名次。 rank_old 取自全量序,不是截断后的序。主榜与观察档两个序列的代码不重叠,两张名次表合并不会互相覆盖。
  9. 一百二十天守卫。 守卫是活的:评析摘要里的 age_days 由数据日减报告运行日算出,取值真实。守卫对质地带、质地门、卡内序三处生效。它不对旧轴的加减八分生效,score_adj 也不受守卫。这与规格书第五节表里那一行的写法不一致,但代码是对的,因为规格书交付要求第四条要求 score_adj 的值不变。规格书这一行我已改正,见第五节建议三。
  10. 入池不变式的验证命令是否成立。 成立。入池走 plan.collect 要五百行的宽池、主题限额取 POOL_THEME_CAP(默认零),再交给纯函数选行;规格书第六节那条命令拿快照全量行喂同一个纯函数,两边看到的是同一个序。
  11. 评析目标名单拿到的是全量行。 是。persist 收到的是落盘快照,main 是全量新序行,每行带原始分与新序名次。
  12. 周报的底本。 周报不读快照,它用 plan.collect 把当日重新装配一遍再取全量行。两份对照名单都从这份重算取。这是既有做法,不是本次引入,但它决定了四周复核读到的是什么,见第六节拍板项。

四、必改

必改一:装配路与快照路对"已启动成员"的主题限额判定不一致

是什么。 主榜截取有两条路。装配路是 collect 里的内部函数 _pick,早上出计划时用它截主榜前二十,写进 md 与快照的 main_shown。快照路是模块级函数 pick_rows,接口读快照时用它按请求参数截取,持仓管理系统要的三百只前缀从这里来。两条路本该同一套规则,test_plan_snapshot.py 拿真实快照逐行比对钉住这件事。

这次的例外判定在两条路上写法不同。装配路看的是传导视图有没有这只票的证据行:没有证据行就当无主题、免限额。快照路看的是行上的 evidence.theme:没有主题才免限额。差别出在"已启动成员"上。已启动成员在传导视图里没有证据行,但装配时 _row 会用已动成员视图的目标环节把 evidence.theme 补上(plan.py 约 550 行)。于是同一只已启动成员,装配路把它当无主题免限额、不计数,快照路把它算进所在主题、受限额。

为什么以前没炸。 旧序下这些行排在八十名以后,碰不到前二十的限额。旧代码里两条路对它们其实也不一致(装配路把它们归进"无传导"桶计数快照路归进所在主题只是不影响前二十。质地序把被指向的已启动成员提到了前列09-16 快照模拟出来前二十里有一行,"储能"又正好五只碰到边界,差一只就会让两条路给出不同的第二十名。

后果。 同一天md 与快照里的 main_shown 是一份前二十,接口给持仓管理系统的是另一份。规格书第二节"同日重跑结果一致"这条原则在两条路之间失守。部署机上跑 test_plan_snapshot.py 会在第一个出事的交易日变红。

修法。 不在两处各打补丁,改成一处实现两处调用:把"按主题限额截取"抽成模块级纯函数,装配路与快照路都调它,主题的取法与 _rowevidence 的取法同源(含已动成员视图的补充)。这样装配路对已启动成员的判定也变成"算进所在主题",与快照路一致。这一步同时改正了旧轴下的既有分歧,改的只是 md 与 main_shown 这条路对已启动成员的计数,接口路(持仓管理系统读的那条)原样不动。逐字回旧那一例单测不受影响。细节与单测在工作方案附录甲。

必改二:encoding 句在旧轴下写错了

collect 返回的 encoding 一句无条件追加了"名次按质地带优先,传导只在带内作次级键(开关 PLAN_RANK_AXIS"。旧轴下名次不按质地带优先,这句话与实际相反。持仓管理系统把这句原样收进它的状态页给人看。修法是只在质地轴下追加,或把句子改成条件式写法。一行改动。

必改三:交接文档一段没写

规格书工作包五第三条要求交接文档一段并四仓库同步,提交里没有。修法是在最新一份交接文档 docs/交接_2026-09-10.md 末尾加一节带日期的补记,把台账那一行从"最新到第 057 条"改成 063四仓库同步。交接段的骨架在工作方案附录甲。


五、建议级(不阻塞)

  1. 评析名单 score 分支不是逐字回旧。 旧代码取快照序前一百,快照序是 09-14 的调整分序;新代码按原始分排前一百。差别只在第一百名附近被加减八分挪动的几只。评析名单只管覆盖,这点差别没有后果。不改,记在这里。
  2. 两处小瑕疵。 _rerank_by_axisaxis 参数没有用到;card.sort_key 在函数体内导入 config。都不影响行为。
  3. 规格书两处文字改正。 第五节表里 COMPANY_REVIEW_STALE_DAYS 那一行说守卫对加减分也生效,工作包二第二条说"也不再加减分",与代码和交付要求第四条矛盾,按代码改正。附录乙说周报"从快照 _full 读主榜行",实际是现算后取 _full,按实际改正。这两处我已经改在规格书里,随本记录同步。
  4. 周报里"主榜新序前二十"对改动前的日子。 09-25 周报的窗口含 09-18 与 09-21 两个旧序日,那两天这一行的名单其实就是旧序前二十,标签会略有误导。旧序名单那一行对这两天正确地跳过了。周报一段注记即可,不改代码。

六、拍板项:周报的底本

周报用 plan.collect 把每个计划日重新装配一遍,而不是读当天落盘的快照。重新装配用的是跑周报那一刻的评析报告:查询取每只票最新一次运行,不按日期截断。于是 09-22 的"新序前二十"在 09-25 算出来时,用的是 09-25 手里的质地档。一家 09-24 才有报告的公司,会被回头算进 09-22 的前列。

这在质地是次级项的时候是小事,在质地成为主轴之后不是。四周复核比的是"新序前二十与旧序前二十谁更好",两份名单都应该是当天真正出过的那份。快照落盘时的注释写着"它是复盘与对账的唯一底本",周报却没有读它。

我的建议是周报改成先读当日快照的全量行,没有快照的日子才回落到重新装配,加一个开关默认读快照。改动小,但它改变周报所有既有名单的取数方式,从 09-02 有快照那天起的读数会与旧周报的口径不同所以要用户拍板。写在工作方案工作包丙与拍板点一。2026-09-17 用户拍板:做,随修复小包同一个提交。


七、答开发者的两个问题

要不要现在推送。 复审已经做完,不必等 09-19。提交 4d7ca55 我随本记录一起推到远端,修复小包在它上面再提一个。选股系统机器只在 09-21 收盘后由人拉代码,中间远端有一个待修的提交没有风险。

要不要现在补交接段。 要,放进修复小包,与必改一、必改二同一个提交。


八、附:文件清单对照

提交改了十五个文件:plan.pycard.pypool.pyreview_targets.pyplan_review.pyplan_reconcile.pyconfig.py.env.exampleREADME.mddocs/复盘决定台账.mddocs/选股计划入池_对接说明.mddocs/选股说明_下游对接.md、新 test_plan_rank_axis.pytest_plan_quality_rank.pytest_plan_snapshot.py。附录己清单里只差交接文档。清单外没有文件被动。


九、修复小包复审补记2026-09-17 晚,评审者):通过

对象。 提交 e7cf0a9选股系统 main),工作包甲三处必改与工作包丙合成一个提交。开发者交付时未推送,复审通过后由我推送。

证据。 本地七个测试文件末行全部 ALL OKtest_plan_rank_axis.py(十二例)、test_plan_snapshot.pytest_plan_quality_rank.py、新 test_plan_review_source.pytest_card.pytest_pool_logic.pytest_plan_review_heads.py。四个开关设旧值再跑 test_plan_rank_axis.py 仍 ALL OKPLAN_REVIEW_SOURCE=live 再跑 test_plan_review_source.py 仍 ALL OK三个改动模块 py_compile 通过。

逐项核对(按工作方案第十节)。

  1. 截取规则只剩一处实现。模块级 take_by_theme 是唯一的循环,collect 里的 _pick 与模块级 pick_rows 都只剩一行调用。
  2. 两条路的主题取法同源。装配路的 _theme_of_rowevidence 的三行逐字对得上:传导视图有证据行取它,否则已启动成员用已动成员视图补的主题,否则空。快照路取行上的 evidence.theme,空串与缺失都当无主题,与装配路一致。
  3. 旧轴下无主题行仍归"无传导"桶计数。uncapped_no_theme 为假时桶名取"(无传导)",第十二例钉住六条只放五条。
  4. encoding 句两轴都对,改成按开关分别写清。
  5. 交接段已写进 docs/交接_2026-09-10.md 第十一节,台账行改成 063。提交信息说"四仓库同步",但开发者只在选股系统仓库提交,另外三份副本由我随本补记同步。
  6. 工作包丙:无快照日回落到现算并注记"底本:现算",开关设 live 时有快照也现算,单测三条都钉住。run 从计划字典里只取 _fullcandidateswatch 三个键,快照整理出的字典正好齐这三个,没有漏掉的键。

建议级,不阻塞。 一、_row 里那三行可以改成直接调 _theme_of,让取法只剩一份文字,眼下是两份逐字相同的文字。二、第十一例在测试里抄了一遍 _theme_of 的逻辑而不是调真函数,_theme_of 若改成模块级纯函数,测试就能调真的。都留到下次顺手改。

下一步。 e7cf0a9 已推送09-21 收盘后按计划拉。开发者转做工作包乙规格在工作方案附录乙09-19 交付。09-22 早上按工作方案第六节核"两条路同一份前二十"。