问题描述
解决方案
要将 QMT 的成交记录导出为 JSON 文件,主要涉及以下三个步骤:
- 获取数据:使用
get_trade_detail_data函数获取成交(DEAL)类型的对象列表。 - 数据转换:QMT 返回的是 C++ 封装的对象(如
CDealDetail),无法直接被 JSON 序列化。需要遍历对象属性(通常以m_开头)并将其转换为标准的 Python 字典。 - 文件写入:使用 Python 标准库
json将字典列表写入文件。
以下是完整的策略代码实现。
策略代码
# -*- coding: gbk -*-
import json
import os
import datetime
def init(ContextInfo):
# ================= 配置区域 =================
# 请在此处替换您的资金账号
ContextInfo.accID = '6000000000'
# 账号类型:'STOCK' (股票), 'FUTURE' (期货), 'CREDIT' (两融)
ContextInfo.accType = 'STOCK'
# 导出文件的路径 (请确保文件夹存在)
ContextInfo.json_path = 'D:/qmt_trade_records.json'
# ===========================================
# 绑定账号,必须步骤
ContextInfo.set_account(ContextInfo.accID)
print("策略初始化完成,准备导出成交记录...")
def handlebar(ContextInfo):
# 仅在最后一根K线(实时行情或回测结束时)执行一次导出,避免重复写入
if not ContextInfo.is_last_bar():
return
# 1. 获取成交记录对象列表
# 参数说明:账号ID, 账号类型, 数据类型('DEAL'代表成交)
deal_objects = get_trade_detail_data(ContextInfo.accID, ContextInfo.accType, 'DEAL')
if not deal_objects:
print("未查询到成交记录,跳过导出。")
return
# 2. 将 QMT 对象转换为字典列表
data_list = []
for obj in deal_objects:
deal_dict = convert_obj_to_dict(obj)
data_list.append(deal_dict)
# 3. 写入 JSON 文件
try:
with open(ContextInfo.json_path, 'w', encoding='utf-8') as f:
# ensure_ascii=False 保证中文能正常显示,indent=4 用于美化格式
json.dump(data_list, f, ensure_ascii=False, indent=4)
print(f"成功导出 {len(data_list)} 条成交记录到: {ContextInfo.json_path}")
except Exception as e:
print(f"导出 JSON 失败: {e}")
def convert_obj_to_dict(qmt_obj):
"""
辅助函数:将 QMT 的 C++ 对象属性转换为 Python 字典
QMT 的属性通常以 'm_' 开头
"""
result = {}
# 获取对象的所有属性名
attributes = dir(qmt_obj)
for attr in attributes:
# 过滤掉内置方法,只保留 m_ 开头的属性
if attr.startswith('m_'):
try:
value = getattr(qmt_obj, attr)
# 处理一下可能的特殊类型,确保 JSON 可序列化
if isinstance(value, (str, int, float, bool, type(None))):
result[attr] = value
else:
# 对于复杂类型,转为字符串
result[attr] = str(value)
except Exception:
continue
# 为了方便阅读,可以手动映射一些关键字段名(可选)
# 如果不需要重命名,可以直接返回 result
formatted_result = {
"stock_code": result.get("m_strInstrumentID", ""), # 证券代码
"stock_name": result.get("m_strInstrumentName", ""), # 证券名称
"direction": result.get("m_nDirection", 0), # 买卖方向 (48买, 49卖)
"price": result.get("m_dPrice", 0.0), # 成交均价
"volume": result.get("m_nVolume", 0), # 成交数量
"amount": result.get("m_dTradeAmount", 0.0), # 成交金额
"trade_time": result.get("m_strTradeTime", ""), # 成交时间
"trade_date": result.get("m_strTradeDate", ""), # 成交日期
"order_id": result.get("m_strOrderSysID", ""), # 委托编号
"trade_id": result.get("m_strTradeID", ""), # 成交编号
"commission": result.get("m_dComssion", 0.0), # 手续费
"remark": result.get("m_strRemark", ""), # 备注
"raw_data": result # 保留原始所有字段以备查
}
return formatted_result
代码说明
get_trade_detail_data: 这是核心 API,用于获取交易明细。第三个参数传入'DEAL'即表示获取成交记录。如果需要导出委托记录,可将其改为'ORDER'。convert_obj_to_dict: QMT 返回的数据是封装好的对象,不能直接用json.dump。这个函数通过dir()遍历对象属性,提取所有以m_开头的字段(这是 QMT 底层 C++ 对象的命名规范),并将其转化为字典。handlebar控制: 代码中加入了if not ContextInfo.is_last_bar(): return判断,确保只在运行到最新行情或回测结束时执行一次导出操作,防止在每一根历史 K 线上重复写入文件导致 IO 瓶颈。- JSON 格式化: 使用
ensure_ascii=False可以让输出的 JSON 文件直接显示中文(如股票名称),而不是 Unicode 编码。
常见问题 Q&A
Q: 为什么导出的文件中没有数据?
A: 请检查 ContextInfo.accID 是否填写正确,且该账号在 QMT 交易终端中已登录。此外,get_trade_detail_data 只能获取当日的成交记录(部分券商版本支持获取历史,视具体柜台接口而定)。如果是回测模式,它会导出回测期间产生的所有模拟成交。
Q: 如何导出历史成交记录(非当日)?
A: QMT 的 Python API 主要针对当日交易和策略运行。如果要获取历史已结算的成交记录,通常需要通过券商提供的外部文件导入,或者如果您的 QMT 版本支持,尝试查阅是否有 get_history_trade_data 类接口(标准版 API 通常仅支持当日)。
Q: 导出的买卖方向是数字 48 和 49,是什么意思?
A: 这是 ASCII 码对应的值。
48对应字符'0',在 QMT 定义中通常代表 买入 (ENTRUST_BUY)。49对应字符'1',在 QMT 定义中通常代表 卖出 (ENTRUST_SELL)。- 建议在处理 JSON 数据时建立一个映射表来翻译这些代码。
Q: 可以自动定时导出吗?
A: 可以。您可以在 init 中使用 ContextInfo.run_time 设置一个定时任务,例如每天收盘后 15:05 执行导出函数。