- R-004 QMT连接配置:多配置管理 + 会话头部快捷切换(03-QMT连接配置) - R-005 WS盘中价格实时更新:服务端中转+缓存+前端轮询(04-WS盘中价格实时更新) - R-006 策略数据 JSON data store 标准化(store.json/market.json/schema.json) - R-007 交易记录接入:当日订单+成交单表合并(委托主行+展开成交明细)、时间段查询(今日/本周/本月+手动起止)、费用计算(佣金费率万m_dCommission+最低5元、印花税卖出万5、过户费双向万0.1) - 架构治理:api 按领域拆分、component 目录、QmtHealthMonitor
43 KiB
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 起标准,中登收取,老师确认) |
| 总费用 | 佣金 + 印花税 + 过户费 | 逐笔计算后聚合展示 |
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 原始返回):
{
"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 原始返回):
{
"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 只能返回当日(当天交易日)的委托与成交,无法查历史。
证据链:
- QMT 桥 OpenAPI 规范:两个端点的 summary 与 description 均明确「当日委托查询」「当日成交查询」,且参数只有 code/status/strategy_name(orders)或无参数(trades)——没有日期范围参数;
- 底层函数:QMT 桥数据由策略线程(adjust)调用
get_trade_detail_data(account, 'stock', 'order'/'deal')缓存刷新(官方 code_examples 确认),官方示例全部是 3 参数调用,无日期参数; - 官方 Q&A(数据来源本质):
get_trade_detail_data从客户端本地缓存读取(非调用时查柜台),缓存是「当前状态」——即当日委托/成交的实时快照;官方明确「不能认为查到的状态与柜台完全一致」; - 行情 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 历史成交的接口,所以请添加一个日期导航,根据交易日历来做一个日期导航。」
做(本期):
- 数据源接入:QmtBridgeRestDataSource 新增当日委托/当日成交查询(getOrders/getTrades,语义化映射,核心字段见 2.1/2.2);
- 服务端 API:新增 orders / trades 端点(支持 code/status 过滤),纳入领域分发(新 api/trades.js);
- 交易记录 tab 落地:占位 tab(odl-trade-records)替换为真实交易视图——单表合并:以委托行为主行,按 m_strOrderSysID 合并成交信息(聚合展示 + 行展开查看每笔成交明细);
- 数据刷新:服务端中转 + 前端轮询(准实时,QMT Bridge 自身 300ms 缓存;前端 3-5s 轮询);
- 状态展示:委托状态中文映射(未报/已报/部成/已成/已撤/废单等)、买卖方向识别(m_nOffsetFlag + m_strOptName)、成交金额/手续费展示;
- 时间段查询(核心,v3 定稿):交易记录 tab 采用起止时间段查询(匹配历史接口按时间范围查询的形态)——标题 + 时间段控件同一行;控件含三个快捷按钮(今日 / 本周 / 本月)+ 手动起止日期(input type=date);快捷范围基于交易日历最近交易日计算且为完整互斥时间段(今日=最近交易日单日、本周=所在自然周周一~周五(不考虑周六日)、本月=所在自然月1号~月末);交互单向模型:preset 意图状态记录用户点的按钮,高亮=preset 匹配,不做「范围反推按钮」(2026-09-01 老师纠正:先有操作后有范围,不是反向推断);手动改日期清除 preset;「今日」范围 3-5s 轮询,其他范围一次性查询;非今日范围展示「历史数据接口开发中」占位(老师完善 QMT Bridge 历史接口后接入,服务端已预留 start/end 透传);
- 委托↔成交合并:按 m_strOrderSysID 关联——委托行为主行,聚合展示成交(成交量/成交价/成交额/手续费),展开后逐笔展示成交明细(2026-09-01 老师拍板)。
不做(本期):
- 下单/撤单等交易操作(QMT Bridge 当前为查询接口,无下单端点;目标-007 人机合一的关键决策控制权另议);
- 历史(非当日)交易查询实现(接口由老师完善 QMT Bridge 后接入,本期仅做日期导航占位);
- 交易数据本地持久化(Q5 本期不做,跨日复盘待历史接口就绪后评估);
- 按策略过滤交易(strategy_name 服务端过滤依赖 QMT passorder 策略名,与插件策略体系不同源,Q10 后续迭代)。
技术研究结论(第一轮)
- API 能力确认:QMT Bridge v2.0.0 已提供 /trade/orders(支持 code/status/strategy_name 过滤)+ /trade/trades(全量当日成交),数据由策略线程 300ms 缓存刷新、HTTP 线程只读缓存,REST 轮询即可达准实时,无需 WS 订阅(规避迭代 04 的 WS 不稳定教训);时效边界:仅当日数据,无法查历史(2.3 数据时效研究);
- 字段语义澄清(核心,官方 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 有结论);
- 字段模型清晰:已沉淀两张字段全表(2.1 小节),语义化映射核心字段约 15 个/表(orderId/tradeId/code/name/exchange/direction/status/volumes/prices/amount/commission/time),数据模型可直接据此设计;
- 集成路径清晰:现有数据源抽象 / API 领域分发 / tab 框架 / 服务端缓存模式全部可复用,新增代码为增量式(1 个 API 领域文件 + 数据源 2 方法 + 前端 1 个组件);
- 架构约束沿用:技术约束-001/003(统一抽象 + REST 直连)、技术约束-004(webServer 自开路由)、技术约束-010 思路(服务端中转 + 缓存,前端轮询);
- 测试隔离:订单/成交为只读查询端点,回归测试可直连生产 API(符合技术约束-011 只读豁免),但禁止测试写操作;
- 日期导航可行性(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 | 次要,悬停/弱显示 |
显示逻辑要点
- 未成交委托(待报/已报/已撤/废单):成交量/成交均价/成交额/手续费列显示 —,状态列已明确表达,无需其他冗余;
- 多笔成交:主行聚合(加权均价 + 求和),展开逐笔;聚合金额与逐笔合计一致;
- 排序:委托主行默认按时间降序(最新在前,老师拍板 2026-09-01);支持手动调整——点击表头列排序(时间/代码/方向/状态/委托量/成交量/金额等可排序列),再次点击切换升/降序,默认回到降序;
- 方向着色:红买绿卖(表头或图例标注「红=买 绿=卖」,避免与行情红涨绿跌混淆);
- 空态:无委托时显示「今日暂无委托记录」;
- 数量格式:整数(股),不强制千分位(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
服务端
- 数据源: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[])。
- API 域:新建 api/trades.js,端点:
orders(args: code/status)→ dataSource.getOrders;trades→ dataSource.getTrades;trading-dates(args: start/end)→ dataSource.getTradingDates;- 纳入 registerApi 的 METHODS + HANDLERS。
- 日期解析:服务端提供「当日交易日」解析(trading_dates 最后一个日期,或本地日期取整),前端导航初始化用。
客户端
- TradeRecordsTab:顶部日期导航(DateNav)+ 单表合并视图(委托主行 + 可展开成交明细);
- DateNav:加载交易日列表(近 N 日,如 -30~+7),前后箭头切换,当日高亮;选中日期状态提升;非当日显示「历史数据接口开发中」提示(老师完善 QMT Bridge 历史接口后启用);
- 数据加载:useRpc 调 orders / trades;当日 3-5s 轮询;切换日期时取消轮询、加载所选日期数据(历史接口就绪后);
- 合并逻辑(前端):fetch 两接口 → 按 m_strOrderSysID 分组成交 → 委托主行聚合(加权均价/求和金额/求和手续费)→ 展开时逐笔渲染成交明细;
- 委托主行列:展开箭头/时间/代码/名称/方向/状态/委托量/成交量/委托价/成交均价/成交额/手续费;
- 展开明细列:成交时间/成交价/成交量/成交额/手续费/成交号;
- 着色:红买绿卖(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 修正四(本周范围,老师纠正):本周 = 周一~周五(不考虑周六日,不是往前推 7 天/自然周周日);实现 weekSunday→weekFriday(周一+4)。
- 2026-09-01 修正三(交互模型,老师纠正):交互是单向的——用户先有操作(点「本月」),才有时间范围;不能反过来从「搜索范围落在哪」反向推断用户交互的是哪个按钮。实现改为 preset 意图状态:点按钮 → 记录 preset → 按钮高亮(单向);手动改日期 → 清除 preset(不再是任何快捷操作);轮询由 preset==='today' 决定,而非范围反推。
- 2026-09-01 · 第八轮(排序逻辑定稿):
- 老师拍板:委托主行排序默认降序(最新委托在前),支持手动调整(点击表头排序,可切换升/降序)。
- AI 更新:显示逻辑要点第 3 条改为「默认降序 + 表头点击排序」;可排序列包含时间/代码/方向/状态/委托量/成交量/金额等。
关联文档(随讨论补充)
- 计划:待 R-007 定稿后派生(符合入范围门槛)
- 迭代记录:待实现后建立
- 设计约束:待定稿后沉淀(方向判定规则、交易数据模型等)