diff --git a/docs/上线与协同阶段工作方案与规格书_2026-09-17.md b/docs/上线与协同阶段工作方案与规格书_2026-09-17.md index d497b82..54d77a6 100644 --- a/docs/上线与协同阶段工作方案与规格书_2026-09-17.md +++ b/docs/上线与协同阶段工作方案与规格书_2026-09-17.md @@ -56,7 +56,7 @@ 5. 选股系统 `plan_review.pms_roster` 读到元数据里 `rank_mode` 为 upstream 时按名册里的名次升序取前 N,否则按原始分降序取前 N(今天的写法)。这一处小改放在选股系统仓库,随工作包丙或单独一个提交。 6. 单测、台账、对接说明。既有的 `scripts/test_batch29_units.py` 里已有按质地分桶的三例,旁边加三例。台账一条,编号 016。持仓管理系统的对接说明加一段"前缀效应",与选股系统下游对接说明那段同义。 -**上线。** 持仓管理系统在选股系统机器上,源码挂载进容器,改代码只要重启进程,重启要用户同意、收盘后做。放在 09-23 收盘后,前提是 09-22 的六处读数干净。不与 09-21 的选股系统拉代码同一天,避免两处同时变化说不清。 +**上线。** 持仓管理系统在选股系统机器上,源码挂载进容器,改代码只要重启进程(不重建镜像,依赖没改),重启要用户同意、收盘后做。放在 09-23 收盘后,前提是 09-22 的六处读数干净。不与 09-21 的选股系统拉代码同一天,避免两处同时变化说不清。同一天按拍板点六决定要不要把档位白名单放宽。 细节见附录乙。 @@ -84,11 +84,11 @@ |---|---|---| | 09-17 | 三 | 复审完成。工作包甲与丙交付并复审通过(e7cf0a9,评审记录第九节),已推送 | | 09-18 | 四 | 收盘后数据基座 09-18 部署窗(附录甲,原样)。开发者做工作包乙 | -| 09-19 | 六 | 工作包乙交付,含选股系统那一处小改。我复审 | +| 09-19 | 六 | 工作包乙已于 09-17 交付并复审(评审记录第十节):持仓管理系统 f5cc1b8 通过,选股系统 4ae6378 退回一处,修补提交 09-19 前交 | | 09-21 | 一 | 收盘后选股系统机器拉代码,接口容器重启并预热,经审批,避开七点到七点半与八点半到九点零五 | | 09-22 | 二 | 07:15 后核首份质地序计划的六处读数,再核"两条路同一份前二十"。收盘后数据基座第二批(附录乙) | -| 09-23 | 三 | 09-22 读数干净,则收盘后重启持仓管理系统进程上线工作包乙,经审批 | -| 09-24 | 四 | 08:45 后核持仓管理系统的候选序等于上游名次序 | +| 09-23 | 三 | 09-22 读数干净,则收盘后重启持仓管理系统进程上线工作包乙(restart,不重建),经审批;按拍板点六改档位白名单 | +| 09-24 | 四 | 08:45 后在一次性容器里调候选选择函数,核 rank_mode 为 upstream、名次递增 | | 09-25 | 五 | 首份带两份对照名单的周报,底本按拍板点一 | | 09-28 | 一 | 数据基座建议级小包复审(若做) | | 09-29 | 二 | 数据基座第三批与前端重建(附录丙) | @@ -128,10 +128,16 @@ cd /Users/baobao/Documents/work/project/akg-factor-bridge && python3 test_plan_r cd /Users/baobao/Documents/work/project/akg-factor-bridge && PLAN_RANK_AXIS=transmission CARD_SORT_BY_QUALITY=0 POOL_SOURCE=tier REVIEW_TARGETS_AXIS=score python3 test_plan_rank_axis.py 2>&1 | tail -1 ``` -**本地(持仓管理系统仓库,工作包乙交付后)。** 预期末行 ALL SUITES PASS,例数只增不减。 +**持仓管理系统单测(选股系统机器,一次性容器)。** 本地 Python 3.9 没有 pydantic 也没有 docker,跑不了它的单测。办法是把提交打包拷到机器上的临时目录,用现成镜像起一个一次性容器把目录挂进去跑,不碰运行中的容器,跑完删目录。预期末行 ALL SUITES PASS,例数只增不减。先在本地打包上传: ```bash -cd /Users/baobao/Documents/work/project/tradingSystem && python3 scripts/run_tests.py 2>&1 | tail -3 +cd /Users/baobao/Documents/work/project/tradingSystem && git archive --format=tar.gz -o /tmp/pms-review.tar.gz HEAD && scp -P 2280 /tmp/pms-review.tar.gz factor@192.168.16.155:/home/factor/tmp/ +``` + +再在选股系统机器上跑并清理: + +```bash +D=/home/factor/tmp/pms-review && rm -rf $D && mkdir -p $D && tar -xzf /home/factor/tmp/pms-review.tar.gz -C $D && docker run --rm --entrypoint python -v $D:/app -w /app pms:latest scripts/run_tests.py 2>&1 | tail -3; rm -rf $D /home/factor/tmp/pms-review.tar.gz ``` **选股系统机器(09-22 早上 07:15 后)。** 两条路同一份前二十。这条读数是必改一的最终判收。预期打出快照文件名与 True。 @@ -142,16 +148,16 @@ cd ~/project/akg-factor-bridge && docker exec akg_factor_bridge python3 -c "impo **选股系统机器(09-22)。** 首份质地序计划的六处读数,命令与预期照《选股计划调整方案_2026-09-17》第六节,不再抄一遍。 -**选股系统机器(09-24 早上 08:45 后)。** 持仓管理系统的候选序等于上游名次序。读它当天的名册快照与元数据。预期 `rank_mode` 为 upstream,打出的名次序列严格递增。 +**选股系统机器(09-24 早上 08:45 后)。** 持仓管理系统的候选序等于上游名次序。名册快照按代码序落库,看它的顺序没有意义,要直接调候选选择函数。在持仓管理系统仓库目录起一个一次性容器(带 compose 的网络与环境,不碰运行中的容器)。预期第一项是 upstream,随后的名次列表严格递增。 ```bash -cd ~/project/akg-factor-bridge && docker exec akg_factor_bridge python3 -c " -import json, db -df = db.read_mysql('pms', 'SELECT roster_json, meta_json FROM pms_plan_snapshot ORDER BY id DESC LIMIT 1') -meta = json.loads(df.iloc[0]['meta_json'] or '{}'); roster = json.loads(df.iloc[0]['roster_json'] or '[]') -print('rank_mode =', meta.get('rank_mode')) -rows = [r for r in roster if str(r.get('b') or 'main') == 'main'] -print('名册前十的名次:', [r.get('r') for r in rows[:10]])" 2>&1 | grep -v Warning +cd ~/project/tradingSystem && docker compose run --rm --no-deps pms-web python -c "from app.services import plan_feed as pf; out = pf.candidates(); print(out.get('rank_mode'), [(i.get('rank'), i['ts_code']) for i in out['items'][:10]])" 2>&1 | tail -2 +``` + +**选股系统机器(09-24)。** 名册元数据已写排序模式。预期打出 upstream。 + +```bash +cd ~/project/akg-factor-bridge && docker exec akg_factor_bridge python3 -c "import json, db; df = db.read_mysql('pms', 'SELECT meta_json FROM pms_plan_snapshot ORDER BY id DESC LIMIT 1'); print(json.loads(df.iloc[0]['meta_json'] or '{}').get('rank_mode'))" 2>&1 | grep -v Warning ``` **选股系统机器(09-25 周报后)。** 周报底本。预期第一节每天的注记写"底本:快照",两份对照名单都在。 @@ -179,7 +185,8 @@ cd ~/project/akg-factor-bridge && grep -n "底本\|新序前二十\|旧序前二 2. **持仓管理系统协同项的默认值与上线日。** 建议默认开、靠上游的 `rank_axis` 自门控,09-23 收盘后上线。不与 09-21 同一天。 3. **提交 4d7ca55 随本方案推送。** 代定。修复小包在它上面再提一个。 4. **两份对照名单的口径。** 代定:两份都是全量序前二十,不设主题限额,与周报其余名单同口径。它比的是排序规则,不是当天交付的那二十只;交付名单另有"候选单"与"强传导交付名单"两行。 -5. **工作包乙里选股系统那一处小改(`pms_roster` 按 `rank_mode` 取序)** 代定随工作包丙同一个提交。 +5. **工作包乙里选股系统那一处小改(`pms_roster` 按 `rank_mode` 取序)** 代定随工作包丙同一个提交。实际单独提交 4ae6378,复审退回一处,修补见附录乙末段。 +6. **持仓管理系统的档位白名单要不要放宽。** 它的运行参数 `PMS_PLAN_TIERS` 现在退回初值"强传导",是一道硬闸:上游序下它拿到的前五十是"强传导行里的质地序",被指向但只算弱传导的好公司永远进不来,与"传导降为雷达门、被指向即可"不一致,也与入池不同源。建议 09-23 随工作包乙上线时在参数页把它改成"强传导,弱传导",不用代码,台账补一行;周报的交付名单还原读同一个参数,自动跟上。不改也行,但四周复核要说明这一处差异。 --- @@ -205,6 +212,8 @@ cd ~/project/akg-factor-bridge && grep -n "底本\|新序前二十\|旧序前二 3. 周报改读快照后,09-18 与 09-21 两个旧序日的"主榜新序前二十"其实是旧序前二十,注记写明。 4. 工作包乙上线那天若持仓管理系统进程重启失败,回退是不重启,代码在挂载目录里但进程还是旧的,行为不变。 5. 名册元数据的 `rank_mode` 只从工作包乙上线那天起有,之前的名册周报仍按原始分还原,注记写明"历史 N 与序不可还原"那句已有。 +6. 持仓管理系统的名册主榜会从六十到一百行跳到接近三百行。它不向上游传主题限额(运行参数 `PMS_PLAN_THEME_CAP` 为零,08-13 设的),上游按默认每主题五只截;质地轴下无传导行不受这条限额,所以 09-22 起它拿到的行数接近它要的三百。多出来的行绝大多数判仅展示,被它的按判决分流剔掉,候选只数不受影响,"考虑只数"跳一次属预期。 +7. 持仓管理系统机器上 `PMS_PLAN_TOP_N` 是五十,与入池的五十对上;文档里写"前三十名"的地方指的都是这个数,不另改。 --- @@ -246,7 +255,8 @@ def take_by_theme(items, n, theme_cap, theme_of, uncapped_no_theme): - 选股系统 `plan_review.pms_roster`(`plan_review.py` 约 120 到 140 行):查询多取 `meta_json`;`rank_mode` 为 upstream 时 `rows.sort(key=lambda r: r.get("r") if r.get("r") is not None else 1 << 60)`,否则保持按 `s` 降序。注记字符串加一句"序按名册元数据 rank_mode 还原"。 - 单测:`scripts/test_batch29_units.py` 约 298 到 324 行旁加三例:上游 `rank_axis` 为 quality 且开关开时按名次排且立场桶不参与;上游没带 `rank_axis` 时开关开也逐字回按分桶排;`rank_mode` 三种取值各对。若 `run_tests.py` 的文档字符串里写了各批例数,按哨兵改数。 - 台账 016,五段式。对接说明加"前缀效应"一段。 -- 上线(选股系统机器,收盘后,经审批):`docker compose --profile sched --profile ws restart -t 30`,重启前用 `docker exec pms-ws date` 看容器时间确认已收盘。 +- 上线(选股系统机器,收盘后,经审批):`docker compose --profile sched --profile ws restart -t 30`,重启前用 `docker exec pms-ws date` 看容器时间确认已收盘。不重建镜像:源码由 `docker-compose.override.yml` 挂载进容器,依赖没改。 +- **4ae6378 的修补(复审退回一处,09-21 拉代码前交)。** `pms_roster` 的上游序分支不能去掉档位过滤:持仓管理系统的 `tiers` 门与排序模式无关,任何模式下都先过它。改法是从 `pms_runtime_param` 读 `PMS_PLAN_TIERS`(查询与读 `PMS_PLAN_TOP_N` 那条同款;缺行退回"强传导",与持仓管理系统 `config/settings.py` 的初值一致;读到空串按不过滤),两种模式都先按它过滤主榜行,再按 `rank_mode` 定序:upstream 按 `r` 升序、否则按 `s` 降序,最后取前 N。把"过滤加定序"抽成纯函数 `_roster_pick(rows, tiers, mode, n)`,在 `test_plan_review_source.py` 里加一例:同一份名册在 upstream 与旧模式下各取到的名单、以及白名单为空时不过滤。注记里写明白名单来自参数表。 ## 附录丙(可不读):工作包丙实现 diff --git a/docs/评审记录_2026-09-17.md b/docs/评审记录_2026-09-17.md index 107f8f7..d26b34c 100644 --- a/docs/评审记录_2026-09-17.md +++ b/docs/评审记录_2026-09-17.md @@ -131,3 +131,23 @@ **下一步。** e7cf0a9 已推送,09-21 收盘后按计划拉。开发者转做工作包乙,规格在工作方案附录乙,09-19 交付。09-22 早上按工作方案第六节核"两条路同一份前二十"。 +--- + +## 十、工作包乙复审(2026-09-17 下午,评审者):持仓管理系统通过,选股系统小改退回一处 + +**对象。** 持仓管理系统仓库 f5cc1b8(台账 016)与选股系统仓库 4ae6378,都在各自 `main`、交付时未推送。 + +**持仓管理系统 f5cc1b8:通过。** 开发者本机跑不了它的单测(缺 pydantic 等依赖,本机 Python 3.9,无 docker),只做了编译与逐行对照,如实说了。我把这个提交用 `git archive` 打包、拷到选股系统机器的临时目录,用 `pms:latest` 镜像起一个一次性容器把代码目录挂进去跑 `scripts/run_tests.py`,末行 ALL SUITES PASS,`test_batch29_units.py` 三例新用例全部 ok,跑完删掉临时目录与包。运行中的容器一个没碰。这是今后复审持仓管理系统代码的办法,写进工作方案第六节。 + +逐项核对(按工作方案第十节四件事):自门控只看计划顶层的 `rank_axis`,由 `_rank_mode` 一处判定,候选选择与名册元数据都用它;上游序下排序键只有名次与原始分,立场桶不参与;上游没带 `rank_axis` 时两个既有分支逐字不动,第二例钉住;名册元数据写了 `rank_mode`,在拉计划处按参数算好传入,与候选选择同一份参数同一份计划。四个新键归一、透传,顶层 `rank_axis`、`rank_rule` 带进来,参数登记默认开,接入说明补了前缀效应,台账 016 五段齐。 + +一处要改口的是台账 016 与提交信息里的"重建镜像加 force-recreate"。选股系统机器上持仓管理系统的源码是挂载进容器的(`docker-compose.override.yml` 把仓库目录挂到 `/app`,我只读核过),依赖没改,上线只需重启进程:`docker compose --profile sched --profile ws restart -t 30`。重建镜像会覆盖镜像标签、丢容器里的定时任务表、再跑一遍建表脚本,没必要。台账那句留着不改,以工作方案第六节为准。 + +**选股系统 4ae6378:退回一处。** `pms_roster` 的上游序分支去掉了"档位强传导"过滤,提交信息说是"与持仓管理系统的上游分支同口径"。这不对。持仓管理系统的档位白名单(`select_candidates` 里的 `tiers` 门)与排序模式无关,任何模式下都先过它再截断。选股系统机器上 `pms_runtime_param` 表里没有 `PMS_PLAN_TIERS` 这一行,`param_store.get` 退回 `config/settings.py` 的初值"强传导",所以持仓管理系统在上游序下仍然只取强传导行。周报按 4ae6378 还原出来的"生产名单"会多出弱传导与无传导的行,与它真正交付的那份对不上,四周复核那一行读数就失真了。 + +修法(工作方案附录乙补了一段):`pms_roster` 从 `pms_runtime_param` 读 `PMS_PLAN_TIERS`(缺行退回"强传导",与持仓管理系统的初值一致;读到空串按不过滤),两种模式都先按它过滤,再按 `rank_mode` 定序,上游序按名次升序、旧序按原始分降序。把"过滤加定序"抽成纯函数加一例单测,不连库。一个小提交,09-21 拉代码之前做完。 + +**顺带核出的三件事实,写下来免得再猜。** 一,持仓管理系统机器上 `PMS_PLAN_TOP_N` 是五十,不是文档里多处写的三十,与入池的 `POOL_TOP=50` 正好对上。二,名册快照按代码序落库,不是名次序,所以"名册前十名次递增"这样的读数无意义,09-24 的读数改成在一次性容器里调候选选择函数看输出,命令在工作方案第六节。三,最近三天名册主榜只有六十三、九十九、九十八行,不是三百。原因查清了:持仓管理系统的运行参数 `PMS_PLAN_THEME_CAP` 在 08-13 被设成零,零的含义是不向上游传主题限额、用上游默认值,上游默认每主题五只,所以它拿到的是每主题五只后的六十到一百行。这是既有设置,不是本次的问题。但 09-22 起会变:质地轴下无传导行不受上游限额,它会拿到接近三百行,多出来的绝大多数是无传导、判仅展示的行,被它自己的按判决分流剔掉,候选只数不受影响,页面上的"考虑只数"会跳一次,写进工作方案第十一节。 + +**一条要拍板(工作方案拍板点六)。** 持仓管理系统的档位白名单"强传导"是一道硬闸:上游序下它拿到的前五十是"强传导行里的质地序",被指向但只算弱传导的好公司永远进不来。这与"传导降为雷达门、被指向即可"不一致,也与入池(被指向即可,不看档位)不同源。建议 09-23 随工作包乙上线时把运行参数 `PMS_PLAN_TIERS` 改成"强传导,弱传导",页面改、不用代码,台账补一行。不改的话四周复核要说明这一处差异。 +