问题描述
解决方案
这是一个基于 QMT 平台的 ROC(变动率指标)动量策略实现。
策略逻辑
- ROC 计算公式:$ROC = \frac{今天的收盘价 - N天前的收盘价}{N天前的收盘价} \times 100$。
- 买入条件:ROC 线从下向上穿过 0 轴(即上一根 K 线 ROC < 0,当前 K 线 ROC > 0)。
- 卖出条件:ROC 线从上向下穿过 0 轴(即上一根 K 线 ROC > 0,当前 K 线 ROC < 0)。
- 仓位管理:信号触发时全仓买入或清仓卖出。
策略代码
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
"""
初始化函数,设定策略参数和账号
"""
# 策略参数设置
ContextInfo.N = 12 # ROC指标的周期,通常为12
ContextInfo.account_id = '6000000000' # 请在此处填写您的资金账号
ContextInfo.account_type = 'STOCK' # 账号类型:'STOCK'股票, 'FUTURE'期货
# 绑定账号,用于接收交易回报(实盘必须)
ContextInfo.set_account(ContextInfo.account_id)
print("策略初始化完成,ROC周期: {}".format(ContextInfo.N))
def handlebar(ContextInfo):
"""
K线周期运行函数
"""
# 获取当前主图的股票代码
stock_code = ContextInfo.stockcode
# 确保是最后一根K线才下单(避免历史回测时的信号闪烁,实盘中通常只在最新K线判断)
# 如果是回测模式,每一根K线都会运行;如果是实盘,ContextInfo.is_last_bar()为True时代表最新行情
# 计算 ROC 需要 N 天前的数据,为了计算上一个 ROC(用于判断穿越),
# 我们至少需要 N + 2 根 K 线的数据。这里取 N + 10 以防万一。
count = ContextInfo.N + 10
# 获取历史行情数据
# 使用 get_market_data_ex 接口获取数据
data_dict = ContextInfo.get_market_data_ex(
['close'], # 获取收盘价
[stock_code], # 股票代码列表
period=ContextInfo.period, # 跟随主图周期
count=count, # 获取数量
dividend_type='front', # 前复权
fill_data=True # 停牌填充
)
# 检查数据是否获取成功
if stock_code not in data_dict:
return
df = data_dict[stock_code]
# 数据长度不足以计算 ROC 时直接返回
if len(df) < ContextInfo.N + 2:
return
# 获取收盘价序列 (转换为 numpy 数组提高计算效率)
closes = df['close'].values
# --- 计算 ROC 指标 ---
# 公式: ROC = (当前价格 - N天前价格) / N天前价格 * 100
# 1. 计算当前的 ROC (对应 closes[-1])
curr_price = closes[-1]
curr_ref_price = closes[-1 - ContextInfo.N] # N天前的价格
roc_curr = (curr_price - curr_ref_price) / curr_ref_price * 100
# 2. 计算上一根K线的 ROC (对应 closes[-2]),用于判断交叉
prev_price = closes[-2]
prev_ref_price = closes[-2 - ContextInfo.N] # (N+1)天前的价格
roc_prev = (prev_price - prev_ref_price) / prev_ref_price * 100
# --- 交易逻辑 ---
# 获取当前持仓
# 注意:回测模式下可用 ContextInfo.get_position() 或类似逻辑,
# 这里使用 order_target_percent 简化仓位管理,它会自动判断买卖方向
# 信号 1: ROC 上穿 0 轴 (金叉) -> 买入
if roc_prev < 0 and roc_curr > 0:
print(f"[{stock_code}] ROC上穿0轴 (前值:{roc_prev:.2f}, 现值:{roc_curr:.2f}) -> 触发买入")
# 调整仓位到 100% (全仓买入)
order_target_percent(stock_code, 1.0, ContextInfo, ContextInfo.account_id)
# 信号 2: ROC 下穿 0 轴 (死叉) -> 卖出
elif roc_prev > 0 and roc_curr < 0:
print(f"[{stock_code}] ROC下穿0轴 (前值:{roc_prev:.2f}, 现值:{roc_curr:.2f}) -> 触发卖出")
# 调整仓位到 0% (清仓卖出)
order_target_percent(stock_code, 0.0, ContextInfo, ContextInfo.account_id)
# (可选) 在图表上画出 ROC 值,方便调试
# ContextInfo.paint('ROC', roc_curr, -1, 0)
代码说明
- 编码格式:首行
# -*- coding: gbk -*-是 QMT Python 策略必须的,防止中文乱码。 - 数据获取:使用了
ContextInfo.get_market_data_ex,这是 QMT 推荐的高效数据接口。我们获取了N + 10条数据,确保有足够的历史数据来计算当前的 ROC 和前一时刻的 ROC。 - ROC 计算:
roc_curr:当前 K 线的 ROC 值。roc_prev:前一根 K 线的 ROC 值。- 通过比较这两个值与 0 的关系,判断是否发生了“上穿”或“下穿”。
- 交易函数:使用了
order_target_percent。这是一个非常方便的高级函数:- 当目标比例为
1.0时,系统会自动计算可用资金,买入直到满仓。 - 当目标比例为
0.0时,系统会自动卖出该股票的所有持仓。
- 当目标比例为
- 账号设置:在
init函数中,请务必将ContextInfo.account_id修改为您实际的资金账号。
如何运行
- 打开 QMT 客户端,进入“模型研究”或“策略编辑器”。
- 新建一个 Python 策略,将上述代码复制进去。
- 修改
init函数中的ContextInfo.account_id为您的真实或模拟账号。 - 在右侧选择回测区间、基准和初始资金进行回测,或者在行情图上加载该策略进行实时运行。
Q&A
Q: 如何修改 ROC 的周期参数?
A: 在代码的 init 函数中,修改 ContextInfo.N = 12 这一行,将 12 改为您想要的数字(例如 20 或 60)。
Q: 为什么使用 order_target_percent 而不是 passorder?
A: order_target_percent 是封装好的高级函数,它自动处理了“当前持有多少股”、“需要买卖多少股才能达到目标仓位”的计算逻辑,非常适合这种趋势跟踪策略。如果您需要更精细的控制(如指定价格、指定手数),可以使用 passorder。
Q: 这个策略可以用于期货吗?
A: 逻辑上可以,但需要修改下单函数。order_target_percent 主要用于股票。如果是期货,需要使用 buy_open (开多), sell_close (平多), sell_open (开空), buy_close (平空) 等函数,并且需要处理保证金和合约乘数。