# 持仓系统 ↔ QMT 侧下游系统 · 数据与接口需求清单

> 用途：本清单由用户持有，与 QMT 侧（下游交易系统）协商。请下游按编号逐项答复（能提供/字段差异/时延/替代方案），答复直接回填本文档「答复」列，作为对接定稿依据。
> 背景：架构调整后，对下游的指挥权由决策系统（bionic_trader）移交持仓系统（tradingSystem/PMS）。`trading_order` / `trading_position` 两表仍归下游维护，PMS 只读；PMS 新增一条带数量的统一指令通道（见 B 部分）。
> 版本：**V2.0（2026-07-28）** —— QMT 侧已就六项问询答复，本版回填答复并调整通道方案。
>
> **本版最大变化：B 部分的表通道方案已废止**。双方商定指令下发与执行回报改走 **WebSocket 双向长连接**，协议细节见同目录 [`QMT_WS_PROTOCOL.md`](./QMT_WS_PROTOCOL.md)。B 部分保留仅供追溯，不再作为实现依据。A 部分（只读数据）与 C 部分（切换约定）继续有效。

---

## A. PMS 需要读取的数据（下游提供，153 代理可达、严格单表查询）

| # | 数据项 | 需要的字段 | 期望更新时延 | 用途 | 答复 |
|---|---|---|---|---|---|
| A1 | 持仓快照 `trading_position` | 股票代码（点式）、持仓数量、**可用数量（T+1 可卖）**、成本价（如有）、冻结数量（如有） | 盘中 ≤ 5 分钟 | 账本对账基准。当前 PMS 只确认过 `stock_code` 列可用，**请提供该表完整字段定义（DDL）**，尤其确认是否已有"可用数量"列——若无，PMS 自行按 T+1 规则推算 | **PMS 侧实机探明（2026-07-27，待下游确认语义）**：该表 14 列，含 `stock_code / stock_name / total_quantity / available_quantity / frozen_quantity / cost_price / market_price / market_value / profit_loss`。**可用数量列已存在**（`available_quantity`），本项数据需求实质已满足，PMS 已按此列对接。仍请确认：①`available_quantity` 是否即 T+1 可卖口径 ②更新时延 ③`stock_code` 格式（点式/前缀式） |
| A2 | 委托与成交 `trading_order` | 委托号、股票代码、方向、委托价、委托量、状态（**完整状态枚举文档**）、成交量、成交均价、委托/成交时间、来源标识 | 状态变更后 ≤ 1 分钟 | 成交回放入账（批次/成本）、指令执行确认。**请提供完整 DDL 与状态流转说明**（现掌握的 submitted/filled/completed/pending/failed 为推断口径，需正式确认） | **PMS 侧实机探明**：该表 20 列，已见 `order_id / strategy_id / stock_code / stock_name / order_side / order_quantity / order_price / target_price / order_status` 等。**仍缺两项，是本清单里最卡 PMS 的**：①状态枚举的正式定义 ②**成交来源标识**（哪条委托对应 PMS 的哪条指令）。在来源标识到位前，PMS 只能按「同股同向 + 下发早于成交 + FIFO」贪心认领，认领不上即判外部成交并告警——人工与 PMS 同时下单时会误判 |<br><br>**QMT 侧答复（2026-07-28）**：①**状态枚举**已给出 —— `submitted` 已提交 / `filled` **部分成交** / `completed` 已完成 / `pending` 挂单成功但 QMT 无响应 / `failed` 委托失败；撤单另有可靠终态。②**成交均价**将新增列。③**策略标识**字段表中已有，改造后可写入。<br><br>**PMS 侧结论**：本项**主链路已由 WebSocket 承接**，`trade` 逐笔成交消息带 `instruction_id`，来源标识问题自然消解。仍需注意两点，已并入新协议：⑴表中的 `strategy_id` 是指向 `t_trading_strategy` 的整数外键（因子策略表），只能回答「是不是 PMS 下的」，回答不了「是哪一条指令」，**不足以支撑回放认领**；⑵`filled` = 部分成交这个命名与字面含义相反，协议层改用 `PARTIAL` / `FILLED` 区分，映射见新协议 §7.2。**PMS 不再读 `order_status` 判断成交** |
| A3 | 账户资金快照（**新增需求**） | 总资产、可用资金、冻结资金、当日卖出可用资金（T+0 回笼） | 盘中 ≤ 5 分钟；日终必须 | 总规模校准与买入前资金校验。形式不限：新表 / 现有表 / HTTP 接口均可，请给出可行方案 | **已确认可提供**，走 WebSocket 实时回传。字段与消息格式见新协议 §5.7 `snapshot{kind:"funds"}`。**`sell_return_today`（当日卖出回笼）务必提供**，PMS 买入前的资金校验依赖它 |
| A4 | 成交回报明细（可选） | 若 A2 已含逐笔或聚合成交（成交量/均价），本项可免；否则请提供逐笔成交表 | 同 A2 | 部分成交场景的精确入账 | **已升格为必需项**：QMT 侧确认可回传「逐笔和最终结果」。**PMS 账本以逐笔 `trade` 消息为唯一入账依据**，`order_update` 仅作状态跟踪与终态校验。见新协议 §5.5 |
| A5 | 上游买入计划 `trading_buy_plan` | PMS 将作为该表的承接方（替代原决策系统 ENTRY_GATE 的角色）。请确认：①下游当前是否仍轮询 `is_active=6` 自动挂单？②切换后是否可以**停止**该轮询（统一走 B1 通道），或保留作为过渡（方案 X） | — | 旧通道处置（见 C2） | **未答复，仍待确认**（新协议 Q1）。另注：该表唯一约束为 `(stock_code, trading_time)` 且 `buy_amount` 是金额非股数，单股分批会撞约束——这也是通道改走 WebSocket 后不再受制于该表的原因之一 |

## B. ~~PMS 写入：统一指令通道~~ 【已废止 · 2026-07-28】

> **本节方案已被 WebSocket 通道取代，不再作为实现依据，保留仅供追溯。**
> 双方商定：指令下发与执行结果（逐笔成交、最终结果、落库结果）全部走 WebSocket 双向长连接；
> 幂等与防重放由「PMS 生成的 `instruction_id` + 签名 + 时间窗」保证，不再依赖表的唯一约束。
> **现行方案见 [`QMT_WS_PROTOCOL.md`](./QMT_WS_PROTOCOL.md)。**
> 下方 B1.1~B1.7 的协商点已在新协议中逐条落实：通道形式（§1）、响应节奏（§1 心跳/实时推送）、
> 部分成交（§5.4/§5.5）、撤单（§4.3）、拒绝码（§7.3）、市价语义（§4.2，改为**限价必填**）、建表归属（不再需要）。

### ~~B1 `pms_order_request` —— 带数量的统一买卖指令（建议表结构，可等价改为 Redis 流）~~

```sql
CREATE TABLE pms_order_request (
  id             BIGINT PRIMARY KEY AUTO_INCREMENT,
  instruction_id VARCHAR(64)  NOT NULL UNIQUE COMMENT '幂等键, PMS 生成, 重复插入应被拒绝',
  ts_code        VARCHAR(16)  NOT NULL COMMENT '点式, 如 600000.SH',
  side           VARCHAR(8)   NOT NULL COMMENT 'buy / sell',
  qty            INT          NOT NULL COMMENT '股数, 整百',
  limit_price    DECIMAL(10,2) NULL   COMMENT '限价; NULL=按下游默认方式',
  valid_until    DATETIME     NOT NULL COMMENT '有效期, 过期未执行由下游置 EXPIRED',
  source         VARCHAR(16)  NOT NULL DEFAULT 'pms',
  status         VARCHAR(16)  NOT NULL DEFAULT 'NEW'
                 COMMENT 'NEW→ACCEPTED→EXECUTING→FILLED / PARTIAL / REJECTED / EXPIRED / CANCELLED',
  exec_qty       INT          NULL COMMENT '已成交数量(聚合)',
  exec_avg_price DECIMAL(10,2) NULL COMMENT '成交均价(聚合)',
  reject_reason  VARCHAR(200) NULL,
  cancel_flag    TINYINT      NOT NULL DEFAULT 0 COMMENT 'PMS 置 1 请求撤单, 下游确认后置 status=CANCELLED',
  create_time    DATETIME     NOT NULL,
  update_time    DATETIME     NOT NULL,
  KEY idx_status (status), KEY idx_code (ts_code)
);
```

| # | 协商点 | 说明 | 答复 |
|---|---|---|---|
| B1.1 | 通道形式 | 表轮询（上述 DDL）还是 Redis 流？下游选定形式，字段语义不变 | |
| B1.2 | 轮询/响应节奏 | 下游多久拉一次 NEW？期望 ≤ 1 分钟；状态回写时延期望 ≤ 1 分钟 | |
| B1.3 | 部分成交 | 有效期内持续执行到 FILLED 或到期置 PARTIAL（exec_qty 如实回写）——可否按此语义？ | |
| B1.4 | 撤单 | PMS 置 cancel_flag=1 → 下游撤在途委托并回写 CANCELLED（已成交部分保留在 exec_qty）——可行？ | |
| B1.5 | 拒绝码 | 涨跌停/停牌/资金不足/数量非法等 reject_reason 枚举，请提供清单 | |
| B1.6 | 市价语义 | limit_price=NULL 时下游按什么方式执行（对手价/最新价±滑点）？ | |
| B1.7 | 建表归属 | 该表建在下游库还是 153 代理侧？（PMS 经 153 代理单表读写均可） | |

## C. 行为与切换约定

| # | 事项 | 说明 | 答复 |
|---|---|---|---|
| C1 | 幂等与重复防护 | instruction_id 唯一约束由表/流层保证；下游对同一 instruction_id 只执行一次 | |
| C2 | 旧通道停用清单 | 切换生效后，下游**停止**：①直接执行决策系统的卖出指令与盘中 ENTRY/EXIT 信号 ②（若 A5 确认）轮询 trading_buy_plan 自动挂单。此后下游只接受 WebSocket 通道指令。请确认停用方式与时点 | ⚠️ **本轮答复未涉及，是当前最大的未决项**（新协议 Q1）。不定死时点，切换当天两套系统会对同一只票同时下单。PMS 侧已完成信号订阅改造（PMS 现已自行消化决策系统信号），下游停用的前置条件已具备 |
| C3 | 灰度共存期 | 切换初期建议双轨观察 N 个交易日（旧通道只读不执行、B1 实际执行），请确认可行性 | |
| C4 | 时钟与代码格式 | 双方统一点式代码（600000.SH）与服务器时钟（NTP）；日期时间字段时区 Asia/Shanghai | |
| C5 | 故障约定 | 下游不可用时 PMS 指令停发并告警（PMS 侧守成）；下游恢复后不补执行已过期指令 | |
| C6 | 账号与权限 | PMS 需要的库账号/代理路由（读 A1-A5、读写 B1），请提供 | |

## D. 请提供的文档

| # | 文档 | 说明 | 答复 |
|---|---|---|---|
| D1 | `trading_position` / `trading_order` / `trading_buy_plan` 完整 DDL | 含索引与状态枚举正式定义 | **列名已由 PMS 实机探明**（`docker compose run --rm pms-web python scripts/check_db.py` 的 [3] 段会打印三表完整列定义，可直接贴进本表）：trading_position 14 列 / trading_order 20 列 / trading_buy_plan 25 列。**仍需下游正式提供的是语义而非列名**：状态枚举取值与流转、各数量列口径、索引 |
| D2 | 下游执行行为说明 | 委托拆单逻辑（如有）、涨跌停处理、集合竞价时段行为 | |
| D3 | 现有信号消费点清单 | 下游当前消费决策系统信号的全部位置（用于 C2 停用核对，防遗漏） | |
