akg-factor-bridge/docs/选股计划入池_对接说明.md

126 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 选股计划入池 —— 与决策系统夜间推理的对接说明
> 2026-08-03 与用户定稿并落码。代码:`pool.py`(入口 `run.py push-pool`
> 纯逻辑单测 `test_pool_logic.py`。本文写清楚三件事:为什么这么接、规则是什么、怎么部署与验证。
## 1. 为什么走股票池,而不是新造一条推理链
决策系统bionic_trader每晚 22:30 的认知扫描,扫描范围就是 Mongo `stock_groups`
集合里**所有分组的股票代码并集**`daily_scan_v2.get_mongo_stock_pool()` 对整个集合取
并集,不看分组归属)。扫到的每只票会产出支撑位、压力位、定性结论,落在
`strategy_daily_results`——持仓系统PMS的参考位、择时执行区间、研判上下文全部读它。
所以把每日计划写成集合里的一个独立分组,候选票当晚就自动获得夜间推理,不需要在任何
系统里新增推理步骤。这正是「提前计算为主、盘中监控为辅」的落法:大模型的活儿全部发生
在凌晨,盘中各接口只读现成结论。
配套的收尾机制决策系统也已经有:掉出池子并集的票,下一次扫描会被
`prune_stale_strategies` 把策略标成 `DROPPED` 并生成离场报告——所以出池动作只需要在
Mongo 侧记账(回收站),分析侧的清理是自动的。
## 2. 入池、留池、出池的规则(用户拍板,`pool.decide()` 逐条对应)
| 规则 | 内容 |
|---|---|
| 入池 | 当日计划(强传导主榜前 `POOL_TOP`,与 PMS 候选同口径)∪ 当前持仓(首选 PMS 账本 `pms_position`——新架构下下游 `trading_position` 已无写入方,账本才是有人维护的持仓事实;账本读不到才退回下游表兜底) |
| 留池 | 旧成员既不在计划也无持仓、但形态未恶化的,留下继续接受每晚分析——榜单是按条数截断过的,掉榜不等于变坏 |
| 出池 | 无持仓、不在计划、且形态已恶化 → 移入回收站 `stock_recycle_bin`。恶化判据用决策系统自己的结论:`strategy_daily_results` 最新定性为 SELL / AVOID / DROPPED |
| 上限 | 池子超过 `POOL_MAX`(默认 60从「留池观察」里清最久没上过榜的这类清退不进回收站它们没有恶化记录出池后由决策系统的 DROPPED 机制收尾 |
| 底线 | **持仓永不出池**,即使形态恶化——恶化持仓在池子备注里给警示,处置是 PMS 风控与体检的事,池子只保证它每晚有结论可用 |
拿不到就不动的三条安全边界:计划数据缺失(因子表没跑出来)→ 整轮中止,池子保持原样;
持仓读不到 → 同样中止(否则可能把持仓票错清出池);决策系统结论读不到 → 本轮不判恶化、
一只都不回收,其余照常并在备注里说明。
## 3. 写进 Mongo 的东西
分组文档(`stock_groups`,按 `group_code=AKG_PLAN` + `org_id` 覆盖式更新):
`stock_codes`(前缀码)、`remark`summary 一句人话 + factor_details + retained_positions +
update_time_str格式沿用现有池子、`strategy_context`(每只计划票的分数/档位/主题/预期
空间,供人查)、`member_meta`(每只票的入池日与最近上榜日,上限清退的排序依据)。
回收站文档(`stock_recycle_bin`字段照抄现有格式group_id / group_name / org_id /
removal_batch / removed_at / stock_code**另加了一个 `reason` 字段**说明移入原因——
Mongo 加字段对老读者无影响,事后能分清是形态恶化还是别的原因。
老模拟系统目前处于停用状态2026-08-03 用户确认),所以新分组不会引发任何自动交易;
将来若重启那套系统,需要先确认它只认自己的分组。
## 4. 每天的时间线
```
07:10 桥机 cronUTC 23:10build + plan既有盘前链07-30 D 案)
07:1x 接着跑 push-pool计划写入池子恶化票移入回收站
07:1x push-pool 顺手触发决策系统增量补扫(/api/v1/xxl/daily-scan?mode=incremental
只补当天还没结论的票——通常就是几只新进榜的)
08:30 决策系统的例行查漏补缺照跑(另一层兜底)
08:40 PMS 拉 /plan 刷新候选池(原有流程,一字未动)
09:30 开盘:候选票的支撑/压力/定性已就绪 → 参考位、择时区间、研判全部可用
22:30 决策系统全量夜扫:覆盖整个池子并清理掉出池的票(终极兜底)
```
增量补扫失败只提示不报错——当晚全量扫是兜底,最坏情形是新进票当天白天没有结论
择时对它们回「不可用」PMS 自动退内置择时,行为与接入前相同)。
## 5. 部署(桥机 factorevaluationakg-factor-bridge 根目录)
```bash
git pull
# 新增 pymongo 依赖,需要重建一次镜像(代码本身是挂载卷,之后改码不用再 build
docker compose build && docker compose up -d
# .env 增补(对照 .env.example 底部「选股计划入池」一节):
# MONGO_* 五项(填决策系统 .env 里的同名值)
# BIONIC_SCAN_URL / BIONIC_SCAN_KEY可选触发增量补扫用
# 纯逻辑单测(不连库,秒级):
docker compose exec -T akg-factor-bridge python test_pool_logic.py
# 首次试跑(只看不写,核对入池/出池明细):
docker compose exec -T akg-factor-bridge python run.py push-pool --dry-run
# 确认无误后真写(写完会打印分组 _id 与回收站回执):
docker compose exec -T akg-factor-bridge python run.py push-pool
```
宿主 crontab把 push-pool **串在既有盘前链命令末尾**(用 && 衔接,链子没跑成就不动
池子),不要单独定时——单独定时撞上链子超时会拿昨天的因子重复入池:
```
# 既有UTC 23:10 = CST 07:10示意
10 23 * * 1-5 docker exec akg_factor_bridge python run.py build all --mode daily && \
docker exec akg_factor_bridge python run.py plan && \
docker exec akg_factor_bridge python run.py push-pool
```
## 6. 验证与判收
部署当天:`--dry-run` 的明细与预期一致;真写后在 Mongo 里能查到 `AKG_PLAN` 分组且
`stock_codes` = 计划 持仓。当晚 22:30 后:`strategy_daily_results` 里新进票有当日行。
次日盘中PMS 侧 `probe_bionic.py` 对候选票探活,择时接口应回 FIRE/WAIT 并带出区间
(不再是「没有昨夜结论」的不可用)。连续跑几天后:掉榜未恶化的票留在池里继续有结论;
出现 SELL/AVOID 的闲置票进回收站,且决策系统随后把它们标 DROPPED。
判收纪律照旧:代码就绪 ≠ 接通 ≠ 判收。判收 = 上面这四条在实机各看到一次。
## 7. 池深与 PMS 下单候选的关系2026-08-27 查明并定稿)
这一节回答一个真问题:一只票能被 PMS 下单、却不在本分析池里,是不是出错了。答案是不出错,但两边的深度本来该对齐,这次把它对齐了。
先分清两个深度,它们同源,但不是一回事。
一个是本分析池的深度,参数是 POOL_TOP。它决定决策系统每晚深度分析多少只票一只约两三分钟。
另一个是 PMS 的下单候选深度,参数是 PMS_PLAN_TOP_N在 tradingSystem 的 config/settings.py 里。PMS 建仓不读本 Mongo 池,而是直接调选股计划接口 plan_api取主榜按分数前 PMS_PLAN_TOP_N 名,作为真正的下单候选。
问题出在两个深度不一致。改之前 POOL_TOP 是 20PMS_PLAN_TOP_N 是 30。于是主榜第 21 到 30 名的强传导票PMS 能下单却不在本分析池里买它们的时候没有当晚的支撑压力位和定性结论托底。有个缓冲买入即成持仓而持仓永远进池所以当晚池子重建后会补上分析。但买入当天到当晚那一段是没有夜间结论的。2026-08-27 的 688599天合光能主榜第 25 名,强传导)正是这情形。那天它还叠了一层:自动闸先判拒,理由是资金净流出、无盘中买入信号、证据不足,随后被页面人工裁决改成通过并成交,所以那一笔是人工强下的,不是自动越界。
用户拍板:本分析池要做成 PMS 下单候选的超集。这样能买的票一定先在夜间被分析过,用户也能观察到更宽的一批。
要长期守的不变式POOL_TOP 必须不低于 PMS_PLAN_TOP_N。日后在 PMS 侧调大 PMS_PLAN_TOP_N必须同步把这里的 POOL_TOP 调到不低于它,否则「能买、没分析」的缺口又会回来。
本次取值POOL_TOP 由 20 提到 50盖住 30 名的候选,另留 20 只强传导做观察POOL_MAX 由 60 提到 100容纳更深的计划加持仓加留池观察。
成本要心里有数夜间每只全量分析约两三分钟POOL_MAX 决定夜扫总时长。100 只大约四小时22:30 起约次日 02:40 完,仍在早上 08:40 盘前拉计划之前。嫌久就把 POOL_MAX 调小。
自动维护:入池、留池、出池由 pool.push() 每天在盘前链里维护,持仓永不出池,掉榜但形态没恶化的票留在池里继续观察,形态恶化的进回收站。名单不需要手工维护。
参数在哪POOL_TOP 和 POOL_MAX 在 akg-factor-bridge 的 .env 里PMS_PLAN_TOP_N 在 tradingSystem 的 config/settings.py 里,也可被 PMS 的 param_store 覆盖。改完 .env 要重建容器让新值生效,见第 5 节部署,注意 restart 不重读 env。