🚀 AI 一键生成 qmt 策略代码
立即体验
👉 AI 生成代码 👈

QMT量化平台回测无成交记录排查指南(附passorder详解)

qmt | 发布时间: 2026/5/29
以下内容由 EasyQuant 生成。

问题描述

【求助】QMT内置Python策略回测无成交记录,已按文档操作仍失败

大家好,我是从聚宽迁移过来的用户,在开发 QMT 内置 Python 策略时遇到几个问题,已经折腾了几天了,客服让我来社区求助。

一、核心问题:回测无成交记录

现象:

策略运行后,日志中有 [BUY] 和 [SELL] 打印
但回测报告窗口是空白的(只有框子,没有内容)
用 get_trade_detail_data('test', 'stock', 'deal') 获取成交记录,返回空
用 get_trade_detail_data('test', 'stock', 'position') 获取持仓,也返回空
回测参数设置:

已按文档要求以"副图模式"执行
最大成交比例设置为 100
回测时间:2024-01-01 到 2024-12-31
初始资金:500000
问题:

回测报告空白是什么原因?策略到底有没有实际成交?
如何确认回测是否成交?有没有可靠的查看方式?
"当日买入"和"持仓明细"面板在回测中是否本来就没有内容?
二、下单函数选择疑问

我看到两种下单方式,不确定哪个对:

方式 1(网上找的,8 个参数):
passorder(23, 1101, accountid, stock, 11, price, volume, ContextInfo)

方式 2(官方文档,11 个参数):
passorder(opcode, ordertype, accountid, stock, price_type, price, volume, strategy_name, quicktrade, remark, C)

问题:

passorder() 到底是 8 个参数还是 11 个参数?
官方文档写的是 11 个参数,但网上大部分示例是 8 个参数,哪个是对的?
回测中应该用哪种下单方式?
三、其他疑问

order_target_value() 在回测中是否能用?官方文档没有提到这个函数
get_bar_timetag(barpos) 在回测中返回 0,这是正常的吗?有没有其他获取时间的方法?
代码中已经加了 #coding:gbk,还有哪里需要注意?
四、测试代码(简化版)

#coding:gbk
import pandas as pd
import numpy as np

def init(C):
C.accountid = "testS"
C.set_universe(['000820.SZ', '601700.SH'])
C.positions = {}
print('Strategy initialized')

def handlebar(C):
barpos = C.barpos

# Get current time
timetag = C.get_bar_timetag(barpos)
if timetag > 0:
import datetime
timestamp_sec = timetag / 1000.0
dt = datetime.datetime.fromtimestamp(timestamp_sec)
current_time = dt.hour * 100 + dt.minute
else:
current_time = 0

# Check buy signal at 14:50
if current_time == 1450:
for code in C.get_universe():
# Buy logic (simplified)
passorder(23, 1101, C.accountid, code, 5, -1, 100, C)
print('BUY %s' % code)

# Check sell signal at 14:30
if current_time == 1430:
for code in C.get_universe():
if code in C.positions:
passorder(24, 1101, C.accountid, code, 5, -1, 100, C)
print('SELL %s' % code)

日志输出:
Strategy initialized
BUY 000820.SZ
BUY 601700.SH
SELL 000820.SZ
SELL 601700.SH

但回测报告空白,get_trade_detail_data 返回空。

请问各位大佬,这是哪里做错了?是下单函数参数不对,还是回测配置有问题?

感谢!

解决方案

欢迎来到 QMT 量化平台!从聚宽(JoinQuant)等云端平台迁移到 QMT 客户端时,由于底层运行机制(特别是数据获取和回测撮合机制)的差异,确实容易遇到一些“水土不服”的问题。针对您提到的回测无成交、passorder 参数疑惑以及其他 API 问题,以下是详细的排查指南和解答:

一、 核心问题:回测无成交记录、报告空白

在 QMT 中,如果日志打印了买卖信号但回测报告空白,通常由以下几个原因导致:

1. 历史行情数据未下载(最常见原因)
QMT 是本地客户端,回测依赖本地数据。如果您没有在【数据管理】中下载对应时间段(2024-01-01 到 2024-12-31)、对应周期(如 1分钟线)的行情数据,策略虽然能空跑循环,但底层撮合引擎没有数据支撑,自然无法产生成交。

  • 解决办法:点击客户端顶部的【数据管理】 -> 【补充数据】,选择您股票池中的股票和对应的周期,下载完整的历史数据。

2. 周期设置不匹配
您的代码中使用了 current_time == 1450(即 14:50)的逻辑。这要求策略必须运行在 1分钟(1m) 周期下。如果您的策略默认周期是日线(1d),get_bar_timetag 解析出来的时间通常是 15:00:00 或 00:00:00,永远无法触发 14:50 的条件。

  • 解决办法:在策略编辑器的【基本信息】中,将“默认周期”改为“1分钟线”,并在回测参数中确认主图周期为 1分钟。

3. 错误的查询函数
您使用了 get_trade_detail_data 来查询回测成交记录。注意:get_trade_detail_data 仅用于实盘或模拟盘获取真实柜台的交易明细,在回测模式下无效(返回空)。

  • 解决办法:在回测模式下,如果想在代码中获取记录,应使用 get_result_records(recordtype, index, ContextInfo) 函数(例如 get_result_records('dealdetails', C.barpos, C))。回测完成后,直接查看界面右侧的“绩效分析”和“操作明细”面板即可。

二、 下单函数 passorder 参数疑问

问题:passorder() 到底是 8 个参数还是 11 个参数?
答案:两者都对。

根据 QMT 官方 API 文档,passorder 的完整定义如下:
passorder(opType, orderType, accountid, orderCode, prType, modelprice, volume[, strategyName, quickTrade, userOrderId], ContextInfo)

  • 必填参数(8个)opType, orderType, accountid, orderCode, prType, modelprice, volume, ContextInfo
  • 可选参数(3个)strategyName(策略名), quickTrade(快速交易标志), userOrderId(自定义订单ID)。这三个参数被包裹在 [] 中,代表可以省略。

结论:在回测中,直接使用 8 个参数的方式(如您代码中的 passorder(23, 1101, C.accountid, code, 5, -1, 100, C))是完全正确且最常用的。

三、 其他疑问解答

1. order_target_value() 在回测中是否能用?
能用。 QMT 提供了丰富的封装下单函数,包括 order_target_value(目标价值下单)、order_value(指定价值下单)、order_lots(指定手数下单)等。这些函数在回测和实盘中均支持。官方文档中明确包含了该函数:
order_target_value(stockcode, tar_value[, style, price], ContextInfo[, accId])

2. get_bar_timetag(barpos) 返回 0 是正常的吗?
不正常。 返回 0 通常意味着:

  • 当前 K 线索引(barpos)越界或无效。
  • 本地没有该股票的历史数据,导致无法获取时间戳。
    请务必先去【数据管理】补充数据。

四、 测试代码修正建议

为了让您的策略顺利跑通,请参考以下修正后的代码,并确保在 1分钟周期下运行,且已下载数据

#coding:gbk
import datetime

def init(C):
    # 设置虚拟回测账号
    C.accountid = "testS"
    C.set_account(C.accountid)
    C.set_universe(['000820.SZ', '601700.SH'])
    print('Strategy initialized')

def handlebar(C):
    # 获取当前时间戳
    timetag = C.get_bar_timetag(C.barpos)
    if timetag <= 0:
        return
        
    # 转换为 datetime 对象
    dt = datetime.datetime.fromtimestamp(timetag / 1000.0)
    current_time = dt.hour * 100 + dt.minute

    # 14:50 买入逻辑
    if current_time == 1450:
        for code in C.get_universe():
            # 使用 order_value 或 passorder 均可
            # 这里演示使用 8参数的 passorder,买入 100股
            passorder(23, 1101, C.accountid, code, 5, -1, 100, C)
            print(f'[{dt}] BUY {code}')

    # 14:30 卖出逻辑
    if current_time == 1430:
        for code in C.get_universe():
            # 回测中获取当前持仓,应使用 get_result_records 或自行维护
            # 简单起见,这里直接尝试卖出 100股
            passorder(24, 1101, C.accountid, code, 5, -1, 100, C)
            print(f'[{dt}] SELL {code}')

总结排查步骤

  1. 检查【数据管理】是否下载了 2024 年的 1分钟线数据。
  2. 检查策略【基本信息】和【回测参数】中的周期是否设为了“1分钟线”。
  3. 运行回测,查看右侧“操作明细”面板,即可看到真实的成交记录。