3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个非常经典的时间止损策略。在 QMT 中实现这个逻辑,我们需要利用 get_trade_detail_data 获取持仓信息(包括建仓日期),然后结合 get_trading_dates 计算持仓天数,最后判断盈亏状态进行卖出。
以下是完整的策略代码及详细说明。
m_strOpenDate),计算从建仓日到当前日期的交易日数量。# -*- coding: gbk -*-
import time
def init(ContextInfo):
# -------------------------------------------------------
# 1. 设置资金账号 (请务必修改为您自己的资金账号)
# -------------------------------------------------------
ContextInfo.accID = '600000248'
ContextInfo.accountType = 'STOCK' # 账号类型:'STOCK'股票, 'FUTURE'期货
# 绑定账号,用于接收回报和获取持仓
ContextInfo.set_account(ContextInfo.accID)
print("策略初始化完成,监控持仓中...")
def handlebar(ContextInfo):
# -------------------------------------------------------
# 2. 获取当前时间和持仓信息
# -------------------------------------------------------
# 获取当前K线的时间戳,并转为 YYYYMMDD 格式字符串
index = ContextInfo.barpos
timetag = ContextInfo.get_bar_timetag(index)
current_date_str = timetag_to_datetime(timetag, '%Y%m%d')
# 获取当前账户的持仓列表
# 参数:账号ID, 账号类型, 数据类型('POSITION'为持仓)
positions = get_trade_detail_data(ContextInfo.accID, ContextInfo.accountType, 'POSITION')
# 如果没有持仓,直接返回
if not positions:
return
# -------------------------------------------------------
# 3. 遍历持仓进行判断
# -------------------------------------------------------
for pos in positions:
stock_code = pos.m_strInstrumentID # 证券代码
open_date = pos.m_strOpenDate # 建仓日期 (字符串格式 YYYYMMDD)
volume = pos.m_nVolume # 持仓数量
can_use_volume = pos.m_nCanUseVolume # 可用数量 (防止卖出当日买入的股票)
# 如果可用持仓为0,说明是今日新买的,跳过
if can_use_volume == 0:
continue
# --- 计算持仓天数 ---
# 获取从建仓日(open_date)到当前日期(current_date_str)之间的交易日列表
# 注意:需要确保本地有下载对应的日线数据,否则 get_trading_dates 可能返回空
trading_days_list = ContextInfo.get_trading_dates(stock_code, open_date, current_date_str, -1, '1d')
if not trading_days_list:
# 如果获取不到交易日数据,无法计算天数,跳过
continue
days_held = len(trading_days_list)
# --- 判断是否上涨 ---
# 使用 m_dFloatProfit (浮动盈亏) 来判断
# 如果浮动盈亏 <= 0,视为"不涨" (包含保本和亏损)
is_not_rising = (pos.m_dFloatProfit <= 0)
# 也可以使用价格判断:现价 <= 成本价
# is_not_rising = (pos.m_dLastPrice <= pos.m_dOpenPrice)
# -------------------------------------------------------
# 4. 执行卖出逻辑
# -------------------------------------------------------
# 逻辑:持仓超过3个交易日 且 不涨
if days_held > 3 and is_not_rising:
print(f"触发止损: {stock_code}, 建仓日: {open_date}, 持仓天数: {days_held}, 浮动盈亏: {pos.m_dFloatProfit}")
# 执行下单:卖出
# opType=24 (卖出), orderType=1101 (单股单账号普通下单)
# priceType=5 (最新价), price=-1 (最新价模式下价格填-1即可)
passorder(24, 1101, ContextInfo.accID, stock_code, 5, -1, can_use_volume, ContextInfo)
get_trade_detail_data:
pos 中,m_strOpenDate 属性记录了该笔持仓的建仓日期,这是计算持仓时间的关键。ContextInfo.get_trading_dates:
当前日期 - 建仓日期,因为中间可能包含周末和节假日。len) 就是真实的持仓交易天数。pos.m_dFloatProfit:
pos.m_dFloatProfit <= 0 意味着当前是亏损或者持平状态,符合你“不涨”的定义。passorder 参数:
24: 代表股票卖出。1101: 代表普通交易模式。5: 代表报价类型为“最新价”。can_use_volume: 使用可用持仓量,避免T+1规则下卖出当日买入的股票导致报错。init 函数中的 ContextInfo.accID 修改为你实际的资金账号。get_trading_dates 依赖本地的基础日线数据。请在 QMT 客户端顶部菜单点击 “数据管理” -> “下载历史数据”,勾选日线数据进行补充,否则计算天数可能会出错。get_trade_detail_data 读取账户信息)。如果是纯历史回测,逻辑需要稍作调整(使用虚拟持仓变量)。Q: 这里的“3天”是指自然日还是交易日?
A: 代码中使用的是 get_trading_dates,所以计算的是交易日。如果你周五买入,周六周日不算时间,周一算第2天。如果你想用自然日,可以直接解析日期字符串相减。
Q: 如果我想改成“盈利少于 1% 也卖出”怎么办?
A: 可以修改判断条件。
将 is_not_rising = (pos.m_dFloatProfit <= 0)
改为 is_not_rising = (pos.m_dFloatProfit / pos.m_dMarketValue < 0.01) (即盈亏比例小于1%)。
Q: 为什么使用 can_use_volume 而不是 volume?
A: A股是 T+1 制度,当天买入的股票当天不能卖出。m_nVolume 是总持仓,m_nCanUseVolume 是可用持仓(昨仓)。使用可用持仓可以防止程序试图卖出今仓而报错。