3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个关于量化交易策略风控的具体实现问题。
要实现基于账户最大回撤的止损,需要维护一个“高水位线”(High Water Mark),即策略运行以来的最高账户总资产。
handlebar 周期(或定时器中),通过 get_trade_detail_data 获取账户的动态权益(总资产)。当前回撤 = 1 - (当前总资产 / 高水位线)。以下是完整的策略代码示例。该代码适用于 QMT 的实盘或模拟盘模式(Run Mode)。
# -*- coding: gbk -*-
import time
def init(ContextInfo):
# ================= 策略参数设置 =================
# 设置资金账号 (请修改为您实际的资金账号)
ContextInfo.accID = '6000000000'
# 账号类型: 'STOCK'股票, 'FUTURE'期货, 'CREDIT'信用
ContextInfo.accType = 'STOCK'
# 最大回撤止损阈值 (例如 0.10 代表 10%)
ContextInfo.drawdown_limit = 0.10
# ================= 全局变量初始化 =================
# 记录账户历史最高权益 (高水位线)
ContextInfo.max_balance = 0.0
# 标记是否已经触发止损
ContextInfo.is_stop_triggered = False
# 绑定账号,用于接收回报
ContextInfo.set_account(ContextInfo.accID)
print("策略初始化完成,账户回撤止损阈值设置为: {:.2%}".format(ContextInfo.drawdown_limit))
def handlebar(ContextInfo):
# 如果已经触发了止损,不再执行任何逻辑 (或者可以在这里只执行平仓确认逻辑)
if ContextInfo.is_stop_triggered:
return
# 1. 获取账户资金信息
# get_trade_detail_data 返回的是一个列表,包含账户对象
account_list = get_trade_detail_data(ContextInfo.accID, ContextInfo.accType, 'account')
if not account_list:
print("未获取到账户信息,跳过本次检查")
return
# 获取账户对象
account_obj = account_list[0]
# m_dBalance 为总资产(动态权益)
current_balance = account_obj.m_dBalance
# 2. 初始化或更新高水位线 (Max Balance)
if ContextInfo.max_balance == 0:
ContextInfo.max_balance = current_balance
elif current_balance > ContextInfo.max_balance:
ContextInfo.max_balance = current_balance
# 3. 计算当前回撤
# 回撤 = 1 - (当前净值 / 最高净值)
current_drawdown = 0.0
if ContextInfo.max_balance > 0:
current_drawdown = 1 - (current_balance / ContextInfo.max_balance)
# 打印当前状态 (可选,用于调试)
# print(f"当前资产: {current_balance}, 历史最高: {ContextInfo.max_balance}, 当前回撤: {current_drawdown:.2%}")
# 4. 判断是否超过阈值
if current_drawdown >= ContextInfo.drawdown_limit:
print("【警告】当前回撤 {:.2%} 已超过阈值 {:.2%},触发账户级止损!开始清仓...".format(
current_drawdown, ContextInfo.drawdown_limit))
# 执行清仓操作
clean_all_positions(ContextInfo)
# 标记止损已触发,防止重复下单或继续开仓
ContextInfo.is_stop_triggered = True
def clean_all_positions(ContextInfo):
"""
清空所有持仓
"""
# 获取当前持仓列表
position_list = get_trade_detail_data(ContextInfo.accID, ContextInfo.accType, 'position')
if not position_list:
print("当前无持仓,无需平仓。")
return
for pos in position_list:
# 获取持仓数量 (m_nVolume 为当前拥股)
volume = pos.m_nVolume
# 获取证券代码
stock_code = pos.m_strInstrumentID
# 如果持仓量为0,跳过
if volume <= 0:
continue
print(f"正在平仓: {stock_code}, 数量: {volume}")
# 下单平仓
# opType: 24 (股票卖出)
# orderType: 1101 (单股、单账号、普通、股/手方式下单)
# accountID: 资金账号
# orderCode: 股票代码
# prType: 14 (对手价,保证尽快成交) 或者 5 (最新价)
# price: -1 (对手价模式下价格无效,填-1即可)
# volume: 数量
passorder(24, 1101, ContextInfo.accID, stock_code, 14, -1, volume, ContextInfo)
print("清仓指令发送完毕。")
# 必须实现的空函数,防止报错(如果策略中没有用到)
def stop(ContextInfo):
pass
init 函数:
ContextInfo.drawdown_limit(回撤阈值)。ContextInfo.max_balance 为 0,用于记录策略启动后的最高资产。ContextInfo.is_stop_triggered 标志位,一旦止损触发,将其置为 True,防止策略在后续的 K 线中继续运行开仓逻辑或重复发送平仓单。handlebar 函数:
get_trade_detail_data(..., 'account') 获取账户详情。m_dBalance 属性代表当前的总资产(现金+持仓市值)。1 - (当前资产 / 历史最高资产)。clean_all_positions 函数。clean_all_positions 函数:
get_trade_detail_data(..., 'position') 获取当前所有持仓。m_strInstrumentID(代码)和 m_nVolume(持仓量)。passorder 函数发送卖出指令。prType=14(对手价),目的是在止损时尽快成交,而不是挂单等待。get_trade_detail_data 来获取真实的账户资金。ContextInfo.capital 加上累计盈亏来计算净值,或者使用 ContextInfo.get_net_value(barpos) 接口。max_balance 是从策略启动那一刻开始记录的。如果你希望基于账户昨天的静态权益开始计算,可以在 init 中尝试获取 m_dPreBalance(期初权益)作为初始高水位线。is_stop_triggered 变为 True,策略实际上就“死”了(不再执行逻辑)。如果需要人工干预后重启,需要重新运行策略或添加手动重置标志位的逻辑。Q: 为什么使用 get_trade_detail_data 而不是 ContextInfo.capital?
A: ContextInfo.capital 通常用于回测时的初始资金设置。在实盘或模拟交易中,资金是动态变化的,且可能包含非策略产生的资金变动(如出入金),get_trade_detail_data 能获取柜台实时的总资产数据,更加准确。
Q: passorder 中的 prType=14 是什么意思?
A: prType=14 代表“对手价”。在股票卖出时,对手价通常指“买一价”。使用对手价下单可以提高成交概率,适合止损这种需要快速离场的场景。
Q: 如何防止因为数据瞬间波动导致的误止损?
A: 可以在判断回撤时增加一个确认机制,例如连续 3 个 tick(分笔)都检测到回撤超标才触发,或者在计算回撤时使用最近几分钟的资产均值。但在极端行情下,立即止损通常是更安全的选择。