问题描述
解决方案
在 QMT 平台中,获取回测详细数据(如每一笔成交明细和每日持仓)主要通过 get_result_records 函数来实现。
核心函数说明
get_result_records(recordtype, index, ContextInfo)
- recordtype (字符串): 指定获取的数据类型。
'dealdetails': 获取交易明细(每一笔买卖记录)。'holdings': 获取当前持仓(指定 K 线位置的持仓)。'buys': 获取买入持仓历史。'sells': 获取卖出持仓历史。'historysums': 获取历史汇总信息。
- index (整数): K 线索引号,通常使用
ContextInfo.barpos。 - ContextInfo: 全局上下文对象。
代码实现
以下是一个完整的 Python 策略示例。该策略会在回测结束时(最后一根 K 线),自动打印出回测期间所有的交易明细和最终持仓情况。
# -*- coding: gbk -*-
import pandas as pd
def init(ContextInfo):
# 设置股票池,这里以平安银行为例
ContextInfo.set_universe(['000001.SZ'])
# 设置基准
ContextInfo.benchmark = '000300.SH'
# 设置账号(回测模式下为虚拟账号)
ContextInfo.set_account('account_name')
# 简单的策略逻辑变量:记录是否买过
ContextInfo.has_bought = False
def handlebar(ContextInfo):
# 获取当前K线索引
index = ContextInfo.barpos
# 获取当前时间
timetag = ContextInfo.get_bar_timetag(index)
# --- 简单的交易逻辑用于生成数据 ---
# 如果是第一根K线,买入
if index == 0 and not ContextInfo.has_bought:
# 按最新价买入 1000 股
order_shares('000001.SZ', 1000, 'fix', 10.0, ContextInfo, 'account_name')
ContextInfo.has_bought = True
print(f"触发买入: 000001.SZ, 时间: {timetag}")
# 如果是第 10 根K线,卖出 500 股
if index == 10:
order_shares('000001.SZ', -500, 'fix', 11.0, ContextInfo, 'account_name')
print(f"触发卖出: 000001.SZ, 时间: {timetag}")
# --- 获取回测数据的核心逻辑 ---
# 通常我们在回测结束时(最后一根K线)一次性获取所有记录
if ContextInfo.is_last_bar():
print("=" * 30)
print("回测结束,开始获取详细数据...")
# 1. 获取交易明细 (每一笔买卖)
# recordtype = 'dealdetails'
trade_records = get_result_records('dealdetails', index, ContextInfo)
print(f"\n--- 交易明细 (共 {len(trade_records)} 笔) ---")
for record in trade_records:
# record 是一个对象,包含多个属性
# 将毫秒时间戳转换为可读时间
trade_time = timetag_to_datetime(record.trade_date, '%Y-%m-%d %H:%M:%S')
# 解析买卖方向
direction_str = "买入" if record.open_close == 1 else "卖出" # 股票: 1买 0卖
print(f"时间: {trade_time}, 代码: {record.stockcode}, "
f"方向: {direction_str}, 价格: {record.trade_price:.2f}, "
f"成交额: {record.trade_balance:.2f}, 手续费: {record.commission:.2f}")
# 2. 获取当前持仓 (最终持仓)
# recordtype = 'holdings'
holding_records = get_result_records('holdings', index, ContextInfo)
print(f"\n--- 最终持仓 (共 {len(holding_records)} 只) ---")
for hold in holding_records:
print(f"代码: {hold.stockcode}, 持仓数量: {hold.position}, "
f"持仓成本: {hold.trade_price:.2f}, 最新价: {hold.current_price:.2f}, "
f"持仓盈亏: {hold.profit:.2f}")
print("=" * 30)
返回对象属性详解
get_result_records 返回的是一个列表,列表中的每个对象包含以下常用属性(可以通过 dir(obj) 查看所有属性):
-
交易明细 (
dealdetails) 对象属性:stockcode: 股票代码trade_date: 交易时间(毫秒级时间戳)open_close: 开平标志(股票:1 买,0 卖;期货:1 开,0 平)trade_price: 成交价格trade_balance: 成交金额commission: 手续费operate_type: 操作类型
-
持仓 (
holdings) 对象属性:stockcode: 股票代码position: 持仓数量trade_price: 持仓成本价current_price: 当前最新价profit: 持仓盈亏market: 市场代码
常见问题 Q&A
Q: 如何获取每日的持仓变化,而不仅仅是最终持仓?
A: get_result_records('holdings', index, ContextInfo) 获取的是 index 对应的那个时刻的持仓。如果你需要每日持仓,可以在 handlebar 函数中判断每日收盘时间(例如判断时间戳或 ContextInfo.period 为日线),然后在每一根 K 线结束时调用该函数并将结果保存到全局列表或写入文件。
Q: 为什么我在实盘模式下调用这个函数没有数据?
A: get_result_records 函数仅支持回测模式。在实盘或模拟交易模式下,你需要使用 get_trade_detail_data 函数来获取账号的实际委托、成交和持仓信息。
Q: 返回的交易时间是时间戳,如何转换成日期?
A: 使用 QMT 内置函数 timetag_to_datetime(timetag, format),例如 timetag_to_datetime(record.trade_date, '%Y%m%d')。