tradingSystem/BIONIC_PMS_INTERFACE.md

302 lines
18 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.

# PMS ↔ 决策系统 (bionic_trader) 接口契约 V1.1
> 待办 #9择时实现 A与 #10研判闸的实现依据。2026-08-03 定稿并双侧落码;同日按用户
> 审核意见把择时部分整个重做过一版(见 §7 变更记录),当前文本以重做后为准。
> PMS 侧代码:`app/services/judge.py`(原有)+ `app/services/exec_advisor.py`(新增)。
> bionic 侧代码:`app/api/main.py` 两个接口 + `workers/tasks_intraday.py` 新增
> `PMS_JUDGE` 分支 + `app/services/pms_advisor.py` + `config/settings.py` P 段。
> 机器口径PMS 跑在 factorevaluationbionic 跑在 4090 机API 宿主端口 **38000**)。
---
## 1. 分工与原则
设计 `POSITION_MGMT_DESIGN.md` §1/§7/§8 的落点:持仓系统管「做什么、多少」,决策系统管
「该不该、何时」。2026-08-03 用户补充的三条原则,两个接口都必须守:
1. **决策系统还在运行、还有其他职责,改造只做加法。** 新功能全部走新增分支、新增接口、
带默认值的新配置;原有六个盘中裁决分支、原有 API、每晚判分、建仓仲裁的输入输出一律
不动。凡是「顺手写进共享表」这类会影响既有功能的副作用,都要在本文写明并给出处置。
2. **提前计算为主、盘中监控为辅,不另做盘中判断。** 大模型推理放在凌晨(每晚的分析本来
就在产支撑位、压力位、定性结论),盘中接口只读凌晨结论和当天已有的监控产出,做比对、
不做判断。可以接受买不上——现价跑出执行区间就等待,不追。
3. **拿不到结论时,各按各的纪律降级。** 对 PMS 而言「通过」「驳回」「拿不到」是三件事:
研判拿不到 → 降级人工确认(绝不当通过,也绝不当驳回——驳回会真的杀掉提议并记一笔
驳回账);择时拿不到 → 退回 PMS 内置的实现 B绝不停出手。所以 bionic 侧任何给不出
结论的情况(解析失败、越界裁决、结论缺失、超时、总开关关)都回
`{"verdict": "UNAVAILABLE", "reason": ...}`,不许硬编一个看起来像结论的默认值。
另外三件事划清归属配额、14:45 的强制完成、停牌一字板、不追高(当日涨幅与均线偏离)、
仓位上限、一手、T+1 可卖——这些检查全部留在 PMS 本地(择时的在 `exec_timing.hard_gate`
其余在规则闸),请求根本不会送到 bionic。两个接口对 PMS 永远回 HTTP 200 + verdict 字段,
PMS 不需要区分状态码(网络层异常自然走 PMS 的超时分支,效果相同)。
---
## 2. 研判闸 `POST /api/intraday/pms_judge`#10走大模型慢是正常
PMS 调用方:`judge.py`(自主提议的二级关口,只审 `PMS_JUDGE_ACTIONS` 里的动作,默认
FILL/ADD/DCA/SWITCH命令驱动的动作不过研判闸。超时 `PMS_JUDGE_TIMEOUT`(默认 90 秒)。
### 2.1 请求PMS → bionic均为 PMS `judge.request()` 现有产出,未改)
```json
{
"direction": "PMS_JUDGE",
"action": "DCA", // FILL / ADD / DCA / SWITCH
"ts_code": "600000.SH", // 点式
"qty": 200,
"reason": "浮亏 -8.5% 触发第一档补仓评估", // PMS 动作引擎给出的提议理由
"hard_numbers": {"cushion_pct": -0.085, "stage": 1, "...": "PMS 已算好的硬数字"},
"context": {
"position": {"ts_code": "...", "avg_cost": 9.1, "total_qty": 1000,
"cushion_pct": -0.085, "support_ref": 8.9, "...": "账本快照"},
"recent_ledger": [{"at": "...", "action": "...", "arbiter": "...",
"verdict": "...", "price": 9.0, "reason": "..."}]
},
"must_answer": ["下跌是杀逻辑还是杀情绪"] // DCA 必带 (设计 §7)
}
```
### 2.2 bionic 侧处理
`process_intraday_audit` 新增 `PMS_JUDGE` 分支,复用既有仲裁哲学与代码路径(同一个大模型
呼叫、同一条审计轨迹),按动作类型给定性判据:
| action | 核心问题 | 驳回判据(示例) |
|---|---|---|
| FILL 回踩补足 | 回踩是健康洗盘还是结构转坏 | 放量破位 / 主力持续净流出 / 弱市无个股独立证据 |
| ADD 盈利加仓 | 趋势延续还是冲顶兑现 | 放量滞涨 / 已到压力位 / 主力借涨派发 |
| DCA 补仓 | **必答**:杀逻辑还是杀情绪 | 杀逻辑一律驳回;说不清 → 驳回 |
| SWITCH 调仓 | 换入侧证据是否成立 | — |
bionic 自动补三样 PMS 没有的上下文:实时资金分布(既有的上游接口)、板块动量与区制快照、
自己昨夜的 `strategy_daily_results` 结论。裁决收口只认 PASS/REJECT**越界与解析失败一律
回 UNAVAILABLE**§1 原则 3「证据不足以支持」按提示词导向驳回宁可错杀
**同股同动作半小时内不重复研判**`PMS_JUDGE_RETRY_TTL`,默认 1800 秒TTL 内直接回上次
结论,应答多一个 `"cached": true`。原因PMS 侧对研判驳回**有意**不做当日去重(其注释
「研判结论会变」),被驳回的候选可能每分钟重来——节流责任放在 bionic 侧,与建仓仲裁的
`ENTRY_GATE_RETRY_TTL` 是同一个思路。UNAVAILABLE 不缓存(瞬时故障要重试)。
**留痕只写 `strategy_audit_log`**verdict 记 `PMS_PASS` / `PMS_REJECT` /
`PMS_UNAVAILABLE`,该列 VARCHAR(20) 装得下,已核对建表语句),**不写 `decision_ledger`**
2026-08-03 复核 `outcome_scorer.py` 确认每晚判分对决策账本是全表扫描、不按裁决类型过滤,
建仓仲裁的历史注入也读整本账——PMS 的裁决写进去会混进这两个既有功能。PMS 自己的评审记录
本来就落在它侧的 `pms_action_ledger`arbiter=judge不需要在 bionic 重复记账。
### 2.3 应答bionic → PMS
```json
{"verdict": "PASS" | "REJECT" | "UNAVAILABLE",
"reason": "50字以内一句话", "confidence": 0-100,
"direction": "PMS_JUDGE", "ts_code": "600000.SH", "action": "DCA",
"cached": false}
```
PMS 侧映射(`judge.py` 现有逻辑未改PASS/APPROVE 一类 → 放行进档位分流REJECT/DENY
一类 → 驳回并记账;**其余一律按 UNAVAILABLE → 降级人工确认 + ERROR 告警**。
### 2.4 执行路径与超时
API 收到请求 → 投递 celery 任务(`intraday.process_audit`,队列 `PMS_JUDGE_QUEUE`)→
同步等结果(`PMS_JUDGE_TASK_TIMEOUT`,默认 75 秒)→ 回 JSON。三层超时必须保持
**75bionic 等任务)< 90PMS 等 HTTP**,否则 PMS 先断开、bionic 白算。
队列默认 `brain_queue`12 并发零部署改动若盘中全量重算把它塞住、PMS 侧频繁超时
降级再切独立队列§5.2),这是预埋的第二档不是必选项。
---
## 3. 盘中择时 `POST /api/intraday/pms_exec`#9读凌晨结论秒回
PMS 调用方:`exec_advisor.py`。只在 `PMS_EXEC_IMPL=A` 且过了本地检查后咨询;应答按
`valid_min` 缓存在指令 `progress.exec_advice`(上限 `PMS_EXEC_ADVICE_TTL_MIN`,默认 10
交易分钟),失败进入冷却(`PMS_EXEC_FAIL_COOLDOWN_MIN`,默认 5 分钟)。超时
`PMS_EXEC_TIMEOUT_SEC` 默认 8 秒——这个接口必须秒回,**大模型不进盘中路径**。
### 3.1 请求PMS → bionic
```json
{
"direction": "PMS_EXEC", "ts_code": "600000.SH",
"side": "buy" | "sell", "action": "OPEN|FILL|ADD|DCA|EXIT|TRIM",
"qty_left": 500, // 今日还该投放的量 (仅参考, 配额归 PMS)
"is_last_day": false, "tdays_left": 3, "now": "10:31",
"day": {"price": 10.0, "vwap": 10.05, "...": "PMS 的当日快照, 可整个留空"},
"refs": {"support": 9.5, "pressure": 11.0, "stop": 9.2, "source": "bionic"},
"position": {"total_qty": 0, "avail_qty": 0, "avg_cost": null, "cushion_pct": null}
}
```
bionic 只用其中的 `ts_code` / `side` / `day.price`(现价缺失时自己从 db13 分钟线取),
其余字段留作排查与将来扩展;区间一律按 bionic 自己库里的昨夜结论算,不用 PMS 转送的参考位
PMS 的参考位在结论停更时会换成它自己兜底算的值,那不能当决策系统的结论用)。
### 3.2 bionic 侧处理(`pms_advisor.py`,版本标记 `daily_band_v1`
**只有三步,全部是读现成的数、做比对,没有任何盘中判断:**
第一步,读昨夜结论。`strategy_daily_results` 里当晚算好的支撑位、压力位、定性。结论缺失、
超过 `PMS_EXEC_YSTRAT_MAX_AGE_DAYS`(默认 5 个自然日)、或支撑压力倒挂 → 回 UNAVAILABLE
PMS 退实现 B。由支撑压力推出两个执行区间推导是算术宽度是参数
```
买入区间 = [ 支撑 × (1 外沿0.01), 支撑 + (压力 支撑) × 0.3 ]
卖出区间 = [ 压力 (压力 支撑) × 0.3, 压力 × (1 + 外沿0.01) ]
只有单边参考位时, 区间宽度按该位的 3% 取, 另一侧回 UNAVAILABLE。
```
第二步,读当天监控。`strategy_audit_log` 今天对该股的裁决(盘前复审与盘中研判本来就在写
这张表PMS_ 开头的行是我们替 PMS 写的,读的时候排除)。今天已有离场类结论
REVERSAL_SELL / TAKE_PROFIT / FAIL→ 买入一律等待、卖出直接放行。监控查询失败不拦
执行(监控是「为辅」),但应答的 `missing` 里必须写明 `today_audit`
第三步,比对现价与区间:
| 情形 | 应答 |
|---|---|
| 买入,现价在买入区间内 | FIRE**限价 = 区间上沿**委托能一直活到价格离开区间不再是贴着现价的窄缝——2026-08-03 委托全部到期的病根即在此) |
| 买入,现价高于区间上沿 | WAIT「不追接受买不上」 |
| 买入,现价低于区间下沿(支撑位下方) | WAIT「区间外不买入」 |
| 买入,今天已有离场结论 | WAIT理由带上是哪条结论 |
| 卖出,现价到达卖出区间(或更高) | FIRE限价不给交回 PMS 按它自己的口径贴现价挂出) |
| 卖出,现价未到卖出区间 | WAIT「等高一点卖当日收尾的强制完成仍由 PMS 兜底」 |
| 卖出,今天已有离场结论 | FIRE与 PMS 已订阅的卖出信号同源,不会互相矛盾) |
### 3.3 应答bionic → PMS
```json
{"verdict": "FIRE" | "WAIT" | "UNAVAILABLE",
"limit_price": 9.95, // 买入=区间上沿; 卖出与等待时为 null (PMS 用本地口径)
"valid_min": 10, "reason": "现价 9.7 在买入区间 [9.4, 9.95] 内 (支撑 9.5/压力 11.0), 限价挂区间上沿",
"confidence": null,
"observed": {"price": 9.7, "support": 9.5, "pressure": 11.0,
"buy_band": [9.4, 9.95], "sell_band": [10.55, 11.11],
"y_signal": "BUY", "today_exit": null},
"missing": [], "advisor": "daily_band_v1"}
```
PMS 侧对建议价有一道保护:偏离现价超过 `PMS_EXEC_LIMIT_BAND`(默认 10%,即 A 股单日
涨跌幅上限)视为异常数据,改回本地口径并在理由里说明。区间边缘离现价百分之几属正常,
正常联调不会触发这条。
### 3.4 下一个迭代(本轮明确不做)
凌晨管线加一步大模型推理,对每只票明示写出买入区间与卖出区间,盘中改为直接读它——届时
只换本模块第一步的取数来源和 `advisor` 版本标记,应答结构与 PMS 侧都不动。动凌晨管线
属于重大迭代,做之前会先出细方案确认(覆盖范围、存放位置、算不出来怎么办)。
---
## 4. 拿不到结论时两侧各自怎么办
| 情形 | bionic 回什么 | PMS 怎么办 | 人看哪里 |
|---|---|---|---|
| 研判超时/队列拥堵 | UNAVAILABLE原因带超时 | 降级人工确认 + ERROR | PMS 日志 `[研判闸]`;频繁出现 → §5.2 切独立队列 |
| 研判裁决解析失败/越界 | UNAVAILABLE | 同上 | bionic 日志 `[PMS_JUDGE]` |
| 该股没有昨夜结论 / 结论过期 | UNAVAILABLE原因写明 | 本轮退实现 B + 冷却 5 分钟 | 指令 `progress.exec_advice.error``last_decision.source` 显示 `B(实现A不可用: ...)` |
| 收盘后 / 无实时价 | UNAVAILABLE「无实时价」 | 同上 | 同上 |
| 择时接口网络不通/超时 | —HTTP 层异常) | 同上 | 同上 |
| 当天监控表查询失败 | 照常按区间应答,`missing` 带 `today_audit` | 正常执行 | 应答的 missing 字段bionic 日志 |
| `PMS_API_ENABLED=False` | UNAVAILABLE原因写明开关 | 各按上述降级 | bionic `.env` |
| PMS 侧未配置(地址空 / `PMS_EXEC_IMPL=B` | 不会发请求 | 研判闸=人工确认;择时=实现 B | PMS 参数设置页 |
---
## 5. bionic 侧部署4090 机)
### 5.1 常规路径(本次交付走这条)
bionic 的源码是**挂载卷**compose 里 `.:/app`),与 PMS 打进镜像正相反——**不需要
build`git pull` 后重启进程即可**backend-api 带自动重载,稳妥起见一起重启)。新增
配置全部有默认值,`.env` 不用动:
```bash
# 【4090 机 · bionic_trader 仓库根目录】
git pull
docker compose restart backend-api worker-brain
# 本机自测 (两接口, 不需要 PMS 参与; 研判走大模型要等 30~90 秒):
docker compose exec backend-api python scripts/pms_smoke.py
```
改动面:`config/settings.py`P 段,全默认值)、`app/api/main.py`(两个接口)、
`workers/tasks_intraday.py`PMS_JUDGE 分支,含同股同动作的重复研判缓存)、
`app/services/pms_advisor.py`(新)、`scripts/pms_smoke.py`(新)。
既有六个盘中裁决分支、既有 API、每晚判分、建仓仲裁的行为一行未动PMS_JUDGE 是新增分支
且提前返回,不经过广播与重算;择时接口不写任何表)。
### 5.2 研判独立队列预埋第二档brain_queue 拥堵时再启用)
判据PMS 侧 `[研判闸] 请求失败 ... Timeout` 成为常态(偶发超时降级人工确认是设计内
行为不用切。启用bionic `.env``PMS_JUDGE_QUEUE=pms_queue`compose 加一个
专属 worker 后 `docker compose up -d worker-pms && docker compose restart backend-api`
```yaml
worker-pms:
build: {context: ., dockerfile: docker/Dockerfile}
container_name: trader_worker_pms
command: celery -A workers.celery_app worker -Q pms_queue -l info -c 2 --prefetch-multiplier=1
env_file: .env
environment: [TZ=Asia/Shanghai]
volumes: ["/etc/localtime:/etc/localtime:ro", ".:/app"]
depends_on: [redis]
networks: [trader_net]
restart: always
```
astock-kg 公告链 2026-07-23 单队列把交互请求饿死的同款处方:交互请求与长任务分开排队。)
### 5.3 bionic 侧新增配置一览(均有默认值)
| 键 | 默认 | 说明 |
|---|---|---|
| `PMS_API_ENABLED` | True | 总开关False = 两接口一律回 UNAVAILABLE |
| `PMS_JUDGE_QUEUE` | brain_queue | 研判任务队列 |
| `PMS_JUDGE_TASK_TIMEOUT` | 75 | API 等任务上限(秒),必须小于 PMS 侧的 90 |
| `PMS_JUDGE_RETRY_TTL` | 1800 | 同股同动作重复研判的间隔(秒),间隔内回上次结论 |
| `PMS_EXEC_BUY_BAND_RATIO` | 0.3 | 买入区间上沿 = 支撑 + (压力−支撑)×此比例 |
| `PMS_EXEC_SELL_BAND_RATIO` | 0.3 | 卖出区间下沿 = 压力 (压力−支撑)×此比例 |
| `PMS_EXEC_BAND_EDGE` | 0.01 | 区间外沿的放宽(买入下沿=支撑×(1此值),卖出上沿对称) |
| `PMS_EXEC_ONE_SIDE_BAND` | 0.03 | 只有单边参考位时区间宽度按该位的此比例取 |
| `PMS_EXEC_VALID_MIN` | 10 | 应答建议有效期(交易分钟) |
| `PMS_EXEC_YSTRAT_MAX_AGE_DAYS` | 5 | 昨夜结论超此自然日龄按过期处理 |
区间比例是初值,联调后按应答里的 `observed`(区间与现价的实际关系)回看再调。
---
## 6. 联调步骤与判收
前提factorevaluation 能路由到 4090 机的 38000 端口(同网段应当直通;不通先
`curl http://<4090内网IP>:38000/health` 排网络)。以下 `<BIONIC>` 代指
`http://<4090内网IP>:38000`
```bash
# 1.【4090 机】部署与本机自测 —— §5.1 的三条命令
# 2.【factorevaluation · tradingSystem 根目录】跨机探活 (只读, 不落表不产生指令):
docker compose run --rm --no-deps pms-web python scripts/probe_bionic.py --base <BIONIC>
# 判读: pms_exec 回 FIRE/WAIT 即通, 看应答里的区间对不对得上该股昨夜的支撑压力;
# 回 UNAVAILABLE 看原因 (无实时价=收盘后正常; 没有昨夜结论=该股不在每晚分析
# 范围, 换一只探)。pms_judge 回 PASS/REJECT 即通, 30~90 秒正常。
# 3.【factorevaluation】页面「参数设置」填 PMS_JUDGE_API_BASE=<BIONIC> → 研判闸生效。
# 验收: 下一轮自主提议扫描里, 需研判的动作 (FILL/ADD/DCA) 的评审账本出现
# arbiter=judge 的 PASS/REJECT 行 (make t-gate 看), 而不再是"降级人工确认"。
# 4.【factorevaluation】要切择时A: 页面把 PMS_EXEC_IMPL 改 A (随时可改回 B)。
# 验收: make watch 里在途指令的 last_decision 出现 source=A / A缓存;
# make t-ins 里 progress.exec_advice 有结论、区间理由与有效期。
# 5. 盘中观察一天: 每次咨询 bionic 侧日志有 [PMS 择时] 行; 对比实现A按区间给出的
# 出手/等待与实现B的差别 (关注限价挂区间上沿后, 委托是否不再成批到期)。
```
判收纪律照旧:**代码就绪 ≠ 接通,接通 ≠ 判收**。#10 判收 = 一笔真实提议走完
「规则闸 → 研判通过/驳回 → 档位分流」并在 PMS 评审账本留下 arbiter=judge 的行;
#9 判收 = 盘中一条指令按区间出手或等待、且对端故障时当轮自动退实现 B 有留痕
`source=B(实现A不可用)`)。
## 7. 变更记录
| 版本 | 日期 | 内容 |
|---|---|---|
| V1.0 | 2026-08-03 | 首版定稿并双侧落码。择时部分是一套盘中判定规则(资金阈值、动量追买等) |
| V1.1 | 2026-08-03 | 按用户审核意见重做择时:删掉 V1.0 的盘中判定规则(违反「提前计算为主、盘中监控为辅、不另做盘中判断、可以接受买不上」),改为由昨夜支撑压力推出执行区间、盘中只做区间比对与当日监控核对;研判留痕不再写 `decision_ledger`(每晚判分全表扫描,已核实);文档与代码注释清理生造词 |