# 交接：三系统量化交易 · 2026-08-06 收盘前

> 这份东西是写给下一个对话开头的。可以整段贴过去，也可以留在仓库里当阶段存档。

我们在推进三个相互衔接的量化交易系统。请先读项目记忆里的 **tradingsystem-bionic-bridge**、
**clock-and-timezones**、**tradingsystem-silent-failure** 三篇（都在 08-06 更新过），
背景需要时再读 tradingsystem-ws-channel 和 quant-factor-synthesis，工作方式看
feedback-plan-first 和 writing-style-readability。读完再看下文，然后等我给任务，不要自行开工。

---

## 一、三系统与部署方式（改错部署方式等于白改）

**tradingSystem（PMS，持仓管理）** 跑在 `factor@factorevaluation`。源码是打进镜像的，
所以改码必须 `make deploy`，跑完再 `make test` 看到 ALL SUITES PASS；盯盘用 `make watch`。
**容器时钟是北京时间。**

**bionic_trader（决策系统）** 跑在 `192.168.16.188:38000`（tlaipro 机）。挂载卷，
`git pull` 之后 `docker compose restart backend-api worker-brain` 即可生效；
但**新增 env 必须 `docker compose up -d --force-recreate`，`restart` 不重读 env**。
**容器时钟是 UTC。**

**akg-factor-bridge（因子桥）** 在桥机 `factorevaluation`，容器名 `akg_factor_bridge`，
API 在 8300。常驻 API 改码要 compose restart。**容器时钟是 UTC。**

线上运行中的系统只做加法。

## 二、规矩（不变）

先出方案再动手；中文可读性第一，写完整自然句、不自造缩写；只做加法；宁可空池；
关键路径禁止丢弃返回值；全程 Docker，不给宿主机直跑 pip/python/psql；每条命令必须标注
运行在哪台机器，一块代码里只放一台机器；判收的标准是真机各见一次；写回 Mac 的文件以
written 回执加 md5 为准；**看桥和 bionic 的任何时间戳，先加八小时再和对话里的时间对齐。**

测试脚本由我在实机上运行，跑完把结果发给你，你不用跑复杂测试。里程碑的代码和文档
请提示我及时创建或上传到项目目录。

## 三、下一个任务：给动作引擎加「新建仓」动作

**要解决的问题**：不能每天靠人手工走一遍下命令、排方案那一串。正常应该是上游选股计划
出现之后，PMS 按规则自己执行。

**已经查清的现状，不用重查**：整条链除了「下命令」这一步，其余全是自动的。调度表里
08:40 拉候选池，08:50 盘前准备，**每分钟**跑命令轮询（把已下达的命令排成方案）、盘中执行
（方案转指令 + 自主提议扫描 + 择时出手）、成交入账加轻对账、消化决策系统的盘中信号，
14:50 T 仓平回，15:10 日终结算加窗口收口，15:30 日报。`make t-*` 只是这些调度位的手动
等价物，是排查用的，不是日常流程。

**唯一要人的就是下命令，而且是刻意的**：计划回答「能买哪些」，命令回答「今天打算投多少」。

**三条路，性质完全不同**：

一是把下命令挂成定时，每天自动下一条升仓命令。改动最小，加一个调度位；代价是仓位单向
增长到撞上组合上限，而且不看市场状态。

二是把自主档位调到 full。**这条解决不了问题**——它只让已有持仓的增持类提议不用人确认，
而动作引擎产出的只有补足底仓、回踩补仓、盈利加仓、减持这四类，**根本没有「新建仓」
这个动作**。

三是给动作引擎加新建仓动作，让自主提议除了管已有持仓，也能从候选池里挑新票，走同样的
规则闸、研判闸、档位分流。这才对得上要解决的问题，是新逻辑不是新调度位。

**我选第三条。先出方案，我拍板之后再动手。**

**开工前要知道的约束**：择时读的那张 `strategy_daily_results` 表，在盘中跑 push-pool
触发补扫时会被就地改写（详见第六节）。开发期靠「盘中不跑 push-pool」这条纪律规避，
没有改代码。设计新建仓动作时要把这件事放进考虑，别让无人值守的建仓建在会漂移的输入上。

## 四、当前系统状态（2026-08-06 盘中，约 11:15）

账户总资产 2,000,288.86，可用 1,881,764.86，组合仓位 5.92%。参数：总规模 200 万，
自主档位 propose_only，下发模式 ws，择时档位 A，组合上限 70%，单股上限 8%，最多 20 只，
执行窗口默认 3 个交易日。

**持仓三只**：600150.SH 一千七百股、可卖一千二、摊薄成本 34.625、安全垫 +0.16%，参考位
支撑 34 压力 37 止损 34（来源 bionic）；002335.SZ 一千九百股、可卖零、摊薄成本 30.984、
安全垫 +0.41%；000035.SZ 一百股、可卖零、摊薄成本 5.028、安全垫 -1.55%，**决策系统对它
的定性是 SELL**。

**在途四条**：000035 的减持一百股（来源自主提议，11:13 我在页面人工放行）；002335 的清仓
一千九百股（来源决策系统风控信号，置信度 95%）；002518 的买入九百股（**被新装的定性闸
拦着**，窗口今天到期）；000063 的买入一千七百股（现价高于买入区间上沿，不追，窗口今天到期）。
前两条都要等明天 T+1 才能出手。

## 五、08-05 到 08-06 做完的事

**买入侧补上了一道闸，两侧都已部署，真机判收成立。** 起因是发现候选层的结论对执行层
完全不可见：桥用 `strategy_daily_results.signal_type` 落在 SELL、AVOID、DROPPED 就把票请出
候选池，而决策系统的择时应答读同一个字段、放进 `observed.y_signal` 原样回传却从不用它判断，
PMS 又只解析 verdict、limit_price、reason。同一条结论一边当判决一边当摆设。
600841 就差一根 3.3% 的阴线会被买进来。

改动：决策系统 `app/services/pms_advisor.py` 加 `BAD_Y_SIGNALS` 常量和买入分支的闸，
`config/settings.py` 加开关 `PMS_EXEC_BLOCK_BAD_Y_SIGNAL`；PMS 侧 `app/core/rule_gate.py`
加 `BAD_SIGNAL` 检查项并把定性无条件写进 hard_numbers，`app/services/exec_advisor.py`
把 `observed.y_signal` 存进 advice 并挂到决策上，`app/services/executor.py` 传进规则闸 flags，
`scripts/test_batch3_units.py` 加一条用例。**08-06 真机拦下第一笔：002518 停在 PROPOSED、
成交 0、理由是新写的那句。** PMS 那道 `BAD_SIGNAL` 至今没真的触发过（决策系统先回 WAIT
就走不到规则闸），符合当初「第二道闸平时空转、只为留痕」的预期。

其余：600841 的在途买入指令已撤（回执是 `cancelled:0` 加「仅本地置撤销」，PROPOSED 从没
下发过子单时走的就是这个分支）；freeze 的 manifest.json 判收通过，08-03 和 08-04 两天
都在；XXL 例行调度的根因查实——从来没有自动拉起过，日志里只有手动触发的行，配置已重新设计。

## 六、悬而未决（按优先级）

1. **主线**：动作引擎加新建仓动作，见第三节。
2. **昨夜结论会被盘中补扫就地改写**。push-pool 跑完触发增量补扫，补扫用 `trade_date=昨日`
   就地覆盖已有那行，而 `fetch_yesterday_strategy` 是取最新一行、不按日期过滤。实证：
   000035 在 09:53 成交时定性是 WATCH、压力 5.2，补扫之后变 SELL、压力 5.15；002335 的支撑
   从 31.27 变成 29.00，差 7.3%。**开发期靠「盘中不跑 push-pool」规避，代码未改。**
   真要修，最小加法是给 `fetch_yesterday_strategy` 加「只取今天之前」的过滤，但那样盘中
   补扫的产出就完全用不上了，是不是想要的要先拍板。
3. **卖出侧的镜像缺口**。桥对「持仓且形态恶化」只警示、明确不出池，而 PMS 没有任何机制
   会因为决策系统的定性去减仓——研判闸只管买入侧的补足、加仓、补仓，规则闸的冻结和黑名单
   只挡增持。买入侧补上了，卖出侧还是空的。000035 现在就是这个状态。
4. **总资产与持仓市值的口径**。总资产来自 QMT 的 ws 资金快照，被组合刹车当权益基准用
   （高水位与回撤，15:10 跑）；持仓市值是 PMS 拿 Redis 分钟线自算的。08-05 两边差 564 元，
   08-06 盘中只差 51 元。倾向是取数时点差不是口径差，收盘后价格静止时再对一次才能定案。
5. **时区显示错位八小时**。`_today_exit_verdict` 把 created_at 直接截成「01:35」拼进理由，
   传到 PMS 页面和北京时间戳并排显示。给 bionic 容器设 `TZ=Asia/Shanghai`（新增 env，
   必须 force-recreate）或者格式化时显式转。低优先。
6. **600150 最后五百股买在区间上沿**。区间连续四个交易日都是 [33.66, 34.9] 没变过，
   今天现价正好等于上沿，闭区间判定放行，成交在 34.89，摊薄成本被从 34.514 抬到 34.625、
   安全垫从峰值 +1.99% 压到 +0.01%。这是分批建仓遇上区间长期不变的固有副作用。
   要不要改（现价等于上沿时不买，或者区间连续 N 天不变时降档）是策略取舍，未拍板。
7. **XXL 例行调度已重新配置**，验证点是看桥的 `data/xxl_build.log` 有没有在北京时间 07:10
   自动长出新行（日志是 UTC，会写成前一天的 23:10）。
8. `trade_no` 格式核验（关系 S3 收尾函第一节的阻断项）：
   `docker compose run --rm --no-deps pms-web python scripts/ws_smoke.py inbox --type trade --width 600`。
   是 `SHADOW-xxxx#1` 这种确定式就闭环，还是 T-SHADOW 随机串就继续追。这条一直没做。
9. 命令目录里 `FREEZE_STOCK` 的说明写着「在途买入撤销」，与实现不符（它是参数类命令、
   没有 planner，只让规则闸的冻结项拦），文案要补账。
10. 文档补账：`WS_INTEGRATION_STATUS.md` 停在 07-30；`BIONIC_PMS_INTERFACE.md` 第三节要
    加一句新闸；README 待办 9 和 10 标判收。
11. 老悬项不变：分歧票候选层拦截未拍板；AKG_PLAN 并入 TECH_POOL 的共管规矩未拍板；
    07-29 那 19 条 SIG_INVALID 未查明。

## 七、这轮我犯过的三个误判，别重犯

**一是看见巧合就当线索。** 600150 的现价 34.900 恰好等于两笔委托的限价，我据此断定取价
有问题，还推出「安全垫其实是负的」。实际行情源是活的、分钟线连续，34.900 就是当时的
真实价。**先验证数据源是否在更新，再怀疑取值。**

**二是没换算时区。** 审计行写着 01:35，我当成北京时间，于是把一个正常的四分钟赛跑
（09:31 咨询干净、09:32 成交、09:35:51 结论落库、09:36 信号到达）判成了「老闸漏读」的 bug，
差点去改代码。**桥和 bionic 的时间戳一律先加八小时。**

**三是推理没落地就当结论。** 我推定 `audit_date` 会因为容器在 UTC 而错位一天，实际写入侧
用的是北京日期，洞根本不存在。**时间相关的猜测必须先查实际数据再下判断。**
