chore: init qmt_bridge repo (HTTP+WS bridge, MCP endpoint, docs, references)

This commit is contained in:
Docker
2026-08-26 16:53:15 +08:00
commit 22a5b8ca04
210 changed files with 68176 additions and 0 deletions
@@ -0,0 +1,253 @@
# 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 共端口)。