3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个非常实用的风控需求。在 QMT 中实现基于资金曲线回撤的自动清仓(止损),核心逻辑如下:
get_trade_detail_data 获取账户的动态权益(总资产)。以下是完整的策略代码实现。
# -*- coding: gbk -*-
import time
def init(ContextInfo):
# ================= 配置区域 =================
# 请在此处填写您的资金账号
ContextInfo.accID = '6000000000'
# 账号类型:'STOCK'股票, 'FUTURE'期货
ContextInfo.accountType = 'STOCK'
# 止损阈值 (20%)
ContextInfo.stop_loss_threshold = 0.20
# ===========================================
# 绑定账号,用于接收回报和下单
ContextInfo.set_account(ContextInfo.accID)
# 初始化全局变量
ContextInfo.max_capital = 0.0 # 记录历史最高权益
ContextInfo.is_triggered = False # 标记是否已经触发了清仓
print("策略初始化完成,开始监控资金曲线...")
def handlebar(ContextInfo):
# 如果已经触发过清仓,则不再执行任何逻辑(防止清仓后又开仓)
if ContextInfo.is_triggered:
return
# 1. 获取账户资金信息
# get_trade_detail_data 返回的是一个列表,通常取第一个对象
account_obj_list = get_trade_detail_data(ContextInfo.accID, ContextInfo.accountType, 'ACCOUNT')
if not account_obj_list:
print("未获取到账户信息,请检查账号配置或连接状态")
return
account_info = account_obj_list[0]
# m_dBalance 代表总资产(动态权益)
current_capital = account_info.m_dBalance
# 2. 更新历史最高权益 (High Water Mark)
if ContextInfo.max_capital == 0:
ContextInfo.max_capital = current_capital
else:
if current_capital > ContextInfo.max_capital:
ContextInfo.max_capital = current_capital
# 3. 计算当前回撤
if ContextInfo.max_capital > 0:
drawdown = (ContextInfo.max_capital - current_capital) / ContextInfo.max_capital
else:
drawdown = 0.0
# 打印资金监控日志 (可选,避免日志过多可注释掉)
# print(f"当前资产: {current_capital:.2f}, 历史最高: {ContextInfo.max_capital:.2f}, 当前回撤: {drawdown*100:.2f}%")
# 4. 判断是否达到止损阈值
if drawdown >= ContextInfo.stop_loss_threshold:
msg = f"【警告】当前回撤 {drawdown*100:.2f}% 已达到阈值 {ContextInfo.stop_loss_threshold*100}%,触发清仓止损!"
print(msg)
ContextInfo.paint('回撤报警', 1, -1, 0, 'red', 'noaxis') # 在界面上画图提示
# 执行清仓操作
clear_all_positions(ContextInfo)
# 标记为已触发,停止后续逻辑
ContextInfo.is_triggered = True
def clear_all_positions(ContextInfo):
"""
清仓函数:遍历所有持仓并市价卖出
"""
# 获取当前持仓列表
positions = get_trade_detail_data(ContextInfo.accID, ContextInfo.accountType, 'POSITION')
if not positions:
print("当前无持仓,无需操作。")
return
print(f"开始执行清仓,共 {len(positions)} 只标的...")
for pos in positions:
stock_code = pos.m_strInstrumentID + "." + pos.m_strExchangeID # 拼接代码,如 000001.SZ
volume = pos.m_nVolume # 持仓数量
# 过滤掉数量为0的持仓(有时会有残余记录)
if volume <= 0:
continue
print(f"正在平仓: {stock_code}, 数量: {volume}")
# 下单参数:
# 24: 卖出
# 1101: 单股单账号普通交易
# ContextInfo.accID: 账号
# stock_code: 代码
# 14: 对手价 (为了快速成交,也可以用 5:最新价 或 44:对手方最优)
# -1: 价格 (市价/对手价模式下无效,填-1即可)
# volume: 数量
passorder(24, 1101, ContextInfo.accID, stock_code, 14, -1, volume, ContextInfo)
print("清仓指令发送完毕。")
init 函数:
ContextInfo.accID,请务必修改为您实际的资金账号。max_capital(最高权益)和 is_triggered(触发标记)。is_triggered 的作用是防止清仓后,策略的其他逻辑(如果有)又去买入股票,或者重复发送清仓指令。handlebar 函数:
get_trade_detail_data(..., 'ACCOUNT') 获取账户对象,其中 m_dBalance 属性即为当前的总资产(现金+持仓市值)。(历史最高 - 当前) / 历史最高。>= 0.20,调用 clear_all_positions 函数。clear_all_positions 函数:
get_trade_detail_data(..., 'POSITION') 获取当前所有持仓。passorder 函数发送卖出指令。14 (对手价),这是为了在止损时能尽快成交。如果是在极速变动的行情中,也可以考虑使用市价委托(需根据具体券商支持情况调整 prType 参数)。get_trade_detail_data,因此必须在实盘或模拟交易模式下运行,不能在纯回测模式下直接使用此逻辑(回测模式下资金获取方式不同)。handlebar 的运行频率取决于您订阅行情的频率。建议在主图上运行,或者订阅全推行情,以确保资金监控的实时性。is_triggered = True 停止运行。如果您后续入金了或者想重新开启策略,需要手动停止并重新运行策略以重置该变量。Q: 如何在回测中实现这个功能?
A: 回测中不需要使用 get_trade_detail_data。在回测模式下,您可以使用 ContextInfo.capital 获取初始资金,结合 ContextInfo.get_net_value(ContextInfo.barpos) 获取当前净值来计算总资产和回撤。
Q: 为什么使用 passorder 而不是 order_target_value?
A: passorder 是 QMT 最底层的下单接口,对于清仓这种操作,它能更精确地控制下单类型(如使用对手价或市价)和指定具体的资金账号,在实盘风控场景下更可靠。
Q: 清仓后如何自动停止策略?
A: 代码中通过 if ContextInfo.is_triggered: return 实现了逻辑上的停止。如果您希望彻底停止 Python 引擎,可以在清仓后调用 ContextInfo.stop()(注意:这可能会导致整个策略线程停止)。