tradingSystem/BIONIC_PMS_INTERFACE.md

302 lines
18 KiB
Markdown
Raw Normal View History

2026-08-03 12:49:01 +08:00
# 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`(每晚判分全表扫描,已核实);文档与代码注释清理生造词 |