问题描述
解决方案
这是一个基于 QMT 平台的 日内均值回归策略。
策略逻辑
- 核心思想:价格总是围绕移动平均线波动。当价格偏离均线过大时,倾向于回归均值。
- 买入条件:当收盘价 低于 移动平均线一定幅度(例如 1%)时,认为超卖,执行买入。
- 卖出条件:当收盘价 高于 移动平均线一定幅度(例如 1%)时,认为超买,执行卖出(平仓)。
- 周期:策略运行在分钟线(如 1分钟、3分钟或 5分钟)上,以捕捉日内波动。
策略代码
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
"""
初始化函数,设定策略参数和账户
"""
# 1. 设置策略参数
ContextInfo.stock_code = '600000.SH' # 标的股票:浦发银行
ContextInfo.period = 20 # 均线周期:20个bar
ContextInfo.threshold = 0.01 # 偏离幅度阈值:1% (0.01)
ContextInfo.trade_amount = 1000 # 每次交易数量(股)
ContextInfo.account_id = '6000000000' # 请替换为您的资金账号
ContextInfo.account_type = 'STOCK' # 账号类型:STOCK-股票,FUTURE-期货
# 2. 设置股票池和周期
ContextInfo.set_universe([ContextInfo.stock_code])
ContextInfo.set_account(ContextInfo.account_id)
# 3. 辅助变量
print("策略初始化完成:日内均值回归策略")
def get_position(ContextInfo, stock_code):
"""
辅助函数:获取当前持仓数量
"""
# 获取持仓对象列表
positions = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
return pos.m_nVolume
return 0
def handlebar(ContextInfo):
"""
K线处理函数,每根K线执行一次
"""
# 获取当前正在处理的K线索引
index = ContextInfo.barpos
stock = ContextInfo.stock_code
# 1. 获取历史行情数据
# 获取过去 period + 5 根K线的数据,确保计算均线有足够数据
# period='1m' 代表取1分钟线,可根据实际运行周期调整
data_dict = ContextInfo.get_market_data_ex(
['close'],
[stock],
period='follow', # 跟随主图周期
count=ContextInfo.period + 5,
dividend_type='front' # 前复权
)
if stock not in data_dict:
return
df = data_dict[stock]
# 数据不足时不计算
if len(df) < ContextInfo.period:
return
# 2. 计算技术指标
# 计算移动平均线 (MA)
ma = df['close'].rolling(window=ContextInfo.period).mean()
# 获取最新一根K线的收盘价和均线值
current_price = df['close'].iloc[-1]
current_ma = ma.iloc[-1]
# 确保均线有效
if np.isnan(current_ma):
return
# 计算上下轨
upper_band = current_ma * (1 + ContextInfo.threshold) # 上轨
lower_band = current_ma * (1 - ContextInfo.threshold) # 下轨
# 3. 获取当前持仓
current_pos = get_position(ContextInfo, stock)
# 4. 交易逻辑
# 信号 1: 价格低于下轨 -> 买入
if current_price < lower_band:
if current_pos == 0: # 如果当前空仓
print(f"时间: {df.index[-1]}, 价格: {current_price:.2f} 低于下轨 {lower_band:.2f}, 触发买入")
# 下单:买入
# opType=23(买入), orderType=1101(单股单账号普通下单)
passorder(23, 1101, ContextInfo.account_id, stock, 5, -1, ContextInfo.trade_amount, ContextInfo)
# 信号 2: 价格高于上轨 -> 卖出
elif current_price > upper_band:
if current_pos > 0: # 如果当前有持仓
# 检查可用持仓(如果是T+1市场,需要额外判断可用持仓,此处简化为有持仓即卖出)
print(f"时间: {df.index[-1]}, 价格: {current_price:.2f} 高于上轨 {upper_band:.2f}, 触发卖出")
# 下单:卖出
# opType=24(卖出)
passorder(24, 1101, ContextInfo.account_id, stock, 5, -1, current_pos, ContextInfo)
# (可选) 绘制指标到图表,方便回测观察
ContextInfo.paint('MA', current_ma, -1, 0, 'yellow')
ContextInfo.paint('Upper', upper_band, -1, 0, 'red')
ContextInfo.paint('Lower', lower_band, -1, 0, 'green')
代码关键点解析
-
数据获取 (
get_market_data_ex):- 使用了
period='follow',这意味着策略会根据你在 QMT 界面上选择的周期(如 1分钟、5分钟)来自动获取对应的数据。 - 获取了
close收盘价,并使用 Pandas 的rolling().mean()计算移动平均线。
- 使用了
-
交易信号:
- 下轨 (Lower Band) =
MA * (1 - 0.01)。当价格跌破下轨,意味着价格短期超跌,预期会反弹,因此买入。 - 上轨 (Upper Band) =
MA * (1 + 0.01)。当价格突破上轨,意味着价格短期超涨,预期会回调,因此卖出。
- 下轨 (Lower Band) =
-
下单函数 (
passorder):opType=23代表股票买入。opType=24代表股票卖出。prType=5代表使用最新价下单(price参数填-1即可)。- 注意:在实盘中,请务必将
ContextInfo.account_id替换为您真实的资金账号。
-
持仓查询 (
get_position):- 通过
get_trade_detail_data获取当前账户的持仓列表,遍历查找目标股票的持仓量,防止重复买入或卖空(A股不支持直接做空)。
- 通过
如何运行
- 打开 QMT 客户端,进入【模型研究】或【策略交易】。
- 新建一个 Python 策略,将上述代码粘贴进去。
- 修改
ContextInfo.account_id为你的实际账号。 - 在右侧设置中,选择运行周期(例如 1分钟 或 5分钟)。
- 点击【运行】或【回测】。
Q&A
Q: 如何修改均线的长度?
A: 修改 init 函数中的 ContextInfo.period = 20,将 20 改为你想要的数字(如 60)。
Q: 如何调整买卖的敏感度?
A: 修改 init 函数中的 ContextInfo.threshold = 0.01。
- 调大(如 0.02):交易次数变少,只有价格波动很大时才交易。
- 调小(如 0.005):交易次数变多,捕捉微小的波动。
Q: 这个策略可以用于期货吗?
A: 可以,但需要修改以下几点:
ContextInfo.account_type改为'FUTURE'。passorder的opType需要修改:期货开多是0,平多是6(优先平今)或7(优先平昨);开空是3,平空是8或9。- 逻辑上需要增加做空逻辑(高于上轨开空,回归均值平空)。