Files
qmt_bridge/docs/桥接口清单与sfgrid满足度对照.md
T

254 lines
15 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.
# QMT Bridge 接口清单 & sfgrid 满足度对照
> 更新日期: 2026-08-26
> 适用范围: `qmt_bridge`(大 QMT 内策略桥,端口 8610)
> 对照对象: `C:\Users\Docker\Development\sfgrid`(网格策略客户端,当前走 miniQMT xtquant SDK 直连)
> 接口规范权威来源: `docs/api_spec/openapi.yaml`(主动维护,可同步 Apifox
---
## 〇、背景与结论速览
- **sfgrid 当前接入方式**: `core/qmt_real.py` 直接封装 `xtquant` SDK`XtQuantTrader` + `xtdata`),**不是** HTTP 桥。
- **目标**: 让 sfgrid 可以改走 qmt_bridge(或桥为其提供等价能力)。
- **一句话结论**: 桥的**查询类接口已满足** sfgrid 的持仓/资金/委托/成交/行情/涨跌停价/全量tick需求;**交易闭环(下单、撤单、异步回报推送)尚未实现**,是 sfgrid 迁移的主要缺口。
---
## 一、当前桥已实现接口(✅ 12 个)
### 1.1 通用
#### GET /health —— 健康检查
- **功能**: 检查桥是否存活,返回运行模式、端口、当前资金账号、桥版本。
- **返回**:
```json
{
"status": "ok",
"connect_info": {"mode": "big_qmt", "http_port": 8610, "ws_port": 8610, "account": "8882874667"},
"last_error": null,
"bridge_version": "2",
"hot_reload": true
}
```
### 1.2 data 命名空间(行情/数据)
#### GET /data/kline —— K线查询
- **功能**: 查询历史/实时 K 线,支持周期合成,按字段筛选。**查询前自动先下载基础周期数据**。
- **参数** (Query):
| 参数 | 类型 | 默认 | 说明 |
|------|------|------|------|
| code | str | 必填 | 股票代码,`600519.SH` 或 `600519` |
| period | str | `1d` | `tick/1m/5m/15m/30m/60m/1h/1d/1w/1mon` |
| start | str | `""` | 起始 `YYYYMMDD` |
| end | str | `""` | 结束 `YYYYMMDD` |
| count | int | `-1` | 条数,`-1`=全部;与 start/end 互斥 |
| fields | str | `open,high,low,close,volume,amount` | 逗号分隔,**不传=返回完整 OHLCV**;传了则只返回指定字段(白名单: `open,high,low,close,volume,amount,settle,openInterest` |
- **返回**(实测 2026-08-21, 600900.SH:
```json
{
"code": "600900.SH", "period": "1d", "count": 14,
"data": [{"time": "20260803", "open": 28.99, "high": 29.2, "low": 28.75,
"close": 29.05, "volume": 1018010.0, "amount": 2949045835.0}, ...]
}
```
- **底层**: 先 `download_history_data`(合成周期自动下载基础周期: 3m←1m, 15m/30m/60m←5m, 1w/1mon←1d),再 `ContextInfo.get_market_data_ex`。
- **fields 行为**(实测): 不传→完整 OHLCV;`fields=close`→只 close`fields=open,high`→只 open/high。
#### GET /data/quote —— 实时行情快照(单票)
- **功能**: 查询指定股票的**完整实时快照**(最新价、开高低收、昨收、成交量额、五档盘口等)。
- **参数** (Query): `code`(必填)。
- **底层**: `ContextInfo.get_full_tick([code])`。sfgrid 的 `getLastPrice()` 就是从 `get_full_tick` 里取 `lastPrice` 字段。
#### GET /data/tick —— 全推 tick 快照(批量)【2026-08-26 新增】
- **功能**: 透传 `get_full_tick(codes)`,返回多个代码的最新分笔快照。客户端按需主动拉取(WS 只推增量,不做 prime 推送)。
- **参数** (Query): `codes`(必填,逗号分隔,如 `600000.SH,000001.SZ`)。
- **返回**:
```json
{"ok": true, "data": {"600000.SH": {"timetag": "20260826 09:25:02", "lastPrice": 9.13, ...}}}
```
- **底层**: `ContextInfo.get_full_tick(codes)`。
#### GET /data/instrument —— 合约详细信息(含涨跌停价)
- **功能**: 查询合约详细信息,**含涨跌停价**(sfgrid `dailyUpStop`/`dailyDownStop` 所需)。
- **参数** (Query): `code`(必填)。
- **返回**(实测 600519.SH: `ExchangeID/InstrumentID/InstrumentName/OpenDate/PreClose/UpStopPrice/DownStopPrice/PriceTick/...`
- **底层**: `ContextInfo.get_instrument_detail(code)`。
#### GET /data/calendar/trading_dates —— 交易日历
- **功能**: 返回 [start, end] 区间交易日列表。可用于市场活跃判断。
- **参数** (Query): `start`、`end`YYYYMMDD,可空)。
- **返回**: `{"market":"SH","count":N,"dates":["20260803",...]}`
- **底层**: `ContextInfo.get_trading_dates`(大 QMT 签名 `(stockcode,start,end,count)`)。
#### 旧路径兼容(别名)
- `/kline` → `/data/kline`、`/quote` → `/data/quote`(保留,旧脚本零改动)。
### 1.3 订阅与推送(WS 单通道)【2026-08-26 新增】
> 设计详见 `docs/迭代记录/WS单通道推送设计.md`(已完成)。单通道 `/ws`,消息按 `type` 区分。
#### POST /data/subscribe —— 订阅数据(全量tick)
- **功能**: 订阅数据推送类型。当前支持 `whole`(全量tick增量推送)。
- **请求体**:
```json
{"type": "whole", "codes": ["SH", "SZ"]}
```
- **返回**: `{"ok": true, "sub_id": 7322507885117440}`snowflake 类唯一 ID
- **推送**: 订阅成功后,WS 连接 `ws://host:8610/ws` 收到增量推送:
```json
{"type": "whole", "data": {"600000.SH": {"lastPrice": 9.13, ...}}}
```
- **底层**: `ContextInfo.subscribe_whole_quote(codes, callback)`。
#### POST /data/unsubscribe —— 退订数据
- **功能**: 按 sub_id 退订某类型订阅。
- **请求体**: `{"sub_id": 7322507885117440}`
- **返回**: `{"ok": true}`
#### WS 端点 /ws —— 单通道推送
- **功能**: 客户端建立 WS 连接接收推送(心跳 30s ping/pong)。
- **推送消息**: `{"type":"whole","data":{code:tick_dict}}`(增量,只含变化品种)。
- **账号交易通知**trade_result/order_update/position_update/asset_update: 预留命名,**尚未实现**。
### 1.4 trade 命名空间(交易查询)
> 数据来自 QMT `get_trade_detail_data`**由策略线程(adjust/handlebar)定时刷新缓存**(当前 300ms),HTTP 线程只读缓存。账号为桥绑定账号(单账号策略),**无需 account/account_type 参数**。
#### GET /trade/positions —— 持仓查询【2026-08-26 增强】
- **功能**: 当前账号**全部持仓**,每行 = 语义化字段(xt 风格,sfgrid 可直接消费)+ 原始 `m_*` 字段超集 + 汇总 summary。
- **不做过滤**: 不提供 code/status 等过滤参数,客户端从全量中自取(sfgrid `getStockPosition` 本就以全部持仓为缓存)。
- **返回**:
```json
{"ok": true, "data": {"positions": [{"stock_code": "600519.SH", "stock_name": "贵州茅台",
"volume": 100, "available": 100, "frozen_volume": 0, "on_road_volume": 0,
"yesterday_volume": 0, "avg_price": 1500.0, "price": 1510.0,
"market_value": 151000.0, "open_price": 1500.0, "profit": 1000.0,
"profit_pct": 0.67, "direction": 48, "m_strInstrumentID": "600519", ...}],
"summary": {"count": 1, "total_market_value": 151000.0, "total_profit": 1000.0}}}
```
- **字段映射**: `m_strInstrumentID` → `stock_code`(补全后缀)、`m_nVolume` → `volume`、`m_nCanUseVolume` → `available`、`m_dOpenPrice` → `avg_price`/`open_price`、`m_dFloatProfit` → `profit`、市值/盈亏缺失时计算兜底。
- **底层**: 策略线程缓存(`get_trade_detail_data('POSITION')`300ms 刷新),HTTP 线程只读。
#### GET /trade/asset —— 资金/资产查询
- **返回**: `{"ok":true,"data":[{m_dBalance, m_dAvailable, m_dAssetBalance, m_dFrozenCash, ...}]}`
#### GET /trade/orders —— 委托查询(支持过滤)
- **功能**: 当日委托,支持三类过滤。
- **参数** (Query):
| 参数 | 说明 |
|------|------|
| code | 客户端按代码过滤(600519 或 600519.SH|
| status | `active`(排除已撤54/废单57)或数字状态码 |
| strategy_name | **服务端**按 passorder 策略名过滤(get_trade_detail_data 第4参数)|
- **返回**: `{"ok":true,"data":[{m_strOrderSysID, m_strInstrumentID, m_nOrderStatus, m_nVolumeTraded, m_dLimitPrice, ...}]}`
#### GET /trade/trades —— 成交查询
- **返回**: `{"ok":true,"data":[{m_strTradeID, m_strOrderSysID, m_strInstrumentID, m_dPrice, m_nVolume, m_strTradeTime, ...}]}`
---
## 二、sfgrid 实际需要的能力(代码调用点)
sfgrid 通过 `core/qmt_real.py` 的 `qmtv` 单例调用以下方法:
| # | qmtv 方法 | sfgrid 用途 | 调用位置 |
|---|-----------|------------|---------|
| 1 | `init_qmtv()` + `connect()` | 初始化并连接 QMT | `core/ui/tinker/app.py`、`core/ui/flet/app_v2.py` |
| 2 | `getAllPositions()` | 全部持仓展示/计算 | `core/ui/*/data_store.py`、`app_v2.py` |
| 3 | `getStockPosition(code)` | 单票持仓查询 | 同上 |
| 4 | `queryTodayOrders()` | 当日委托列表 | 同上 |
| 5 | `queryTodayTrades()` | 当日成交列表 | 同上 |
| 6 | `queryPendingOrder(code, tag)` | **按 strategy_name 过滤未成交挂单**(网格恢复核心) | `core/sfgrid/sfgrid_strategy.py` |
| 7 | `orderAsync(code, vol, type, price, priceType, remark, strategy_name)` | 下单(带 remark + strategy_name | 同上 |
| 8 | `xt_trader.cancel_order_stock_async(account, order_id)` | 撤单(用 order_id | 同上 |
| 9 | `dailyUpStop(code)` / `dailyDownStop(code)` | 涨跌停价(网格边界校验) | 同上 |
| 10 | `getLastPrice(code)` | 最新价兜底 | `data_store.py`、`app_v2.py` |
| 11 | `getInstrumentName(code)` | 股票名称 | 同上 |
| 12 | `isMarketActive`(属性) | 市场是否活跃(交易门禁) | `sfgrid_strategy.py` |
| 13 | `subscribe_whole_quote(['SH','SZ'], cb)` + `_on_market_data` | 全市场 tick 推送 | `core/qmt_real.py` |
| 14 | 回调 `on_order_stock_async_response` / `on_stock_trade` / `on_order_error` | 下单回报/成交/失败**异步推送** | `sfgrid_strategy.py` 事件订阅 |
**sfgrid 网格逻辑对桥的关键要求**:
1. 下单必须透传 **`remark`**(如 `BUY_3`)和 **`strategy_name`** —— 用于关联网格档位、恢复挂单。
2. 挂单查询必须支持**按 strategy_name 过滤**`queryPendingOrder(code, tag)` 等价)。
3. 撤单依赖 **order_id**(订单系统号),不是 remark。
4. 回报必须**异步推送**(下单成功/成交/失败),驱动网格状态机。
---
## 三、对照结论:已满足 / 未满足
### ✅ 已满足(桥可直接支撑)
| sfgrid 需求 | 桥接口 | 备注 |
|-------------|--------|------|
| 全部持仓 | `GET /trade/positions` | **已增强(2026-08-26**:语义化字段(stock_code/volume/available/avg_price/market_value/profit...+ summarysfgrid 无需再映射 |
| 资金资产 | `GET /trade/asset` | 同上 |
| 当日委托 | `GET /trade/orders` | 同上 |
| 当日成交 | `GET /trade/trades` | 同上 |
| 最新价 | `GET /data/quote` 或 `GET /data/tick`(取 `lastPrice` | 返回完整快照(含五档)|
| K线 | `GET /data/kline` | 桥已可用 |
| 涨跌停价 | `GET /data/instrument``UpStopPrice`/`DownStopPrice`| 已实现(实测 600519 → 1438.67/1177.09|
| 股票名称 | `GET /data/instrument``InstrumentName`| 已实现 |
| 全市场 tick 订阅 | `POST /data/subscribe` + WS `/ws``type=whole`| **2026-08-26 已实现**,增量推送 |
### ⚠️ 部分满足(可用但缺细节)
| sfgrid 需求 | 现状 | 缺口 |
|-------------|------|------|
| 市场活跃 `isMarketActive` | 无对应概念 | 桥没有暴露(可用 trading_dates + 时间判断)|
### ❌ 未满足(sfgrid 迁移必须补)
| sfgrid 需求 | 缺失的桥能力 | 建议实现 |
|-------------|-------------|---------|
| 下单 `orderAsync` | **`POST /trade/order`** | passorder 走 **adjust() 队列**(策略线程),透传 remark/strategy_name/价格类型 |
| 撤单 `cancel_order_stock_async` | **`POST /trade/cancel`** | cancel(队列→adjust),按 order_id 撤单 |
| 异步回报推送 | WS 只有 `whole`(行情)| WS 推送 `trade_result`submitted/order_update/dealt/order_error),账号交易通知预留未实现 |
| 下单状态查询(轮询兜底) | 无 | `GET /trade/order/status?rid=...` |
---
## 四、建议的 sfgrid → 桥 接口映射(未来实现)
| sfgrid 调用 | 桥端点(建议) | 说明 |
|-------------|---------------|------|
| `getAllPositions()` | `GET /trade/positions` | ✅ 已实现(语义化字段 + summary,直接可用)|
| `getStockPosition(code)` | `GET /trade/positions` 后客户端按 code 取 | ✅ 已实现(桥返回全部持仓,客户端过滤即可;已确认不做服务端过滤)|
| `queryTodayOrders()` | `GET /trade/orders` | ✅ 已实现 |
| `queryTodayTrades()` | `GET /trade/trades` | ✅ 已实现 |
| `queryPendingOrder(code, tag)` | `GET /trade/orders?strategy_name=xxx&status=active` | ✅ 已实现(服务端 strategy_name 过滤)|
| `orderAsync(...)` | `POST /trade/order` | **需新增**body 含 code/volume/opType/price/prType/remark/strategyName |
| `cancel(...)` | `POST /trade/cancel` | **需新增**body 含 orderId |
| `dailyUpStop/DownStop` | `GET /data/instrument?code=` | ✅ 已实现 |
| `getLastPrice(code)` | `GET /data/quote?code=` 或 `GET /data/tick?codes=` | ✅ 已实现 |
| `getInstrumentName(code)` | `GET /data/instrument?code=` | ✅ 已实现 |
| `subscribe_whole_quote` | `POST /data/subscribe` + WS `/ws` | ✅ 已实现(type=whole 增量推送)|
| 回报推送 | WS `trade_result`(账号通知)| **需实现**(预留命名)|
| `isMarketActive` | `GET /data/calendar/trading_dates` + 时间判断 | 建议扩展 |
---
## 五、官方接口依据(涨跌停价数据来源)
**涨跌停价不在行情快照里,而在合约详细信息里。**
- 官方接口: `ContextInfo.get_instrument_detail(code)`(根据代码获取合约详细信息)
- 涨跌停字段: `UpStopPrice`(涨停)/ `DownStopPrice`(跌停)
- 其他常用字段: `InstrumentName`、`OpenDate` 等
**对桥的启示**: `GET /data/instrument` 已实现,透传 `UpStopPrice`/`DownStopPrice`,同时满足 sfgrid 的 `dailyUpStop`/`dailyDownStop` 和 `getInstrumentName`。
---
## 六、桥的已知限制(文档留痕)
1. **线程约束**: `get_trade_detail_data`/`passorder`/`cancel` 只能在**策略线程**调用,HTTP worker 线程禁止直调(查询已用缓存/队列方案解决;下单/撤单将来必须走 adjust 队列)。`download_history_data`、`get_full_tick`、`subscribe_whole_quote` 在 HTTP 线程调用已验证可行。
2. **字段名**: 桥返回 QMT 原生 `m_*` 字段,sfgrid 消费时需自行映射(如 `m_strInstrumentID` → `stock_code`)。
3. **WS 推送**: 当前已实现 `whole`(全量tick增量);**账号交易通知**trade_result/order_update/position_update/asset_update)预留命名未实现。
4. **周期**: `/kline` 周期白名单 `tick/1m/5m/15m/30m/60m/1h/1d/1w/1mon`,合成周期自动先下载基础周期。
5. **单账号**: 桥是单账号(QMT 一个策略实例=一个账号),HTTP 接口无 account 参数;多账号=多桥实例(不同端口)。
6. **端口**: 桥监听 **8610**HTTP + WS 共端口)。