问题描述
解决方案
在 QMT 平台中,要实现对交易账户资金和风险状态的实时监控,最有效的方法是使用 交易回报实时主推函数 中的 account_callback。
相比于在 handlebar 中轮询查询,account_callback 是事件驱动的。这意味着只有当资金账号状态发生变化(如成交、出入金、持仓市值变化导致净值改变)时,该函数才会被触发。这种方式响应速度最快,且占用资源最少。
实现步骤
- 初始化 (
init):必须使用ContextInfo.set_account(account_id)绑定需要监控的资金账号。如果不绑定,回调函数将不会接收到任何推送。 - 定义回调 (
account_callback):编写该函数逻辑,当收到推送时,从accountInfo对象中提取总资产、可用资金、冻结资金及风险度等关键信息。
策略代码实现
以下是一个完整的 Python 策略示例,专门用于监控并打印账户资金变动:
# -*- coding: gbk -*-
def init(ContextInfo):
"""
初始化函数
"""
# 1. 设置需要监控的资金账号
# 请将 'YOUR_ACCOUNT_ID' 替换为您实际的资金账号,例如 '600000248'
# 注意:实盘模式下必须设置正确的账号,回测模式下该回调可能不触发或行为不同
account_id = '600000248'
ContextInfo.account_id = account_id
# 2. 绑定账号以接收主推消息
# 这一步至关重要,没有这一步 account_callback 不会被触发
ContextInfo.set_account(account_id)
print(f"策略初始化完成,开始监控账号: {account_id}")
def handlebar(ContextInfo):
"""
行情驱动函数
"""
# 此策略主要依赖回调监控资金,handlebar 中无需编写轮询逻辑
pass
def account_callback(ContextInfo, accountInfo):
"""
资金账号状态变化主推函数
当资金、权益、风险度发生变化时,系统自动调用此函数
"""
# 过滤非当前监控账号的推送(如果是多账号策略)
if accountInfo.m_strAccountID != ContextInfo.account_id:
return
print("=" * 40)
print(f"【资金变动实时监控】 时间: {ContextInfo.get_bar_timetag(ContextInfo.barpos)}")
print(f"账号ID: {accountInfo.m_strAccountID}")
# --- 核心资金数据 ---
# 总资产 (动态权益/市值)
print(f"总资产 (Balance): {accountInfo.m_dBalance:.2f}")
# 可用资金
print(f"可用资金 (Available): {accountInfo.m_dAvailable:.2f}")
# 冻结资金 (保证金 + 手续费等)
print(f"冻结资金 (Frozen): {accountInfo.m_dFrozenCash:.2f}")
# --- 风险与持仓数据 ---
# 风险度 (通常用于期货或两融,普通股票通常为0或无效)
# 计算公式通常为:占用保证金 / 权益
print(f"风险度 (Risk): {accountInfo.m_dRisk:.2f}")
# 持仓盈亏
print(f"持仓盈亏 (Position Profit): {accountInfo.m_dPositionProfit:.2f}")
# 净值 (Nav)
print(f"单位净值 (Nav): {accountInfo.m_dNav:.4f}")
# --- 针对信用/期货账户的额外字段 ---
# 如果是信用账户,可以监控总负债
if accountInfo.m_dTotalDebit > 0:
print(f"总负债 (Total Debit): {accountInfo.m_dTotalDebit:.2f}")
print("=" * 40)
# 扩展逻辑:可以在这里添加风控报警
# 例如:如果可用资金低于某个阈值,或者风险度过高,发送通知或停止交易
# if accountInfo.m_dRisk > 0.8:
# print("警告:风险度过高!")
代码关键点解析
-
ContextInfo.set_account(account_id):- 这是开启实时监控的开关。必须在
init中调用。如果策略需要监控多个账号,可以多次调用该函数传入不同的账号 ID。
- 这是开启实时监控的开关。必须在
-
accountInfo对象属性:m_dBalance: 总资产。这是最常用的字段,代表当前的动态权益。m_dAvailable: 可用资金。用于判断当前是否还有资金进行新开仓。m_dFrozenCash: 冻结资金。包含挂单冻结、保证金占用等。m_dRisk: 风险度。- 对于期货/两融账户,这是一个核心风控指标。
- 对于普通股票账户,此值可能没有实际意义(通常显示为 0)。
m_dPositionProfit: 持仓盈亏。反映当前持仓的浮动盈亏情况。
-
运行模式:
- 此功能必须在实盘(或连接了柜台的模拟盘)模式下运行才有效。
- 在纯历史回测模式下,
account_callback通常不会模拟资金推送,回测中的资金变化通常在handlebar内部通过ContextInfo.capital等变量体现。
常见问题 (Q&A)
Q: 为什么我的 account_callback 从来没有被触发?
A: 请检查以下两点:
- 是否在
init函数中正确调用了ContextInfo.set_account('账号ID')。 - 策略是否处于“实盘”运行模式。如果是在编辑器的“回测”模式下,该回调不会触发。
Q: 如何监控持仓变化而不是资金变化?
A: QMT 提供了 position_callback(ContextInfo, positionInfo) 函数。用法与 account_callback 类似,当持仓数量或状态发生变化时会触发,用于监控具体的持仓明细。
Q: m_dRisk 风险度是如何计算的?
A: 具体的计算方式依赖于柜台返回的数据。一般而言,在期货中风险度 = 保证金占用 / 客户权益。在股票两融中,通常关注维持担保比例(m_dPerAssurescaleValue,位于信用账号详情中),而不是简单的 m_dRisk 字段。
Q: 我可以在回调函数中直接下单吗?
A: 可以。例如,当检测到 m_dAvailable (可用资金) 大于一定金额时触发买入,或者当 m_dRisk (风险度) 超过警戒线时触发平仓(强平保护)。但要注意避免死循环下单(例如下单导致资金变化,资金变化又触发回调)。