@@ -1,544 +0,0 @@
# R-007 交易记录接入(订单与成交数据)· 需求与技术研究
> 需求状态:**起草**(2026-09-01 入池,需求与技术研究进行中;AI 提议提为「讨论中」,待老师确认)
> 更新日期:2026-09-01 | 来源:老师指令(2026-09-01)
> 关联:docs/05-需求池/需求池索引.md( R-007)、docs/01-终极目标/终极目标.md(目标-006 交易复盘、目标-003 按策略监控市场)
> 前置研究:本文件已含第一轮技术研究(QMT Bridge 订单/成交 API 实测 + 现有架构集成分析),待确认问题 Q1-Q10 待老师逐条拍板
## 需求概述
在神之一手的**「交易记录」tab**(当前为占位)中接入 **QMT Bridge 的订单(当日委托)与成交(当日成交)数据 ** ,实时展示当日委托与成交明细,支撑**交易复盘**(目标-006)与**按策略监控交易**(目标-003)。
- **订单数据**:当日委托列表(含委托状态、委托量、成交量、限价、时间等);
- **成交数据**:当日成交列表(含成交价、成交量、手续费、成交额、时间等);
- **展示位置**:现有「交易记录」tab( conversation.view, id=odl-trade-records, tradeRecords 显隐开关已预置,当前渲染 PlaceholderTab 占位);
- **数据来源**:插件服务端直连 QMT Bridge REST(技术约束-003,不经过 MCP 层),复用现有统一数据源抽象(技术约束-001)。
## 现状分析
### 1. QMT Bridge 已具备订单/成交查询能力(API 面实测)
| 端点 | 方法 | 说明 | 过滤参数 |
|---|---|---|---|
| /trade/orders | GET | 当日委托列表(m_* 原始字段),账号为桥绑定账号(单账号策略) | code(客户端过滤,600519 或 600519.SH)、status( active=排除已撤54/废单57,或数字状态码 48-57)、strategy_name( QMT 服务端按 passorder 策略名过滤) |
| /trade/trades | GET | 当日成交列表(m_* 原始字段) | 无(返回全部当日成交) |
**数据特性 ** :两类数据均由 QMT 策略线程(adjust)每 **300ms 缓存刷新 ** ,HTTP 线程只读缓存——即数据本身是准实时的(约 300ms 延迟),服务端无需自建订阅,REST 轮询即可。
### 2. 数据结构(OpenAPI 规范 + 线上实测样本)
**Order(当日委托)核心字段 ** :
| 字段 | 类型 | 说明 |
|---|---|---|
| m_strOrderSysID | string | 合同编号/委托号(撤单凭此号) |
| m_strInstrumentID / m_strInstrumentName | string | 证券代码(不带后缀)/ 名称 |
| m_nDirection | int | 操作标记(EEntrustBS: **股票买卖永远是 48**,不表达方向,官方已注明;见 Q3 研究) |
| m_nOrderStatus | int | 委托状态码:48=未报 49=待报 50=已报(待成交) 51=已报待撤 52=部成待撤 53=部撤 54=已撤 55=部成 56=已成 57=废单 |
| m_nVolumeTotalOriginal / m_nVolumeTraded / m_nVolumeTotal | int | 委托数量(最初)/ 已成交量 / 剩余量 |
| m_dLimitPrice | number | 委托价格(限价单限价) |
| m_strInsertDate / m_strInsertTime | string | 委托日期(YYYYMMDD) / 时间(HHMMSS) |
| m_dTradedPrice | number | 成交均价(股票) |
| m_strErrorMsg / m_strCancelInfo | string | 状态信息 / 废单原因 |
| m_strOptName | string | **操作名称(实测「限价卖出」,含方向语义,方向判定关键字段) ** |
**Trade(当日成交)核心字段 ** :
| 字段 | 类型 | 说明 |
|---|---|---|
| m_strTradeID | string | 成交编号 |
| m_strOrderSysID | string | 合同编号/委托号(与委托表同号,**用于关联委托↔成交**) |
| m_strInstrumentID / m_strInstrumentName | string | 证券代码(不带后缀)/ 名称 |
| m_nDirection | int | 操作标记(同订单,股票恒为 48,不判方向) |
| m_dPrice | number | 成交均价 |
| m_nVolume | int | 成交量(股) |
| m_strTradeDate / m_strTradeTime | string | 成交日期(YYYYMMDD) / 时间(HHMMSS) |
| m_dCommission | number | 手续费 |
| m_dTradeAmount | number | 成交额 |
| m_strOptName | string | 操作名称(含方向语义) |
**线上实测样本 ** ( 2026-09-01):当日 1 笔委托 + 1 笔成交(博纳影业 001330.SZ,限价卖出 200 股 @6 .00,已成 m_nOrderStatus=56,手续费 0.852 元)。方向判定结论(官方 innerApi 字典核实):m_nDirection=48 是 EEntrustBS 的「买卖」操作标记(官方注明股票买卖永远是 48),**不表达方向**;真实方向在 m_nOffsetFlag( EOffset_Flag_Type: 48=买入开仓/49=卖出平仓,实测 49 与「限价卖出」吻合);m_strOptName 为买卖标记中文展示。**方向判定应以 m_nOffsetFlag 为准 + m_strOptName 交叉验证(Q3 已解决)**。
### 2.1 QMT 桥返回字段全表(实测 2026-09-01)
> 实调 QMT 桥 /trade/orders 与 /trade/trades 得到的完整字段。标注 ★ 的字段为**语义化映射应保留的核心字段**(股票场景),其余为期权/期货/人民币换算/融资融券等场景字段(股票时为空或默认值)。
> 字段体系:大 QMT 内置 API( innerApi),枚举见 https://dict.thinktrader.net/innerApi/enum_constants.html
**当日委托(/trade/orders)字段表 ** :
| 字段 | 类型 | 实测值 | 说明 | 映射 |
|---|---|---|---|---|
| m_strOrderSysID | string | 645009273 | 合同编号/委托号(唯一标识,撤单凭此号) | ★ |
| m_strOrderRef | string | SZ_645009273 | 委托引用号(带交易所前缀) | ★ |
| m_strAccountID | string | 8882874667 | 资金账号 | |
| m_strAccountKey | string | 2____10125____… | 账号组合键(内部用) | |
| m_strAccountName | string | (空) | 账号名称 | |
| m_strAccountRemark | string | (空) | 账号备注 | |
| m_strInstrumentID | string | 001330 | 证券代码(不带后缀) | ★ |
| m_strInstrumentName | string | 博纳影业 | 证券名称 | ★ |
| m_strExchangeID | string | SZ | 交易所(SH/SZ) | ★ |
| m_strExchangeName | string | 深交所 | 交易所中文名 | |
| m_strUnderCode | string | (空) | 标的代码(衍生品用) | |
| m_strProductID / ProductName | string | (空) | 产品代码/名称(股票为空) | |
| m_nDirection | int | 48 | EEntrustBS 操作标记:**股票买卖恒为 48**(非方向) | |
| m_nOffsetFlag | int | 49 | **EOffset_Flag_Type: 48=买入开仓 / 49=卖出平仓(方向判定关键) ** | ★ |
| m_nHedgeFlag | int | 49 | 投保标记(投机=49) | |
| m_nOrderPriceType | int | 50 | 价格类型(50=限价,enum_EBrokerPriceType) | |
| m_eEntrustType | int | 48 | 委托类型(48=普通委托) | |
| m_strOptName | string | 限价卖出 | **买卖标记中文(方向展示/交叉验证) ** | ★ |
| m_strOption / OrderParam / OrderStrategyType | string | (空) | 期权属性/委托参数/策略类型名 | |
| m_nOrderStrategyType / m_nOpType | int | 异常值 | 策略类型码/操作类型(无实际意义) | |
| m_nVolumeTotalOriginal | int | 200 | 委托数量(最初委托量) | ★ |
| m_nVolumeTraded | int | 200 | 已成交量 | ★ |
| m_nVolumeTotal | int | 0 | 剩余量(总委托-成交) | ★ |
| m_dLimitPrice | number | 6 | 委托价格(限价) | ★ |
| m_dTradedPrice | number | 6 | 成交均价 | ★ |
| m_dTradeAmount | number | 1200 | 成交金额 | ★ |
| m_dCancelAmount | number | 0 | 已撤数量 | |
| m_dOrderPriceRMB / TradeAmountRMB / ReferenceRate | number | 0 | 人民币换算/参考汇率(外币标的) | |
| m_dFrozenCommission / FrozenMargin / ShortOccupedMargin | number | 0 | 冻结手续费/保证金/卖空占用保证金 | |
| m_eCashgroupProp / m_eCoveredFlag | int | 48 / 0 | 资金组属性/遮盖标记 | |
| m_nOrderStatus | int | 56 | 委托状态:48未报 49待报 50已报 51已报待撤 52部成待撤 53部撤 54已撤 55部成 56已成 57废单 | ★ |
| m_nOrderSubmitStatus | int | 51 | 委托提交状态(51=已提交) | |
| m_strErrorMsg / m_strCancelInfo | string | (空) | 状态信息/废单原因 | |
| m_nErrorID | int | 2147483647 | 错误码(默认值=无错误) | |
| m_strInsertDate / m_strInsertTime | string | 20260901 / 101333 | 委托日期(YYYYMMDD) /时间(HHMMSS) | ★ |
| m_nStrategyID | int | 4322211943619263000 | 策略 ID( passorder 所属) | |
| m_strSource / m_strXTTrade | string | (空)/其他终端 | 委托来源/交易终端 | |
| m_nTaskId / FrontID / SessionID / Ref / GroupId | int | -1/2147483647 等 | 任务号/前置机/会话/引用/组号(无实际意义) | |
| m_strCompactNo / BrokerName / LocalInfo / Remark | string | (空) | 合约编号/券商/本地信息/备注 | |
| m_bEnable | bool | true | 是否有效 | |
| m_xtTag | null | null | 扩展标签 | |
**当日成交(/trade/trades)字段表 ** :
| 字段 | 类型 | 实测值 | 说明 | 映射 |
|---|---|---|---|---|
| m_strTradeID | string | 0103000030812084 | 成交编号(唯一标识) | ★ |
| m_strOrderSysID | string | 645009273 | **合同编号/委托号(与委托表同号,关联委托↔成交) ** | ★ |
| m_strOrderRef | string | SZ_645009273 | 委托引用号(带交易所前缀) | |
| m_strAccountID | string | 8882874667 | 资金账号 | |
| m_strAccountKey / AccountRemark | string | (空) | 账号组合键/备注 | |
| m_strInstrumentID | string | 001330 | 证券代码(不带后缀) | ★ |
| m_strInstrumentName | string | 博纳影业 | 证券名称 | ★ |
| m_strExchangeID / ExchangeName | string | SZ / 深交所 | 交易所 | ★ |
| m_strProductID / ProductName | string | (空) | 产品代码/名称(股票为空) | |
| m_nDirection | int | 48 | EEntrustBS 操作标记:**股票买卖恒为 48**(非方向) | |
| m_nOffsetFlag | int | 49 | **EOffset_Flag_Type: 48=买入开仓 / 49=卖出平仓(方向判定关键) ** | ★ |
| m_nRealOffsetFlag | int | -1 | 实际操作类型(-1=未用) | |
| m_nHedgeFlag | int | 49 | 投保标记(投机=49) | |
| m_nOrderPriceType / m_eEntrustType | int | 50 / 48 | 价格类型/委托类型 | |
| m_eCoveredFlag / m_eFutureTradeType | int | 48 | 遮盖标记/期货交易类型 | |
| m_strOptName | string | 限价卖出 | **买卖标记中文(方向展示/交叉验证) ** | ★ |
| m_strOperation | string | (空) | 操作描述 | |
| m_dPrice | number | 6 | 成交均价 | ★ |
| m_nVolume | int | 200 | 成交量(股) | ★ |
| m_dTradeAmount | number | 1200 | 成交金额 | ★ |
| m_dCommission | number | 0.852 | **佣金费率(万分之,2026-09-01 老师定) ** : 0.852 = 万分之0.852 = 0.0000852(非金额);另有 m_dComssion 拼写变体同值 | ★ |
| m_dComssion | number | 0.852 | 手续费(拼写变体) | |
| m_dCloseProfit / m_nCloseTodayVolume | number/int | 0 | 平仓盈亏/平今量(期货用) | |
| m_dOrderPriceRMB / TradeAmountRMB / PriceRMB / ReferenceRate | number | 0 | 人民币换算/参考汇率 | |
| m_strTradeDate / m_strTradeTime | string | 20260901 / 101333 | 成交日期(YYYYMMDD) /时间(HHMMSS) | ★ |
| m_nStrategyID | int | 4322211943619263000 | 策略 ID( passorder 所属) | |
| m_nOrderStrategyType / m_strOrderStrategyType | int/string | 异常值/(空) | 委托策略类型(无实际意义) | |
| m_nTaskId / m_nGroupId / m_nRef | int | -1/2147483647/0 | 任务号/组号/引用(无实际意义) | |
| m_strSource / m_strXTTrade | string | (空)/其他终端 | 成交来源/交易终端 | |
| m_strCompactNo / LocalInfo / Remark | string | (空) | 合约编号/本地信息/备注 | |
| m_xtTag | null | null | 扩展标签 | |
**映射要点 ** :★ 字段共约 15 个/表,构成语义化模型(orderId/tradeId/code/name/exchange/direction/status/volumes/prices/amount/commission/time);m_nOffsetFlag+m_strOptName 双字段定方向;m_strOrderSysID 关联两表;**m_dCommission = 佣金费率(万分之),非金额(费用计算见「费用计算规则」)**。
**费用计算规则(2026-09-01 老师定,已实现 calcTradeFees) ** :
| 费用项 | 计算 | 说明 |
|---|---|---|
| 佣金 | 成交额 × (m_dCommission ÷ 10000), **不足 5 元按 5 元** | 账号没免五,费率=万分之 m_dCommission(如 0.852 = 万0.852) |
| 印花税 | 成交额 × 万分之5(0.05%) | **仅卖出**收取(2023-08-28 起标准,单边) |
| 过户费 | 成交额 × 万分之0.1(0.01‰) | **双向**收取,无最低限额(2022-04-29 起标准,中登收取,老师确认) |
| 总费用 | 佣金 + 印花税 + 过户费 | **委托级合并计费 ** :同一委托分笔成交按总金额算一次佣金;多笔独立委托各自算(2026-09-01 老师修正) |
### 2.2 Sample Data(真实数据样本,2026-09-01 实调)
> 实调 QMT 桥 /trade/orders 与 /trade/trades 获取的**当日真实数据**(账号 8882874667,交易日期 20260901)。
> 当日共 1 笔委托 + 1 笔成交(博纳影业 001330.SZ 限价卖出 200 股 @6.00,已成,手续费 0.852 元)。
> 以下为**完整原始返回**(含空/默认字段),与 2.1 字段表一一对应。
**订单 Sample( /trade/orders 原始返回) ** :
``` json
{
"m_bEnable" : true ,
"m_dCancelAmount" : 0 ,
"m_dFrozenCommission" : 0 ,
"m_dFrozenMargin" : 0 ,
"m_dLimitPrice" : 6 ,
"m_dOrderPriceRMB" : 0 ,
"m_dReferenceRate" : 0 ,
"m_dShortOccupedMargin" : 0 ,
"m_dTradeAmount" : 1200 ,
"m_dTradeAmountRMB" : 0 ,
"m_dTradedPrice" : 6 ,
"m_eCashgroupProp" : 48 ,
"m_eCoveredFlag" : 0 ,
"m_eEntrustType" : 48 ,
"m_nDirection" : 48 ,
"m_nErrorID" : 2147483647 ,
"m_nFrontID" : -1 ,
"m_nGroupId" : 2147483647 ,
"m_nHedgeFlag" : 49 ,
"m_nOffsetFlag" : 49 ,
"m_nOpType" : 2147483647 ,
"m_nOrderPriceType" : 50 ,
"m_nOrderStatus" : 56 ,
"m_nOrderStrategyType" : -1645423200 ,
"m_nOrderSubmitStatus" : 51 ,
"m_nRef" : 0 ,
"m_nSessionID" : -1 ,
"m_nStrategyID" : 4322211943619263000 ,
"m_nTaskId" : -1 ,
"m_nVolumeTotal" : 0 ,
"m_nVolumeTotalOriginal" : 200 ,
"m_nVolumeTraded" : 200 ,
"m_strAccountID" : "8882874667" ,
"m_strAccountKey" : "2____10125____105214____49____8882874667____" ,
"m_strAccountName" : "" ,
"m_strAccountRemark" : "" ,
"m_strBrokerName" : "" ,
"m_strCancelInfo" : "" ,
"m_strCompactNo" : "" ,
"m_strErrorMsg" : "" ,
"m_strExchangeID" : "SZ" ,
"m_strExchangeName" : "深交所" ,
"m_strInsertDate" : "20260901" ,
"m_strInsertTime" : "101333" ,
"m_strInstrumentID" : "001330" ,
"m_strInstrumentName" : "博纳影业" ,
"m_strLocalInfo" : "" ,
"m_strOptName" : "限价卖出" ,
"m_strOption" : "" ,
"m_strOrderParam" : "" ,
"m_strOrderRef" : "SZ_645009273" ,
"m_strOrderStrategyType" : "" ,
"m_strOrderSysID" : "645009273" ,
"m_strProductID" : "" ,
"m_strProductName" : "" ,
"m_strRemark" : "" ,
"m_strSource" : "" ,
"m_strUnderCode" : "" ,
"m_strXTTrade" : "其他终端" ,
"m_xtTag" : null
}
```
**成交 Sample( /trade/trades 原始返回) ** :
``` json
{
"m_dCloseProfit" : 0 ,
"m_dCommission" : 0.852 ,
"m_dComssion" : 0.852 ,
"m_dOrderPriceRMB" : 0 ,
"m_dPrice" : 6 ,
"m_dPriceRMB" : 0 ,
"m_dReferenceRate" : 0 ,
"m_dTradeAmount" : 1200 ,
"m_dTradeAmountRMB" : 0 ,
"m_eCoveredFlag" : 48 ,
"m_eEntrustType" : 48 ,
"m_eFutureTradeType" : 48 ,
"m_nCloseTodayVolume" : 0 ,
"m_nDirection" : 48 ,
"m_nGroupId" : 2147483647 ,
"m_nHedgeFlag" : 49 ,
"m_nOffsetFlag" : 49 ,
"m_nOrderPriceType" : 50 ,
"m_nOrderStrategyType" : -296943616 ,
"m_nRealOffsetFlag" : -1 ,
"m_nRef" : 0 ,
"m_nStrategyID" : 4322211943619263000 ,
"m_nTaskId" : -1 ,
"m_nVolume" : 200 ,
"m_strAccountID" : "8882874667" ,
"m_strAccountKey" : "2____10125____105214____49____8882874667____" ,
"m_strAccountRemark" : "" ,
"m_strCompactNo" : "" ,
"m_strExchangeID" : "SZ" ,
"m_strExchangeName" : "深交所" ,
"m_strInstrumentID" : "001330" ,
"m_strInstrumentName" : "博纳影业" ,
"m_strLocalInfo" : "" ,
"m_strOperation" : "" ,
"m_strOptName" : "限价卖出" ,
"m_strOrderRef" : "SZ_645009273" ,
"m_strOrderStrategyType" : "" ,
"m_strOrderSysID" : "645009273" ,
"m_strProductID" : "" ,
"m_strProductName" : "" ,
"m_strRemark" : "" ,
"m_strSource" : "" ,
"m_strTradeDate" : "20260901" ,
"m_strTradeID" : "0103000030812084" ,
"m_strTradeTime" : "101333" ,
"m_strXTTrade" : "其他终端" ,
"m_xtTag" : null
}
```
**语义化映射结果(供数据模型参考) ** :
| 语义字段 | 订单值 | 成交值 | 来源字段 |
|---|---|---|---|
| orderId | 645009273 | 645009273 | m_strOrderSysID |
| tradeId | - | 0103000030812084 | m_strTradeID |
| code | 001330 | 001330 | m_strInstrumentID |
| name | 博纳影业 | 博纳影业 | m_strInstrumentName |
| exchange | SZ | SZ | m_strExchangeID |
| direction | 卖 | 卖 | m_nOffsetFlag=49 + m_strOptName=限价卖出 |
| status | 56 已成 | 已成 | m_nOrderStatus |
| orderVolume | 200 | - | m_nVolumeTotalOriginal |
| tradedVolume | 200 | 200 | m_nVolumeTraded / m_nVolume |
| limitPrice | 6 | - | m_dLimitPrice |
| price | 6 | 6 | m_dTradedPrice / m_dPrice |
| amount | 1200 | 1200 | m_dTradeAmount |
| commission | - | 0.852 | m_dCommission |
| date | 20260901 | 20260901 | m_strInsertDate / m_strTradeDate |
| time | 101333 | 101333 | m_strInsertTime / m_strTradeTime |
| accountId | 8882874667 | 8882874667 | m_strAccountID |
### 2.3 数据时效研究(能查多少天?)
> 核实日期:2026-09-01 | 结论来源:QMT 桥 OpenAPI 规范 + 大 QMT 内置 API 官方文档(dict.thinktrader.net/innerApi/)
**结论:/trade/orders 与 /trade/trades 只能返回当日(当天交易日)的委托与成交,无法查历史。 **
**证据链 ** :
1. **QMT 桥 OpenAPI 规范 ** :两个端点的 summary 与 description 均明确「**当日**委托查询」「**当日**成交查询」,且参数只有 code/status/strategy_name( orders)或无参数(trades)——**没有日期范围参数**;
2. **底层函数 ** :QMT 桥数据由策略线程(adjust)调用 `get_trade_detail_data(account, 'stock', 'order'/'deal')` 缓存刷新(官方 code_examples 确认),官方示例全部是 3 参数调用,**无日期参数**;
3. **官方 Q&A(数据来源本质) ** : `get_trade_detail_data` 从**客户端本地缓存**读取(非调用时查柜台),缓存是「当前状态」——即当日委托/成交的实时快照;官方明确「不能认为查到的状态与柜台完全一致」;
4. **行情 vs 交易差异 ** :历史查询能力只存在于**行情**域(down_history_data / get_market_data_ex 可查历史 K线),**交易域(order/deal)没有历史查询接口**。
**对本需求的含义 ** :
| 影响面 | 结论 |
|---|---|
| 「交易记录」tab 展示范围 | 只能展示**当日**委托与成交(当日快照) |
| 交易复盘(目标-006) | 当日复盘可完整支撑;跨日复盘需**每日落盘存档**(Q5 关联:若要做历史复盘,必须本地持久化) |
| Q5 是否持久化 | **由「可选」升级为「必要前提」 ** :不做持久化则复盘能力只有当日,重启即丢 |
| 数据刷新 | 当日数据准实时(300ms 缓存 + 柜台推送 50ms~6s),轮询即可 |
**结论映射 ** :R-007 功能范围「不做」中「历史(非当日)交易查询」一项**确认成立**(QMT Bridge 仅提供当日缓存,无法查历史);但「不做持久化」需要重新审视——若老师要支撑跨日复盘,Q5 应改为「做持久化(store.trades.json,按日存档)」或明确接受「仅当日复盘」。
### 3. 现有架构可复用点(代码审查)
| 现有能力 | 位置 | 复用方式 |
|---|---|---|
| 统一数据源抽象 + REST 直连 | src/component/QmtBridgeRestDataSource.js | 新增 getOrders()/getTrades() 方法(参照 getAsset/getPositions, m_ 字段→语义字段映射) |
| 服务端 HTTP API 领域分发 | src/api/index.js + 领域文件 | 新增 api/trades.js(订单/成交域),注入 METHODS + HANDLERS |
| 前端 tab 框架 | src/client/index.js( GENERAL_TABS) | 交易记录 tab 的 render 从 PlaceholderTab 换成真实组件 |
| 服务端缓存 + 前端轮询模式 | MarketDataHub + MarketFeed( market-snapshot) | 交易数据可复用轮询模式(Q5 待定) |
| 设置显隐开关 | settings.js tabs.tradeRecords | 已预置,无需新增 |
| 涨跌着色/高亮组件 | PriceCell.jsx | 交易方向着色可复用(Q7 待定) |
## 功能范围(定稿,2026-09-01 老师拍板)
> 老师指令(2026-09-01):「先把当日订单与成交记录接进来,数据放到交易记录这个 tab 下;我会完善 QMT Bridge 历史成交的接口,所以请添加一个日期导航,根据交易日历来做一个日期导航。」
**做(本期) ** :
1. **数据源接入 ** : QmtBridgeRestDataSource 新增当日委托/当日成交查询(getOrders/getTrades,语义化映射,核心字段见 2.1/2.2);
2. **服务端 API ** :新增 orders / trades 端点(支持 code/status 过滤),纳入领域分发(新 api/trades.js);
3. **交易记录 tab 落地 ** :占位 tab( odl-trade-records)替换为真实交易视图——**单表合并**:以委托行为主行,按 m_strOrderSysID 合并成交信息(聚合展示 + 行展开查看每笔成交明细);
4. **数据刷新 ** :服务端中转 + 前端轮询(准实时,QMT Bridge 自身 300ms 缓存;前端 3-5s 轮询);
5. **状态展示 ** :委托状态中文映射(未报/已报/部成/已成/已撤/废单等)、买卖方向识别(m_nOffsetFlag + m_strOptName)、成交金额/手续费展示;
6. **时间段查询(核心,v3 定稿) ** :交易记录 tab 采用**起止时间段查询**(匹配历史接口按时间范围查询的形态)——标题 + 时间段控件**同一行**;控件含三个快捷按钮(**今日 / 本周 / 本月**)+ 手动起止日期(input type=date);快捷范围基于交易日历最近交易日计算且为**完整互斥时间段**(今日=最近交易日单日、本周=所在自然周**周一~周五**(不考虑周六日)、本月=所在自然月1号~月末);**交互单向模型**:preset 意图状态记录用户点的按钮,高亮=preset 匹配,不做「范围反推按钮」(2026-09-01 老师纠正:先有操作后有范围,不是反向推断);手动改日期清除 preset;「今日」范围 3-5s 轮询,其他范围一次性查询;非今日范围展示「历史数据接口开发中」占位(老师完善 QMT Bridge 历史接口后接入,服务端已预留 start/end 透传);
7. **委托↔成交合并 ** :按 m_strOrderSysID 关联——委托行为主行,聚合展示成交(成交量/成交价/成交额/手续费),展开后逐笔展示成交明细(2026-09-01 老师拍板)。
**不做(本期) ** :
- 下单/撤单等交易操作(QMT Bridge 当前为查询接口,无下单端点;目标-007 人机合一的关键决策控制权另议);
- 历史(非当日)交易查询实现(**接口由老师完善 QMT Bridge 后接入**,本期仅做日期导航占位);
- 交易数据本地持久化(Q5 本期不做,跨日复盘待历史接口就绪后评估);
- 按策略过滤交易(strategy_name 服务端过滤依赖 QMT passorder 策略名,与插件策略体系不同源,Q10 后续迭代)。
## 技术研究结论(第一轮)
1. **API 能力确认 ** : QMT Bridge v2.0.0 已提供 /trade/orders(支持 code/status/strategy_name 过滤)+ /trade/trades(全量当日成交),数据由策略线程 300ms 缓存刷新、HTTP 线程只读缓存,**REST 轮询即可达准实时**,无需 WS 订阅(规避迭代 04 的 WS 不稳定教训);**时效边界:仅当日数据,无法查历史(2.3 数据时效研究)**;
2. **字段语义澄清(核心,官方 innerApi 字典核实) ** :大 QMT 内置 API( dict.thinktrader.net/innerApi/)中,m_nDirection 对应 EEntrustBS 枚举(48=买卖/49=查询/50=撤单),官方注明「股票买卖永远是 48」——**它是操作标记不是方向**;真实买卖方向在 m_nOffsetFlag( EOffset_Flag_Type: 48=买入开仓/49=卖出平仓,实测 49=限价卖出 完全吻合)+ m_strOptName(买卖标记中文);**方向判定规则已确定:m_nOffsetFlag 为主 + m_strOptName 交叉验证(Q3 有结论)**;
3. **字段模型清晰 ** :已沉淀两张字段全表(2.1 小节),语义化映射核心字段约 15 个/表(orderId/tradeId/code/name/exchange/direction/status/volumes/prices/amount/commission/time),数据模型可直接据此设计;
4. **集成路径清晰 ** :现有数据源抽象 / API 领域分发 / tab 框架 / 服务端缓存模式全部可复用,新增代码为增量式(1 个 API 领域文件 + 数据源 2 方法 + 前端 1 个组件);
5. **架构约束沿用 ** :技术约束-001/003(统一抽象 + REST 直连)、技术约束-004(webServer 自开路由)、技术约束-010 思路(服务端中转 + 缓存,前端轮询);
6. **测试隔离 ** :订单/成交为只读查询端点,回归测试可直连生产 API(符合技术约束-011 只读豁免),但禁止测试写操作;
7. **日期导航可行性(2026-09-01 实测) ** : QMT Bridge /data/calendar/trading_dates 可用(GET,支持 start/end 过滤,返回 { market, count, dates[] }, dates 为 YYYYMMDD 数组)——取近 N 日交易日列表做前后切换导航即可;当日由 trading_dates 返回的最后一个交易日确定(或客户端本地日期取整)。
## 待确认问题(Q1-Q10,待老师拍板)
| # | 问题 | AI 建议 |
|---|---|---|
| # | 问题 | 结论(2026-09-01) |
|---|---|---|
| Q1 | **展示范围 ** :交易记录 tab 展示「当日委托 + 当日成交」两块,还是仅成交? | ✅ **两块都展示 ** :委托表(含状态)+ 成交表(含费用),tab 内分区块 |
| Q2 | **数据刷新机制 ** :服务端中转 + 前端轮询?周期? | ✅ 服务端中转 + 前端 3-5s 轮询(复用 market-snapshot 模式);QMT 自身 300ms 缓存 |
| Q3 | **买卖方向判定 ** : m_nDirection 恒 48 不判方向,方向取哪个字段? | ✅ **m_nOffsetFlag 为主(48买/49卖)+ m_strOptName 展示交叉验证 ** (官方枚举核实,Q3 闭环) |
| Q4 | **委托状态展示 ** :默认过滤已撤/废单?还是全量? | ✅ 全量展示 + 状态列 + 可选筛选(active/全部),默认全量(首版简化:全量展示即可) |
| Q5 | **是否持久化 ** :跨日复盘的前提是落盘? | ✅ **本期不做持久化 ** (当日实时查询);跨日复盘待历史接口就绪后评估(老师将完善 QMT Bridge 历史成交接口) |
| Q6 | **委托↔成交关联形态 ** :两表还是单表合并? | ✅ **单表合并 ** :委托行为主行 + 展开/折叠查看每笔成交(老师拍板,2026-09-01);聚合成交信息在委托行展示,展开后逐笔明细 |
| Q7 | **方向着色 ** :买/卖如何着色? | ✅ 遵循 A 股习惯:**红买绿卖**(与行情红涨绿跌相反语义;前端实现时用色值区分) |
| Q8 | **手续费/成交额精度 ** :展示位与单位? | ✅ 成交额元(2 位小数)、手续费元(3 位小数)、成交量股 |
| Q9 | **优先级 ** :老师定级? | ✅ **P1 ** (交易复盘是目标-006 核心输入,tab 已占位) |
| Q10 | **与策略联动 ** :按插件策略过滤交易? | ✅ 本期不做(strategy_name 是 QMT passorder 概念,与插件策略不同源;后续迭代) |
| Q11 | **日期导航 → 时间段查询 ** ( 2026-09-01 演进):基于交易日历的日期导航如何做? | ✅ **起止时间段查询 ** (老师反馈 v3):放弃单日前后导航,改为时间段(start~end);标题 + 时间段控件同一行;快捷按钮 今日/本周/本月 + 手动起止日期;匹配历史接口按时间范围查询的形态;服务端 orders/trades 预留 start/end 透传 |
## 展示设计:单表合并(委托为主,成交并入)
> 2026-09-01 老师拍板:**不要两个表,一个表**——当日委托信息为主行,根据 m_strOrderSysID 合并成交信息。
### 合并规则
- **主行 = 一笔委托**(按 m_strOrderSysID 唯一);
- **成交聚合**:该委托的全部成交(/trade/trades 中 m_strOrderSysID 匹配)聚合进主行;
- **行展开**:点击展开/折叠,展开后逐笔展示每笔成交明细(成交时间/价格/数量/金额/手续费)。
### 委托主行列(聚合态)
| 列 | 来源 | 显示逻辑 |
|---|---|---|
| 展开箭头 | - | ▸/▾ 切换(有成交才可展开;无成交置灰/隐藏) |
| 时间 | m_strInsertTime | HH:MM:SS(委托时间) |
| 代码 | m_strInstrumentID | 6 位代码 |
| 名称 | m_strInstrumentName | 中文名 |
| 方向 | m_nOffsetFlag(48买/49卖) + m_strOptName | **红买绿卖 ** (Q7);显示「买入/卖出」,悬停显示完整操作名(限价卖出) |
| 状态 | m_nOrderStatus | 中文映射:未报/待报/已报/已报待撤/部成待撤/部撤/已撤/部成/已成/废单 |
| 委托量 | m_nVolumeTotalOriginal | 整数 + 股 |
| 成交量 | Σ成交 m_nVolume(优先)或 m_nVolumeTraded | 聚合成交总量;无成交显示 — |
| 委托价 | m_dLimitPrice | 2 位小数;市价单显示「市价」 |
| **成交均价 ** | Σ(成交价×量)/Σ量 加权 | 聚合加权均价;无成交显示 — |
| **成交额 ** | Σ成交 m_dTradeAmount | 聚合求和;无成交显示 — |
| **手续费 ** | Σ成交 m_dCommission | 聚合求和(3 位小数);无成交显示 — |
### 展开明细行(每笔成交)
| 列 | 来源 | 显示逻辑 |
|---|---|---|
| 成交时间 | m_strTradeTime | HH:MM:SS |
| 成交价 | m_dPrice | 2 位小数 |
| 成交量 | m_nVolume | 整数 + 股 |
| 成交额 | m_dTradeAmount | 2 位小数 |
| 手续费 | m_dCommission | 3 位小数 |
| 成交号 | m_strTradeID | 次要,悬停/弱显示 |
### 显示逻辑要点
1. **未成交委托 ** (待报/已报/已撤/废单):成交量/成交均价/成交额/手续费列显示 —,状态列已明确表达,无需其他冗余;
2. **多笔成交 ** :主行聚合(加权均价 + 求和),展开逐笔;聚合金额与逐笔合计一致;
3. **排序 ** :委托主行**默认按时间降序**(最新在前,老师拍板 2026-09-01);**支持手动调整**——点击表头列排序(时间/代码/方向/状态/委托量/成交量/金额等可排序列),再次点击切换升/降序,默认回到降序;
4. **方向着色 ** :红买绿卖(表头或图例标注「红=买 绿=卖」,避免与行情红涨绿跌混淆);
5. **空态 ** :无委托时显示「今日暂无委托记录」;
6. **数量格式 ** :整数(股),不强制千分位(A股习惯)。
## 技术实现方案(草案,2026-09-01)
### 程序结构(改造后)
```
src/
├── component/
│ └── QmtBridgeRestDataSource.js # 改:新增 getOrders()/getTrades()(语义化映射)
├── api/
│ └── trades.js # 新增:订单/成交域(orders / trades / trading-dates 端点)
│ └── index.js # 改:合并 TRADE_METHODS
├── client/
│ └── views/
│ ├── TradeRecordsTab.jsx # 新增:交易记录 tab(日期导航 + 单表合并视图)
│ ├── OrderRow.jsx # 新增:委托主行(聚合成交,可展开)
│ ├── TradeDetailRows.jsx # 新增:展开的成交明细行(每笔成交)
│ └── DateNav.jsx # 新增:日期导航组件(交易日历驱动)
│ └── index.js # 改:tradeRecords tab 渲染 TradeRecordsTab
```
### 服务端
1. **数据源 ** : QmtBridgeRestDataSource 新增
- `getOrders({ code, status })` → 调 `/trade/orders` ,语义化映射(orderId/status/direction/volumes/prices/amount/time/...,见 2.2 映射表);
- `getTrades()` → 调 `/trade/trades` ,语义化映射(tradeId/orderId/code/price/volume/amount/commission/time);
- `getTradingDates({ start, end })` → 调 `/data/calendar/trading_dates` (支持 start/end,返回 dates[])。
2. **API 域 ** :新建 api/trades.js,端点:
- `orders` ( args: code/status)→ dataSource.getOrders;
- `trades` → dataSource.getTrades;
- `trading-dates` ( args: start/end)→ dataSource.getTradingDates;
- 纳入 registerApi 的 METHODS + HANDLERS。
3. **日期解析 ** :服务端提供「当日交易日」解析(trading_dates 最后一个日期,或本地日期取整),前端导航初始化用。
### 客户端
1. **TradeRecordsTab ** :顶部日期导航(DateNav)+ 单表合并视图(委托主行 + 可展开成交明细);
2. **DateNav ** :加载交易日列表(近 N 日,如 -30~+7),前后箭头切换,当日高亮;选中日期状态提升;非当日显示「历史数据接口开发中」提示(老师完善 QMT Bridge 历史接口后启用);
3. **数据加载 ** : useRpc 调 orders / trades;当日 3-5s 轮询;切换日期时取消轮询、加载所选日期数据(历史接口就绪后);
4. **合并逻辑(前端) ** : fetch 两接口 → 按 m_strOrderSysID 分组成交 → 委托主行聚合(加权均价/求和金额/求和手续费)→ 展开时逐笔渲染成交明细;
5. **委托主行列 ** :展开箭头/时间/代码/名称/方向/状态/委托量/成交量/委托价/成交均价/成交额/手续费;
6. **展开明细列 ** :成交时间/成交价/成交量/成交额/手续费/成交号;
7. **着色 ** :红买绿卖(Q7 已定);方向列红色=买、绿色=卖,表头图例标注。
### 构建与验证
- pnpm run build + 页面刷新(客户端 bundle)或宿主重启(服务端模块);
- 回归:现有持仓/策略/行情端点不受影响(技术约束-011 只读豁免,可直连生产 API 验证 orders/trades/trading-dates 返回)。
## 讨论记录
> 追加式记录:日期 · 轮次 · 结论。
- **2026-09-01 · 第一轮(需求登记 + 技术研究)**:
- 老师指令:添加一个新的需求,接入订单与成交数据,开始需求和技术研究。
- AI 完成:需求登记 R-007(本文件);QMT Bridge API 面探索(/trade/orders + /trade/trades 端点确认);线上实测真实数据样本(博纳影业 001330.SZ 委托+成交);现有架构代码审查(数据源/API 领域/tab 框架/缓存模式可复用);形成 Q1-Q10 待确认问题。
- 状态建议:**起草 → 讨论中**(已开始需求与技术研究),待老师确认后推进。
- **2026-09-01 · 第二轮(官方字典核实,方向字段澄清)**:
- 老师指正:QMT 桥转接的是**大 QMT 服务**,字段值应参考内置 API( innerApi)字典:https://dict.thinktrader.net/innerApi/start_now.html?id=gdJsE0 (而非 nativeApi/xtquant)。
- AI 核实(dict.thinktrader.net/innerApi/enum_constants.html + data_structure.html):
① m_nDirection 对应 enum_EEntrustBS( 48=买卖/49=查询/50=撤单),官方注明「股票买卖永远是 48」——**操作标记,非方向**;
② 真实方向在 m_nOffsetFlag( enum_EOffset_Flag_Type: 48=买入开仓/49=卖出平仓/50=强平/51=平今/52=平昨/53=强减/54=本地强平);
③ m_strOptName = 买卖标记,展示委托属性的中文(实测「限价卖出」)。
- 线上实测闭环验证:博纳影业订单 m_nDirection=48、**m_nOffsetFlag=49**、m_strOptName=限价卖出 → 49=卖出平仓 与「限价卖出」完全吻合。
- 结论:**方向判定规则确定 = m_nOffsetFlag 为主(48买/49卖)+ m_strOptName 交叉验证**;Q3 由「待确认」变为「有结论,待老师确认后固化」。
- 文档同步修正:字段表 m_nDirection 释义、实测样本说明、技术研究结论第 2 条、Q3 描述。
- **2026-09-01 · 第三轮(字段全表沉淀)**:
- 老师指示「看看 QMT 桥返回了哪些字段,列一个表」「要的要的」(确认沉淀)。
- AI 实调 /trade/orders 与 /trade/trades 提取完整原始字段,整理为两张字段全表(见 2.1 小节),标注 ★ 语义化映射核心字段(约 15 个/表)。
- 关键点:① m_nOffsetFlag+m_strOptName 双字段定方向(Q3 闭环);② m_strOrderSysID 关联委托↔成交;③ 手续费字段有拼写变体 m_dComssion(取值优先 m_dCommission);④ 大量期权/期货/人民币换算字段在股票场景为空/默认值,映射时裁剪。
- **2026-09-01 · 第四轮(Sample Data 沉淀)**:
- 老师指示「今天有成交数据,帮我在文档中完善 sample data」。
- AI 实调最新 /trade/orders + /trade/trades(当日 1 笔委托 + 1 笔成交:博纳影业 001330.SZ 限价卖出 200 股 @6 .00,已成,手续费 0.852 元),完整原始 JSON 沉淀为 2.2 小节 Sample Data。
- 同时给出语义化映射结果表(orderId/tradeId/code/name/exchange/direction/status/volumes/prices/amount/commission/time/accountId 共 16 字段),作为数据模型直接参考。
- 注:实调确认今日数据仅此一笔(订单 645009273 = 成交 0103000030812084 同号关联),sample 覆盖委托↔成交完整链路。
- **2026-09-01 · 第五轮(数据时效研究)**:
- 老师提问:这两个接口可以返回多少天以内的成交记录?
- AI 核实(QMT 桥 OpenAPI + 大 QMT 官方文档 code_examples/question_answer):/trade/orders 与 /trade/trades **只能返回当日数据 ** ——规范明确「当日」且无日期参数;底层 get_trade_detail_data 从客户端本地缓存读取(当前状态快照),交易域无历史查询接口(历史能力仅限行情域)。
- 沉淀 2.3 数据时效研究小节;Q5 持久化由「可选」升级为「跨日复盘的必要前提」,待老师拍板。
- **2026-09-01 · 第六轮(范围定稿 + 日期导航)**:
- 老师拍板:① 先把**当日订单与成交记录接进来**,数据放到**交易记录 tab** 下;② 老师将完善 QMT Bridge **历史成交接口 ** ;③ 请添加一个**日期导航**,**根据交易日历**做一个日期导航。
- AI 更新:功能范围定稿(当日委托+成交 + 日期导航占位);Q1-Q10 全部有结论(Q5 本期不做持久化);新增 Q11 日期导航结论;技术实现方案草案(数据源 3 方法 / api/trades.js / TradeRecordsTab + DateNav);实测 trading_dates 接口可用。
- 状态:R-007 从「讨论中」推进,功能范围已定稿,待老师确认定稿后进入计划/迭代。
- **2026-09-01 · 第七轮(单表合并展示设计)**:
- 老师指示讨论委托/成交的显示字段与显示逻辑;AI 提出两表方案;老师拍板:**不要两个表,要一个表**——当日委托信息为主行,根据 m_strOrderSysID 合并成交信息。
- 三个决策点老师逐条确认:① **委托行展开/折叠 ** ——主行显示订单状态 + 聚合成交信息,展开后逐笔成交明细;② 委托未成交(已撤/废单/待报)——状态列已明确,成交列显示 —;③ 聚合展示在委托行,展开后分别展示。
- AI 沉淀「展示设计:单表合并」章节:委托主行列(时间/代码/名称/方向/状态/委托量/成交量/委托价/成交均价/成交额/手续费)+ 展开明细列(成交时间/价格/量/额/手续费/成交号)+ 合并规则(按 m_strOrderSysID 分组、加权均价、求和、未成交显示 —)。
- 技术方案同步:组件改为 TradeRecordsTab + OrderRow(委托主行)+ TradeDetailRows(展开明细)+ DateNav;前端合并逻辑明确(fetch 两接口 → 分组 → 聚合)。
- **2026-09-01 · 第九轮(日期导航改版 v2)**:
- 老师反馈:顶部日期按钮没有美感——不要做成一个个按钮、不要加边框;以当前**最后一个交易日**为基准,先是**前面 7 个日期**;做成**一行**,把前一日/后一日的导航箭头也放进同一行;每个日期用**小竖线**分隔。
- AI 重写 DateNav v2:一行式布局(◀ 日期|日期|...|日期 ▶),日期为纯文本无边框,竖线 | 分隔,选中绿色加粗;基准 = 交易日历最后一个交易日(最近交易日),显示最近 + 前 7 个共 8 个;箭头 ◀ ▶ 同在一行(上一个/下一个交易日);TradeRecordsTab 的「今日」基准同步改为最近交易日(onLastDate 通知)。
- **2026-09-01 · 第十轮(时间段查询重构 v3)**:
- 老师反馈:看了历史交易接口,是按**传入时间范围**确定查询哪些日期的记录——现有单日导航逻辑不适合;倾向**按日期查询但能指定起止时间**;同时布局浪费空间(标题一行、日期导航一行)——① 日期导航改为**起止时间段**,不再用前一日/后一日;② 时间段控件与**标题同一行**;③ 新增三个功能键:**今日 / 本周 / 上周**快速查询近期数据,其余用户手动指定日期。
- AI 重构:删除 DateNav,新增 RangeSelector(今日/本周/上周快捷 + 起止日期 input);TradeRecordsTab 标题 + 时间段同一行;今日范围 4s 轮询、其他范围一次性查询;服务端 orders/trades 预留 start/end 透传(匹配历史接口形态);历史范围占位「历史数据接口开发中」。
- 决策:今日=最近交易日单日;本周=所在自然周周一~周日(完整周);本月=所在自然月1号~月末(完整月);均基于交易日历最近交易日计算。
- 2026-09-01 修正一:快捷按钮由「今日/本周/上周」改为「**今日/本周/本月**」(老师反馈,去掉上周、新增本月)。
- 2026-09-01 修正二(老师纠正):快捷范围为**完整时间段**(本月=9/1~9/30、本周=周一~周日),不做「截至今天」截断——否则月初第一天点「本月」会与「今日」重合、无法操作;这是操作逻辑问题,不是数据问题。
- 2026-09-01 · 第十一轮(费用计算规则,老师定):m_dCommission 的值是**佣金费率(万分之)**——如 0.852 = 万分之0.852 = 0.0000852(不是金额 0.852 元)。费用规则:佣金 = 成交额 × 万0.852,不足 5 元按 5 元(账号没免五);卖出加印花税(万分之5,2023-08-28 起)和过户费(万分之0.1,双向,无最低,2022-04-29 起,老师确认费率);过户费收费方中登、ETF/LOF/可转债/REITs 免收。已实现 calcTradeFees 逐笔计算 + 前端聚合展示(总费用/佣金/印花税/过户费四列)。
- 2026-09-01 修正五(佣金合并计费,老师纠正):**同一笔委托分多笔成交,按总成交金额合并算一次佣金**(不逐笔重复收费);多笔独立委托(分开下单)才各自算佣金、各自触发 5 元最低。实现:calcOrderFees 委托层合并计费(Σ成交额×费率→最低5元一次),mapTrade 保留原始成交字段不再逐笔算费;前端聚合调用 calcOrderFees,展开明细只展示成交本身(费用在委托主行)。验证:200股拆两笔100+100,佣金5元(非10元);两笔独立委托各5元(10元)。
- 2026-09-01 修正四(本周范围,老师纠正):本周 = **周一~周五 ** (不考虑周六日,不是往前推 7 天/自然周周日);实现 weekSunday→weekFriday(周一+4)。
- 2026-09-01 修正三(交互模型,老师纠正):**交互是单向的**——用户先有操作(点「本月」),才有时间范围;不能反过来从「搜索范围落在哪」反向推断用户交互的是哪个按钮。实现改为 **preset 意图状态 ** :点按钮 → 记录 preset → 按钮高亮(单向);手动改日期 → 清除 preset(不再是任何快捷操作);轮询由 preset==='today' 决定,而非范围反推。
- **2026-09-01 · 第八轮(排序逻辑定稿)**:
- 老师拍板:委托主行排序**默认降序**(最新委托在前),**支持手动调整**(点击表头排序,可切换升/降序)。
- AI 更新:显示逻辑要点第 3 条改为「默认降序 + 表头点击排序」;可排序列包含时间/代码/方向/状态/委托量/成交量/金额等。
## 关联文档(随讨论补充)
- 计划:待 R-007 定稿后派生(符合入范围门槛)
- 迭代记录:待实现后建立
- 设计约束:待定稿后沉淀(方向判定规则、交易数据模型等)