29 KiB
QMT 模块功能文档
1. 系统概述
QMT 模块是 NewStock 量化交易系统的实盘交易执行模块,通过 xtquant 库连接 QMT 交易终端,实现自动化交易功能。该模块采用多终端架构设计,支持同时管理多个 QMT 终端实例,并提供 Web 仪表盘和 RESTful API 接口进行监控和操作。
系统核心特性包括:多终端并行管理、异步订单处理、断线自动重连、收盘自动清算、实时心跳检测等。所有交易信号通过 Redis 消息队列接收,确保交易指令的可靠传递和执行。
系统分为信号发送端和交易执行端两部分。信号发送端(qmt_signal_sender.py)运行在聚宽策略环境中,将策略产生的买卖信号推送至 Redis 队列;交易执行端(qmt_engine.py + run.py)运行在本地,从 Redis 消费信号并通过 QMT 终端执行实盘交易。
2. 核心组件
2.1 文件结构
| 文件 | 说明 |
|---|---|
run.py |
系统启动入口,负责初始化多终端管理器并启动 API 服务 |
qmt_engine.py |
核心引擎模块,包含多终端管理器和交易执行单元 |
qmt_trader.py |
旧版单终端交易引擎(保留兼容) |
qmt_signal_sender.py |
信号发送端,运行于聚宽策略侧,将交易信号推送至 Redis 队列 |
api_server.py |
FastAPI Web 服务,提供 RESTful API 接口 |
dashboard.html |
Web 仪表盘前端页面 |
start.bat |
Windows 启动脚本 |
logs/ |
系统运行日志目录 |
2.2 核心类说明
qmt_engine.py
| 类名 | 功能说明 |
|---|---|
MultiEngineManager |
多终端管理器单例,负责管理所有 QMT 终端实例和交易路由 |
TradingUnit |
终端执行单元,代表单个 QMT 进程的管理器 |
PositionManager |
Redis 虚拟持仓管理器,维护策略级别的持仓状态 |
DailySettlement |
日终对账处理器,执行收盘后的持仓核对 |
AutoReconnectScheduler |
定时自动重连调度器,支持配置重连时间和启用状态 |
UnitCallback |
终端回调处理器,处理成交回报和错误通知 |
TerminalStatus |
终端状态数据类,封装终端连接状态信息 |
qmt_trader.py
| 类名 | 功能说明 |
|---|---|
SystemState |
全局状态管理器,维护交易系统运行状态 |
PositionManager |
虚拟持仓管理器,管理策略与股票的持仓映射 |
DailySettlement |
日终清算处理器,处理收盘后的撤单和持仓修正 |
MyXtQuantTraderCallback |
QMT 交易回调,处理成交和错误事件 |
3. 功能详细说明
3.1 多终端管理
系统支持同时连接和管理多个 QMT 终端实例,每个终端可以配置独立的账户信息。每个 TradingUnit 对应一个 QMT 终端,具备独立的连接状态、回调处理和交易能力。终端之间相互隔离,可以通过配置文件定义每个策略使用哪个终端执行交易。
多终端管理器的核心职责包括:终端初始化、连接状态监控、健康检查、断线重连、交易消息路由等。主循环每 25 秒执行一次健康检查,通过查询资产验证物理连接有效性。当检测到连接丢失时,系统自动执行重连操作,并避开 QMT 夜间重启高峰期。
3.2 交易消息处理
交易信号通过 Redis 消息队列传递,每个策略对应一个独立的队列。消息格式为 JSON 对象,包含股票代码、操作类型、价格、时间戳等字段。系统对每条消息进行严格校验,包括日期校验、时间戳校验、必填字段校验等,确保只有当天的有效指令才会被执行。
买入逻辑支持两种模式:
-
槽位控制模式:通过
total_slots参数限制同时持有的股票数量。系统根据可用资金和目标槽位自动计算每只股票的买入数量,金额过小或股数不足的请求会被拦截。 -
仓位百分比模式:通过
position_pct参数指定目标持仓占账户总资产的比例。系统根据账户总资产计算目标金额,然后转换为具体股数进行下单。该模式无持仓数量限制。
卖出逻辑根据策略配置模式有所不同:槽位模式下根据 Redis 中的虚拟持仓和实盘可用持仓计算实际卖出数量;百分比模式下执行清仓操作。
3.3 持仓管理
持仓管理采用虚拟账本与实盘持仓双轨制。虚拟账本记录策略意图持有的股票,通过 Redis Hash 结构存储,使用 POS:{strategy_name} 作为键。当策略发出买入指令时,系统先在虚拟账本中标记持仓,再执行实际委托。
成交回调会实时更新虚拟持仓数量,买入增加持仓,卖出减少持仓。如果下单失败,系统会自动回滚虚拟持仓的槽位占用。日终结算时会核对虚拟持仓与实盘持仓的差异,发现幽灵持仓或占位符不一致时自动修正。
3.4 断线重连机制
系统实现了三级断线检测和自动重连机制。第一级是回调通知,当 QMT 底层检测到连接断开时触发 on_disconnected 回调。第二级是逻辑状态跟踪,系统维护 callback.is_connected 标志反映回调状态。第三级是物理探测,通过查询账户资产验证连接是否真正有效。
重连策略包含智能避让机制,在每日 21:32 至 21:50 的 QMT 维护时段内不执行重连操作,避免与系统重启冲突。自动重连调度器支持配置定时执行重连任务,可在盘后 22:00 自动断开并重新建立所有连接。
3.5 日终清算
日终清算是系统的重要保障机制,在每个交易日收盘后自动执行。清算流程包括三个步骤:首先撤销所有可撤单订单,释放未成交的委托;然后查询实盘真实持仓,建立持仓映射;最后遍历所有策略的虚拟持仓,与实盘进行比对和修正。
清算过程中发现的异常情况包括:幽灵持仓(Redis 有记录但实盘无持仓)和占位符不一致(Redis 记录为 0 但实盘有持仓)。这些异常会被自动修正,确保系统状态与实际账户一致。清算完成后设置标志位,防止重复执行。
3.6 日志系统
系统采用增强型日志系统,支持文件和控制台双路输出。日志格式包含时间戳、线程名、级别和消息内容,便于追踪问题。文件日志按日期命名,自动存放在 logs/ 目录下。控制台输出强制刷新流,确保在 Windows 环境下日志实时显示。
日志级别分为 INFO、WARNING、ERROR 三级,重要操作和状态变化都会记录。交易相关日志特别标注策略名称和股票代码,方便后续分析和审计。
4. API 接口列表
4.1 端点列表
| 方法 | 路径 | 功能说明 |
|---|---|---|
| GET | / |
返回 Web 仪表盘页面 |
| GET | /api/status |
获取所有终端的连接状态 |
| GET | /api/positions |
获取实盘持仓和虚拟持仓 |
| GET | /api/logs |
获取系统运行日志 |
| GET | /api/health |
健康检查接口 |
| GET | /api/config |
获取自动重连配置 |
| POST | /api/config |
更新自动重连配置 |
| POST | /api/reconnect |
手动触发所有终端重连 |
| GET | /api/file_config |
获取配置文件内容(敏感信息脱敏) |
4.2 响应模型
StatusResponse
{
"running": true,
"start_time": "2026-01-27 10:00:00",
"terminals": [
{
"qmt_id": "terminal_001",
"alias": "主账户",
"account_id": "****1234",
"is_connected": true,
"callback_connected": true,
"physical_connected": true,
"last_heartbeat": "10:30:15"
}
]
}
PositionsResponse
{
"real_positions": {
"terminal_001": [
{"code": "600519", "volume": 1000, "can_use": 500, "market_value": 850000.00}
]
},
"virtual_positions": {
"strategy_a": {"600519": "500", "000001": "1000"}
}
}
ConfigResponse
{
"reconnect_time": "22:00",
"auto_reconnect_enabled": true
}
5. 配置说明
5.1 配置文件结构
系统使用 config.json 作为主配置文件,文件结构如下:
{
"redis": {
"host": "localhost",
"port": 6379,
"db": 0,
"password": null
},
"qmt_terminals": [
{
"qmt_id": "terminal_001",
"alias": "主账户",
"path": "C:/QMT/xiadan/",
"account_id": "12345678",
"account_type": "STOCK"
}
],
"strategies": {
"strategy_a": {
"qmt_id": "terminal_001",
"total_slots": 5,
"execution": {
"buy_amount_per_stock": 20000,
"min_buy_amount": 2000
}
}
},
"auto_reconnect": {
"enabled": true,
"reconnect_time": "22:00"
}
}
5.2 配置项说明
| 配置项 | 类型 | 说明 |
|---|---|---|
redis |
Object | Redis 连接配置,支持 host、port、db、password 等参数 |
qmt_terminals |
Array | QMT 终端列表,每个终端包含唯一标识、别名、路径、账户信息 |
strategies |
Object | 策略配置,键为策略名,值包含使用的终端 ID 和执行参数 |
total_slots |
Integer | 策略的最大持仓股票数量(槽位模式) |
order_mode |
String | 下单模式,可选 slots(槽位)或 percentage(百分比),默认为 slots |
auto_reconnect |
Object | 自动重连配置,包含启用状态和执行时间 |
槽位模式配置示例:
{
"strategies": {
"strategy_a": {
"qmt_id": "terminal_001",
"order_mode": "slots",
"total_slots": 5,
"execution": {
"buy_amount_per_stock": 20000,
"min_buy_amount": 2000
}
}
}
}
百分比模式配置示例:
{
"strategies": {
"strategy_b": {
"qmt_id": "terminal_001",
"order_mode": "percentage"
}
}
}
6. Web 仪表盘功能
6.1 功能概览
Web 仪表盘基于 Vue 3 和 Naive UI 组件库开发,提供可视化的系统监控界面。仪表盘支持暗黑主题,采用卡片式布局展示各类信息。页面每 30 秒自动刷新,也支持手动点击刷新按钮获取最新数据。
6.2 功能模块
| 模块 | 功能说明 |
|---|---|
| 顶部状态栏 | 显示系统运行状态总览,提供配置查看、重连、刷新按钮 |
| 终端状态卡片 | 展示每个 QMT 终端的连接状态、账户信息、心跳时间 |
| 持仓统计卡片 | 显示持仓品种数、总持仓量、总市值、策略数 |
| 实盘持仓面板 | 按终端分组展示真实持仓,包含代码、持仓量、可用量、市值 |
| 虚拟账本面板 | 按策略分组展示 Redis 持仓记录 |
| 系统日志面板 | 实时滚动显示系统运行日志,按级别着色 |
| 配置查看模态框 | 展示 Redis 配置、终端配置、策略配置,支持复制 JSON 源码 |
6.3 访问地址
仪表盘默认访问地址为 http://localhost:8001,该地址在系统启动时打印在控制台。首次访问时会自动加载所有终端状态、持仓信息和系统日志。
7. 信号发送端(qmt_signal_sender.py)
7.1 模块定位
qmt_signal_sender.py 是 QMT 交易系统的信号生产端,部署在聚宽(JoinQuant)策略运行环境中。它负责将策略产生的买卖信号序列化后推送到 Redis 队列,由本地 QMT 交易引擎消费并执行。该模块是连接"策略研究/回测平台"与"实盘交易执行"的桥梁。
7.2 核心函数
send_qmt_signal(code, target_total_slots, price, context, redis_config)
| 参数 | 类型 | 说明 |
|---|---|---|
code |
str | 股票代码,聚宽格式(如 000001.XSHE、600519.XSHG) |
target_total_slots |
int | 目标总槽位数。大于 0 表示买入意图,等于 0 表示卖出(清仓)意图 |
price |
float | 当前最新价格,用于实盘限价单参考 |
context |
object | 聚宽上下文对象,提供 run_params.type(运行类型)和 current_dt(当前时间) |
redis_config |
dict | Redis 连接配置,包含 host、port、password、db、strategy_name 等字段 |
7.3 处理流程
策略触发信号
│
▼
1. 环境判断与流量控制
├─ 实盘模式 → 直接通过
└─ 回测模式 → 限制最多发送 10 条(防止回测刷爆队列)
│
▼
2. 建立 Redis 连接(socket_timeout=1s)
│
▼
3. 数据转换与规范化
├─ 股票代码格式转换:.XSHE → .SZ,.XSHG → .SH
└─ 动作判定:target_total_slots > 0 → BUY,= 0 → SELL
│
▼
4. 构建 JSON 消息体
│
▼
5. 队列路由
├─ 回测 → {strategy_name}_backtest(TTL: 1 小时)
└─ 实盘 → {strategy_name}_real(TTL: 7 天)
│
▼
6. 控制台日志输出
7.4 消息格式
发送到 Redis 队列的 JSON 消息结构:
{
"strategy_name": "my_strategy",
"stock_code": "000001.SZ",
"action": "BUY",
"price": 15.50,
"total_slots": 5,
"timestamp": "2026-02-17 14:30:00",
"is_backtest": false
}
| 字段 | 类型 | 说明 |
|---|---|---|
strategy_name |
str | 策略名称,来自 redis_config['strategy_name'],用于队列路由和持仓管理 |
stock_code |
str | QMT 格式的股票代码(.SZ / .SH) |
action |
str | 交易动作,BUY 或 SELL |
price |
float | 信号触发时的最新价格 |
total_slots |
int | 策略的总槽位数(BUY 时为策略设定值,SELL 时为 0) |
timestamp |
str | 信号生成时间,格式 YYYY-MM-DD HH:MM:SS |
is_backtest |
bool | 是否为回测环境发出的信号 |
7.5 买卖意图判定逻辑
信号发送端不直接区分"买入函数"和"卖出函数",而是通过 target_total_slots 参数的值进行语义推断:
target_total_slots > 0(BUY):策略意向持有该股票,total_slots传递策略的总持仓上限,供交易引擎计算单只股票的资金分配。target_total_slots = 0(SELL):策略意向清仓该股票,释放所占槽位。
7.6 回测流量控制
模块级全局变量 _BACKTEST_SEND_COUNT 用于限制回测模式下的信号发送数量,上限为 10 条。这一机制防止长周期回测期间大量无效信号涌入 Redis 队列,回测队列的 TTL 也相应缩短为 1 小时(实盘为 7 天)。
7.7 队列命名规则
| 运行模式 | 队列名格式 | TTL |
|---|---|---|
| 实盘 | {strategy_name}_real |
604800 秒(7 天) |
| 回测 | {strategy_name}_backtest |
3600 秒(1 小时) |
7.8 股票代码格式转换
| 来源平台 | 格式 | 示例 |
|---|---|---|
| 聚宽 | .XSHE / .XSHG |
000001.XSHE、600519.XSHG |
| QMT | .SZ / .SH |
000001.SZ、600519.SH |
8. 百分比下单信号发送端(qmt_percentage_sender.py)
8.1 模块定位
qmt_percentage_sender.py 是基于仓位百分比的 QMT 信号发送端,与槽位模式的 qmt_signal_sender.py 并行存在。该模块用于配置为 order_mode: "percentage" 的策略,通过指定目标持仓占账户总资产的比例来触发交易。
8.2 核心函数
send_qmt_percentage_signal(code, position_pct, action, price, is_backtest, timestamp, redis_config)
| 参数 | 类型 | 说明 |
|---|---|---|
code |
str | 股票代码,聚宽格式(如 000001.XSHE、600519.XSHG) |
position_pct |
float | 目标持仓占总资产的比例,范围 0.0 ~ 1.0(如 0.2 表示 20%) |
action |
str | 交易动作,固定为 "BUY" 或 "SELL" |
price |
float | 当前最新价格,用于实盘限价单参考 |
is_backtest |
bool | 是否为回测模式(True/False) |
timestamp |
str | 时间戳字符串,格式 "YYYY-MM-DD HH:MM:SS" |
redis_config |
dict | Redis 连接配置,包含 host、port、password、db、strategy_name 等字段 |
8.3 处理流程
策略触发信号
│
▼
1. 环境判断与流量控制
├─ 实盘模式 → 直接通过
└─ 回测模式 → 限制最多发送 10 条(防止回测刷爆队列)
│
▼
2. 建立 Redis 连接(socket_timeout=1s)
│
▼
3. 数据转换与规范化
└─ 股票代码格式转换:.XSHE → .SZ,.XSHG → .SH
│
▼
4. 构建 JSON 消息体(包含 position_pct 字段)
│
▼
5. 队列路由
├─ 回测 → {strategy_name}_backtest(TTL: 1 小时)
└─ 实盘 → {strategy_name}_real(TTL: 7 天)
│
▼
6. 控制台日志输出
8.4 消息格式
发送到 Redis 队列的 JSON 消息结构:
{
"strategy_name": "my_strategy",
"stock_code": "000001.SZ",
"action": "BUY",
"price": 15.50,
"position_pct": 0.2,
"timestamp": "2026-02-17 14:30:00",
"is_backtest": false
}
| 字段 | 类型 | 说明 |
|---|---|---|
strategy_name |
str | 策略名称,来自 redis_config['strategy_name'],用于队列路由和持仓管理 |
stock_code |
str | QMT 格式的股票代码(.SZ / .SH) |
action |
str | 交易动作,BUY 或 SELL |
price |
float | 信号触发时的最新价格 |
position_pct |
float | 目标持仓占账户总资产的比例,范围 0.0 ~ 1.0 |
timestamp |
str | 信号生成时间,格式 YYYY-MM-DD HH:MM:SS |
is_backtest |
bool | 是否为回测环境发出的信号 |
8.5 买卖意图判定逻辑
与槽位模式不同,百分比模式需要显式指定交易动作:
action = "BUY":策略意向买入该股票,目标持仓占比为position_pct。交易引擎根据账户总资产计算目标金额,然后转换为具体股数下单。action = "SELL":策略意向清仓该股票。百分比模式下卖出采用简化逻辑,直接执行清仓操作。
8.6 买入计算公式
当 QMT 端接收到百分比模式的买入信号时,按以下公式计算买入股数:
目标金额 = 账户总资产 × position_pct
可用金额 = min(目标金额, 可用资金)
买入股数 = int(可用金额 / 价格 / 100) × 100
边界处理:
- 单笔金额 < 2000 元 → 拦截不下单
- 计算股数 < 100 股 → 拦截不下单
- 价格 ≤ 0 → 强制设为 1.0(仅测试用)
8.7 与槽位模式的对比
| 特性 | 槽位模式 (slots) | 百分比模式 (percentage) |
|---|---|---|
| 核心参数 | total_slots |
position_pct |
| 持仓限制 | 有(同时持仓数量限制) | 无 |
| 资金分配 | 按剩余槽位均分资金 | 按总资产比例计算 |
| 卖出逻辑 | 按持仓数量计算 | 清仓 |
| 配置方式 | 配置文件设置 order_mode: "slots" |
配置文件设置 order_mode: "percentage" |
| 信号发送 | send_qmt_signal() |
send_qmt_percentage_signal() |
8.8 使用示例
from qmt_percentage_sender import send_qmt_percentage_signal
# Redis 配置
redis_config = {
"host": "localhost",
"port": 6379,
"password": None,
"db": 0,
"strategy_name": "MyPercentageStrategy"
}
# 买入信号:目标持仓占账户总资产的 20%
send_qmt_percentage_signal(
code="000001.XSHE",
position_pct=0.2,
action="BUY",
price=15.5,
is_backtest=False,
timestamp="2026-02-17 14:30:00",
redis_config=redis_config
)
# 卖出信号:清仓
send_qmt_percentage_signal(
code="000001.XSHE",
position_pct=0,
action="SELL",
price=15.8,
is_backtest=False,
timestamp="2026-02-17 14:35:00",
redis_config=redis_config
)
9. 系统架构
8.1 组件关系图
┌─────────────────────────────────────────────────────────────────────────────┐
│ QMT 交易系统架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ 启动入口 │ │ 多终端管理器 │ │ API 服务器 │ │
│ │ run.py │ ──> │ MultiEngineMgr │ <──> │ FastAPI + uvicorn │ │
│ └─────────────┘ │ │ │ │ │
│ └────────┬────────┘ └───────────┬─────────────┘ │
│ │ │ │
│ ┌──────────────────────┼──────────────────────────┤ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ 日志系统 │ │ 终端执行单元 │ │ Web 仪表盘 │ │
│ │ logs/ │ │ TradingUnit │ │ dashboard.html │ │
│ └─────────────┘ │ × N │ │ │ │
│ └────────┬────────┘ └─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ QMT 终端 │ │
│ │ xtquant 连接 │ │
│ └─────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────────┤
│ 外部依赖 │
│ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Redis │ │ QMT 终端 │ │ 浏览器客户端 │ │
│ │ 消息队列 │ │ 实盘交易 │ │ HTTP 请求 │ │
│ └─────────────┘ └─────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
8.2 数据流向图
┌─────────────────────────────────────────────────────────────────────────────┐
│ 数据流向 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 聚宽策略 ──> qmt_signal_sender ──> Redis 队列 ──> 消息处理循环 │
│ (信号发送端) {strategy}_real │ │
│ ▼ │
│ 槽位检查 ──> 资金检查 │
│ │ │
│ ▼ │
│ 订单执行 (QMT API) │
│ │ │
│ ┌───────────────────────┼───────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 成交回调处理 错误回调处理 持仓更新 (Redis) │
│ │ │ │ │
│ └───────────────────────┴───────────────────────┘ │
│ │ │
│ ▼ │
│ 日终清算 / 状态同步 │
│ │
├─────────────────────────────────────────────────────────────────────────────┤
│ 监控数据流向 │
│ │
│ 系统状态 ──> API 端点 ──> JSON 响应 ──> Web 仪表盘渲染 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
8.3 消息处理流程
- 消息接收:系统从 Redis 队列
{strategy_name}_real中取出消息 - 消息解析:将 JSON 消息解析为结构化数据,验证必填字段
- 日期校验:检查消息日期是否为当天,过期消息丢弃
- 槽位检查:查询策略已占用槽位,判断是否允许新买入
- 资金检查:查询账户可用资金,计算单只股票可买入金额
- 数量计算:根据资金和价格计算买入股数(向下取整为百股)
- 订单执行:调用 QMT API 下单,成功则缓存订单信息
- 状态更新:标记虚拟持仓,异步等待成交回调
10. 启动与停止
9.1 Windows 启动
使用提供的 start.bat 脚本启动系统:
start.bat
或手动启动:
cd qmt
python run.py
9.2 日志文件位置
系统日志保存在 qmt/logs/{日期}.log 目录下,文件名格式为 2026-01-27.log。日志按日期自动切分,当日期变化时创建新的日志文件。
9.3 端口说明
| 服务 | 默认端口 | 说明 |
|---|---|---|
| API 服务 | 8001 | Web 仪表盘和 RESTful API 监听端口 |
11. 注意事项
- QMT 终端要求:确保 QMT 终端已登录且路径配置正确
- Redis 服务:系统依赖 Redis 运行,请确保 Redis 服务可用
- 交易日时间:交易逻辑仅在 09:15-11:30 和 13:00-15:00 期间执行
- 维护时段:每日 21:32-21:50 为 QMT 维护时段,此时段不执行重连
- 权限要求:确保程序有权限写入
logs/目录