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

> 这份是给下一个对话窗口用的。详细节点在 `tradingSystem/DEVLOG.md`，那里按时间记着每一条
> 「做了什么 / 动了哪些文件 / 部署方式 / 真机判收 / 还欠着什么」。**这份只写坐标、状态和待办，
> 需要细节就去翻 DEVLOG，不要凭这份文件的概括去改代码。**
>
> 另一条更要紧的：**以项目代码为准，不要迷信文档。** 上一轮交接文档里有大量上一个对话
> 汇总时产生的幻觉（例如把已经存在的 `A_OPEN` 说成不存在）。读代码，不读文档的结论。

---

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

| 系统 | 位置 | 改码怎么生效 |
|---|---|---|
| **tradingSystem（PMS，仓位管理）** | `factor@factorevaluation` | 源码**打进镜像**，必须 `make deploy`；跑 `make test` 见 `ALL SUITES PASS`（当前 469 例）；盯盘 `make watch` |
| **bionic_trader（决策系统）** | `192.168.16.188:38000` | 挂载卷，`git pull` 后 `docker compose restart backend-api worker-brain`；**新增 env 必须 `docker compose up -d --force-recreate`**，restart 不重读 env |
| **akg-factor-bridge（因子桥）** | `factorevaluation`，容器 `akg_factor_bridge`，API 8300 | 常驻 API 改码要 compose restart |

**时区（08-06 查实、08-07 再证一次，按「哪种时间」分，不能按「哪个系统」一刀切）**

- **容器时钟与容器日志正文** —— 北京时间，**不加八小时**。证据：bionic 容器内 `date` = `15:37 CST`。
- **数据库时间列**（`strategy_audit_log.created_at`、`strategy_daily_results.updated_at`）—— **UTC，要加八小时**。证据：08-07 北京 09:03 时该库 `SELECT NOW()` = `2026-08-07 01:03:32`。
- **日期列**（`audit_date`、`trade_date`）—— 北京日期，不换算。
- `docker logs -t` 前缀是 UTC，跟正文里 Python logging 的时间不是一个口径，别混读。

> 上一轮交接文档写的是「bionic 容器时钟是 UTC，先加八小时」，**错的**，已在 DEVLOG 头部改正。

---

## 二、规矩（不变，逐条都被真实事故验证过）

1. **先出方案再动手。** 不要边想边改。
2. **中文可读性第一**：写完整自然句、不自造缩写、不发明术语。（我发明过「干跑」，项目自己的词是「试算」，被当场纠正。）
3. **只做加法。** 动线上系统只加不改，改既有行为要单独提、单独拍板。
4. **宁可空池。** 数据不干净就交空的，不要凑。
5. **关键路径禁止丢弃返回值**（静默失败是这个项目的头号 bug 模式，有 48 例专项单测盯着）。
6. **全程 Docker**，不给宿主机直跑 pip / python / psql。
7. **每条命令必须标注运行在哪台机器，一块代码里只放一台机器。**
8. **判收标准是真机各见一次**，不写「应该没问题」。
9. **写回 Mac 的文件以 written 回执加 md5 为准。**
10. **测试脚本由用户在实机上跑**，跑完把结果贴回来；不要自己跑复杂测试。
11. **里程碑要提示用户及时创建/上传到项目目录。**
12. **动 tradingSystem 以外的系统之前先答三题**，第一题是「是否影响了原有业务系统」。决策系统本来就不管仓位，要加仓位信息只能单独处理 PMS 那部分逻辑。

**三个不许重犯的误判**（外加这轮新增的第四个）

1. 把巧合当证据。
2. 时间不换算就下判断。
3. 推理没落地就当结论。（这轮又犯了一次：我推断「新建仓没部署，所以那些 `PMS_REJECT` 不可能来自它」，而用户当时已经盘中部署了。）
4. **新增：把「我没改过这个字符串」当成结论交付。** 今早那个「【手动重跑AKG_PLAN】」，正确做法是全仓库检索出唯一写入点、把调用链一行行走通、再用落库时刻查实，而不是止步于「不是我改的」。

---

## 三、当前系统状态（2026-08-07 开盘前）

- **新建仓（OPEN）自主提议已上线并跑过实盘**，`PMS_OPEN_AUTONOMY = full`，QMT 是模拟池。
- **研判闸已真正接通**（`PMS_JUDGE_API_BASE` 之前一直是空的，08-06 才第一次真发请求）。
- 决策系统的盘中买入信号已接进 PMS（此前一直被丢在门口）。
- 两个 bug 已修：研判请求体的 `Decimal` 序列化、运维口径文本污染股票评述。

**最近提交**

| 仓库 | 最新提交 | 部署状态 |
|---|---|---|
| tradingSystem | `bcf9ff0 08-07 09:13 处理评述问题` | ✅ 已 `make deploy` + `make test` 通过（用户确认） |
| bionic_trader | `6e82dcb 08-07 09:14 处理评述问题` | ⚠️ **未部署**，等收盘后 `docker compose restart worker-brain` |
| akg-factor-bridge | `d076c96 08-05` | 本轮未动 |

---

## 四、08-06 → 08-07 做完的事（细节见 DEVLOG 同名节点）

1. **新建仓动作，两侧代码写完**。PMS 侧动了 `action_engine`（新增 `eval_open` / `scan_open`）、`proposal_service`（`_scan_open` / `_route_one` 的 OPEN 分支）、`judge`（OPEN 硬数字裁剪，只送定性材料不送仓位数字）、`exec_advisor`（参照位漂移保护）、`exec_timing`（窗口末日强制完成只对命令生效）、`signal_rules` / `signal_service`（新增 `NOTE_BUY`）、`param_store` / `settings`。新增 `test_batch12_units.py` 44 例，全量 469 例通过。bionic 侧只动 `tasks_intraday.py` 的两个 `PMS_JUDGE` 块（48 增 4 删），不加 env 所以 restart 即可。
2. **决策系统买入信号接进 PMS**：并入既有 Redis 信号链，不轮询。
3. **窗口末日追高修正**：`hard_gate` 的 `is_command` 改成必填无默认值，自主买入在窗口末日不强制完成、到期作废（可以接受买不上）；卖出侧故意不动。
4. **跳过原因分得开**：`skip` 从集合改成带原因的字典，四种处境（在途提议 / 在途指令 / 规则闸拒过 / 研判闸驳回）各有各的话。
5. **研判闸的 `Decimal` 崩溃**（危害渐进，值得记住）：`_recent_ledger()` 把 `price_at`（DECIMAL 列）塞进请求体，`requests.post(json=)` 序列化不了。只要一只票的评审账本里有过任何一行，它的研判就必然失败，而账本只会越积越多——过几天几乎所有票的研判都会失败并降级人工确认，而每一条看起来都只是「研判不可用，降级人工确认」这种系统里天天有的正常降级。修法是在请求出口统一做 `jsonable()` 递归转换，不是修单个字段。
6. **运维口径护栏（今早）**：见下节。

---

## 五、今早这条单独说：【手动重跑AKG_PLAN】

**现象**：页面上部分股票的评述里冒出「【手动重跑AKG_PLAN】」这种看不懂的字段。

**根因**（全部源码验证，非推测）：`bionic_trader/scripts/rerun_pool.py:237-238` 拼出
`【手动重跑】手动重跑池 AKG_PLAN`，然后 `delay(c, reason, None, target_date)` ——
**第二个位置参数不是留痕字段，是 `reviewer_feedback`，风控官质询通道**。
文本不带「【盘中」前缀，`IntradayClassifier` 返回 None，于是掉进 `elif reviewer_feedback:`，
被拼进提示词的 `[📢 CRITICAL CHALLENGE FROM RISK OFFICER]` 段，后面跟着
`You MUST address this in your analysis`。那是命令，大脑照办写进 `analysis`，
再经 `save_to_database` 覆盖进 `strategy_daily_results.analysis_summary`。

**比字段本身重要得多的收获——悬项二从推测变成实证。** `rerun_pool` 落库是
`ON DUPLICATE KEY UPDATE`，把 `signal_type / support_level / pressure_level /
analysis_summary` 一起就地覆盖。查实的落库时刻（库时钟 UTC，下表已换算北京时间）：

| trade_date | 行数 | 落库时刻（北京） | 当时是什么时候 |
|---|---|---|---|
| 20260806 | 16 | 08-06 22:32 → 22:45 | 收盘后，安全 |
| 20260805 | 18 | 08-05 22:30 → **08-06 11:04** | **盘中**，改写的是当时正被读的昨夜结论 |
| 20260804 | 11 | **08-05 13:04** → 08-07 00:50 | **盘中**，补录历史日 |

这张表下游有两个读者：bionic `pms_advisor`（支撑压力位算 PMS 买卖区间、`signal_type` 走
`BAD_Y_SIGNALS` 闸）与桥 `pool.py` 的 `BAD_SIGNALS`（决定把哪只票请出候选池）。
所以「盘中不跑 push-pool」这条纪律不只针对桥，**`rerun_pool` 同样在内**。

**改法（两处，缺一不可）**：`rerun_pool.py` 下发固定传 `None`（根治），`--reason` 保留但只印在
脚本输出里；`tasks_brain.py` 加 `_is_ops_note()` 前缀护栏认
`【手动重跑】/【运维】/【回补】`，先打日志再置空（兜底）。只做根治不做兜底不行——脚本用法
示例明写着鼓励人手填 `--reason "调整X因子后重跑"`。新增
`scripts/selftest_ops_note_guard.py` 24 项，做过三次变异验证确认它真会失败。

**已污染的评述救不回来**：就地覆盖，没有历史表。

---

## 六、待办（按优先级）

**P0 — 收盘后第一件事**

- bionic：`git pull` → `docker exec trader_worker_brain python scripts/selftest_ops_note_guard.py`（见「全部通过」）→ `docker compose restart worker-brain`。没有新增 env，不用 `--force-recreate`；`backend-api` 没动，不用重启它。
- 判收第二步：收盘后跑 `rerun_pool.py --group AKG_PLAN --limit 1 --reason "护栏验证"`，确认 worker 日志有「🛡️ 运维口径说明已从提示词中剔除」，且该票新落库的 `analysis_summary` 不含「手动重跑」。

**P1**

- 队列里那三条 `PRP_..._OPEN` 提议要在页面上驳回（它们的研判从来没成功过，留着会挡住这三只票的重新评估）。**08-07 早上是否已驳回，未确认。**
- 第一周要盯的五个数：每天有几只候选落在买入区间内、研判驳回率、**研判 UNAVAILABLE 率**（Decimal 事故后新加的第五个）、`intraday_exec` 一跳的耗时分布、评审账本每天的行数。
- OPEN 的研判驳回率要连着看几天。08-06 是 11 条全 REJECT（14:07–14:38，理由都在谈开新仓，框架是对的），零 PASS。理由引用宏观空头/熊市共振，当天 100% 驳回可能是对的，但 OPEN 的判据里有**双重保守**（「标准应当比加仓类更严」＋「宁可错杀不可错放」），要盯几天看是不是压得过死。

**P2 — 需要用户拍板**

- **口径三**：信号票是否可以放宽「不追高」或择时闸。背景：信号池与候选池重叠只有 1/67，唯一重叠的 `300570.SZ` 倒在 `NO_CHASE_MA5: 距 MA5 12.22% > 上限 6%`。而那 67 条信号理由全是「放量突破 / 量比 8.55 / 站上关键位」——**按定义就是离 MA5 远的票**。所以口径一（信号只重排序、不放宽任何闸）结构上注定拿不到增量，只有口径三才有真增量。但它动的是「不追高、接受买不上」这条纪律，必须用户决定。
- `SZ000800` 有两行打架的定性：`trade_date=20260806` 是 `SELL`（08-06 22:38 写入），`trade_date=20260804` 是 `DROPPED`（08-07 00:50 写入，**晚了两个多小时**）。补录历史日会写出「时间更新、日期更旧」的行，取数方按 `trade_date` 排还是按 `updated_at` 排会拿到不同答案。`pms_advisor` 与桥 `pool.py` 两处口径要对一遍。
- 已污染的三批评述（16/18/11 行）要不要收盘后洗一遍。洗＝再跑一次全量重算，支撑压力位会再动，只能收盘后做。

**P3**

- 给 `industry.get_many` 的 `gp_stock_category` 分支加按日缓存（每跳省几十次库查询，但改的是既有函数的时序行为，单独提、单独拍板）。
- `rerun_pool.py` 里 `--dry-run` 的中文说明写的是「干跑」，项目里同类东西叫「试算」，措辞不统一。
- 文档补账：`WS_INTEGRATION_STATUS.md` 停在 07-30；`BIONIC_PMS_INTERFACE.md` 要补新建仓这一路（研判闸第五类动作、硬数字裁剪、择时侧零改动）。

---

## 七、悬而未决

| # | 事 | 状态 |
|---|---|---|
| 二 | 盘中改写 `strategy_daily_results` 顶掉昨夜结论 | **已从推测变实证**，08-05 / 08-06 各发生一次。PMS 侧有 `PMS_OPEN_REF_DRIFT_MAX` 漂移保护，但那是止损不是免疫 |
| 四 | 总资产与持仓市值的差额 | 已结：564（08-05）→ 51（08-06 盘中）→ **4.00 元**（收盘后），是时点与价格精度差，不是口径差。**但 `+ sell_return_today` 双算的假设没测过**（当天零卖出），仍开着 |
| 八 | `trade_no` 幂等键 | `trade_no` 是随机串（`T-SHADOW-420911c4b8`），不是确定性的 `SHADOW-xxxx#1`。回放到底按 `trade_no` 还是按单调的 `seq` 去重，未确认 |
| — | `trading_buy_plan` | 死枝：最新一行停在 07-30，ENTRY_GATE 已关（`ENTRY_GATE_ENABLED=False`），无重复买入风险 |

---

## 八、速查

**关键参数**：`PMS_AUTONOMY`、`PMS_OPEN_AUTONOMY`（新，默认 `full`）、`PMS_JUDGE_ACTIONS`（默认 `FILL,ADD,DCA,SWITCH,OPEN`）、`PMS_JUDGE_TIMEOUT=90`、bionic 侧 `PMS_JUDGE_TASK_TIMEOUT=75`、`PMS_OPEN_REF_DRIFT_MAX=0.03`、`PMS_JUDGE_TICK_BUDGET_SEC=150`、`PMS_OPEN_REQUIRE_WS_CASH=True`、`PMS_OPEN_SIGNAL_PRIORITY=True`、`PMS_PLAN_TOP_N=30`、`PMS_PLAN_TOP=300`。

**两道闸**：规则闸（`rule_gate.check`，纯代码，所有指令都要过）→ 研判闸（`judge.request` → bionic `POST /api/intraday/pms_judge`，LLM，30–90 秒，只管自主提议，命令驱动不过）→ 自主档位路由（`full` / `propose_only` / `off`）。

**择时**：实现A = bionic `POST /api/intraday/pms_exec`（读昨夜支撑压力位推买卖区间，秒级）；实现B = PMS 内置保守择时（`exec_timing.decide`）。

**文档**：`DEVLOG.md`（节点日志，**最先读这个**）、`NEW_POSITION_ACTION_PLAN.md`（新建仓方案 V3 + 八步判收）、`BIONIC_PMS_INTERFACE.md`（欠更新）、`README.md`。
