问题描述
请帮我实现海龟交易法则,即价格突破N日高点时买入,跌破M日低点时卖出。
解决方案
这是一个基于 QMT (Quant Maker Trading) 平台的 Python 策略代码,实现了经典的海龟交易法则核心逻辑:唐奇安通道(Donchian Channel)突破策略。
策略逻辑
- 入场(买入):当当前价格突破过去 N 日的最高价时,做多买入。
- 出场(卖出):当当前价格跌破过去 M 日的最低价时,平仓卖出。
- 参数设置:默认 N=20(20日突破),M=10(10日止损)。
QMT 策略代码
# -*- coding: gbk -*-
import pandas as pd
def init(ContextInfo):
"""
初始化函数,设定策略参数和账号
"""
# 策略参数设置
ContextInfo.N = 20 # 买入突破周期 (如:20日高点)
ContextInfo.M = 10 # 卖出止损周期 (如:10日低点)
ContextInfo.trade_vol = 1000 # 每次交易的股数
# 设置账号 (请替换为您自己的资金账号)
# 格式通常为 '账号ID'
ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
ContextInfo.set_account(ContextInfo.account_id)
print("策略初始化完成:海龟法则 (N={}, M={})".format(ContextInfo.N, ContextInfo.M))
def handlebar(ContextInfo):
"""
K线周期回调函数,处理核心交易逻辑
"""
# 获取当前主图的股票代码和市场
stock_code = ContextInfo.stockcode + '.' + ContextInfo.market
# 获取当前K线索引
index = ContextInfo.barpos
# 确保有足够的历史数据进行计算 (需要 N+1 根K线,因为要计算前N天的极值)
if index < ContextInfo.N + 1:
return
# 获取历史行情数据
# count = N + 2 是为了确保能取到前 N 天的数据,并包含当前 bar
# dividend_type='front' 使用前复权数据,防止分红造成假突破
data = ContextInfo.get_market_data(
['high', 'low', 'close'],
stock_code=[stock_code],
count=ContextInfo.N + 2,
period=ContextInfo.period,
dividend_type='front'
)
# 如果数据获取失败或长度不足,直接返回
if data is None or len(data) == 0:
return
# 将数据转换为 Series 方便计算
# 注意:get_market_data 返回的是 DataFrame,列是股票代码,我们需要转置或直接取列
# 这里假设是单只股票运行,直接取该股票的数据
try:
high_series = data['high']
low_series = data['low']
close_series = data['close']
except:
return
# --- 核心指标计算 ---
# 获取当前最新价格
current_price = close_series.iloc[-1]
# 计算唐奇安通道上轨:过去 N 天的最高价(不包含当前最新K线)
# 使用 iloc[-(N+1):-1] 取倒数第 N+1 个到倒数第 2 个数据
recent_highs = high_series.iloc[-(ContextInfo.N + 1):-1]
donchian_upper = recent_highs.max()
# 计算唐奇安通道下轨:过去 M 天的最低价(不包含当前最新K线)
recent_lows = low_series.iloc[-(ContextInfo.M + 1):-1]
donchian_lower = recent_lows.min()
# --- 仓位查询 ---
current_pos = get_holding_position(stock_code, ContextInfo)
# --- 交易信号判断 ---
# 1. 买入信号:当前价格突破过去 N 日最高价,且当前无持仓
if current_price > donchian_upper and current_pos == 0:
print(f"触发买入信号: {stock_code}, 现价: {current_price}, N日高点: {donchian_upper}")
# 下单买入
order_shares(stock_code, ContextInfo.trade_vol, 'fix', current_price, ContextInfo, ContextInfo.account_id)
# 2. 卖出信号:当前价格跌破过去 M 日最低价,且当前有持仓
elif current_price < donchian_lower and current_pos > 0:
print(f"触发卖出信号: {stock_code}, 现价: {current_price}, M日低点: {donchian_lower}")
# 下单卖出 (卖出所有持仓)
order_shares(stock_code, -current_pos, 'fix', current_price, ContextInfo, ContextInfo.account_id)
def get_holding_position(stock_code, ContextInfo):
"""
辅助函数:查询指定股票的当前持仓数量
"""
position_list = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
for pos in position_list:
# QMT返回的证券代码可能不带后缀,或者带后缀,需要做兼容处理
# 这里的逻辑是简单的字符串包含匹配,实际使用建议严格匹配
if pos.m_strInstrumentID == stock_code or pos.m_strInstrumentID in stock_code:
return pos.m_nVolume
return 0
代码使用说明
- 编码格式:代码第一行
# -*- coding: gbk -*-是必须的,因为 QMT 的 Python 编辑器默认使用 GBK 编码,否则中文注释会乱码或报错。 - 账号设置:请务必将
init函数中的ContextInfo.account_id = 'YOUR_ACCOUNT_ID'替换为您实际的资金账号(回测模式下通常不需要修改,实盘模式必须修改)。 - 运行周期:
- 该策略依赖
ContextInfo.period,请在运行策略时选择 日线 (1d) 周期,这是海龟法则的标准周期。
- 该策略依赖
- 数据获取:
- 代码中使用了
dividend_type='front'(前复权)。在突破策略中,复权非常重要,否则分红除权导致的股价“下跌”会触发错误的止损信号。
- 代码中使用了
- 指标计算细节:
- 计算 N 日高点时,必须排除当前 K 线。如果包含当前 K 线,那么当前价格永远不可能大于 N 日高点(最多相等),导致永远无法触发买入信号。代码中使用了切片
iloc[-(N+1):-1]来确保只取历史数据。
- 计算 N 日高点时,必须排除当前 K 线。如果包含当前 K 线,那么当前价格永远不可能大于 N 日高点(最多相等),导致永远无法触发买入信号。代码中使用了切片
如何在 QMT 中运行
- 打开 QMT 客户端,进入【模型研究】或【策略编辑器】。
- 新建一个 Python 策略。
- 将上述代码复制粘贴进去。
- 点击【编译】检查是否有语法错误。
- 在右侧选择一只股票(例如
600000.SH)和周期(日线)。 - 点击【运行】或【回测】查看结果。