3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个经典的 Dual Thrust 策略实现。Dual Thrust 是一种著名的趋势跟踪策略,它通过计算前 N 天的最高价、最低价和收盘价来确定一个波动区间(Range),然后结合当天的开盘价来确定上下轨。
计算 Range (波动区间):
利用前 N 日的历史数据计算:
确定上下轨:
交易信号:
以下代码适用于 期货 交易(因为包含做空逻辑)。如果您用于股票,需要去掉做空部分,仅保留买入和卖出逻辑。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
# --- 策略参数设置 ---
ContextInfo.N = 5 # 回溯天数,用于计算Range
ContextInfo.k1 = 0.5 # 上轨系数
ContextInfo.k2 = 0.5 # 下轨系数
ContextInfo.symbol = 'IF00.IF' # 交易标的,这里以股指期货主力合约为例
ContextInfo.account_id = 'YOUR_ACCOUNT_ID' # 请填写您的资金账号
# 设置股票池
ContextInfo.set_universe([ContextInfo.symbol])
# 设置交易账号
ContextInfo.set_account(ContextInfo.account_id)
# 初始化全局变量
ContextInfo.pos = 0 # 简单的持仓状态记录:0空仓,1多头,-1空头
def handlebar(ContextInfo):
# 获取当前K线位置,如果是回测模式,避免数据引用未来
index = ContextInfo.barpos
realtime = ContextInfo.get_bar_timetag(index)
# 1. 获取历史行情数据 (取过去 N+1 天,因为要包含昨天及之前的N天)
# 注意:我们需要的是"昨天"及之前的N天数据来计算Range,不包含"今天"
# period='1d' 获取日线
data = ContextInfo.get_market_data(
['high', 'low', 'close', 'open'],
stock_code=[ContextInfo.symbol],
period='1d',
count=ContextInfo.N + 1,
dividend_type='none'
)
# 检查数据是否足够
if ContextInfo.symbol not in data or len(data[ContextInfo.symbol]) < ContextInfo.N + 1:
return
df = data[ContextInfo.symbol]
# 2. 数据处理
# 截取前N天的数据(排除当前最新的这一根K线,因为最新的K线是今天正在走的)
# 假设df的最后一行是当前日期(实盘中通常是这样),我们需要取倒数第2行往前推N行
history_df = df.iloc[-(ContextInfo.N + 1):-1]
# 获取今天的开盘价 (df的最后一行是今天)
current_open = df.iloc[-1]['open']
# 获取当前最新价格
# 在实盘中,建议使用 get_full_tick 获取更实时的tick数据
current_price = 0
tick = ContextInfo.get_full_tick([ContextInfo.symbol])
if tick and ContextInfo.symbol in tick:
current_price = tick[ContextInfo.symbol]['lastPrice']
else:
# 如果获取不到tick,降级使用K线收盘价(回测时使用)
current_price = df.iloc[-1]['close']
# 3. 计算 Dual Thrust 的 Range
hh = history_df['high'].max() # N日High的最高价
hc = history_df['close'].max() # N日Close的最高价
lc = history_df['close'].min() # N日Close的最低价
ll = history_df['low'].min() # N日Low的最低价
# Range = Max(HH-LC, HC-LL)
r_range = max(hh - lc, hc - ll)
# 4. 计算上下轨
upper_bound = current_open + ContextInfo.k1 * r_range
lower_bound = current_open - ContextInfo.k2 * r_range
# 打印调试信息 (可选)
# print(f"Time: {realtime}, Price: {current_price}, Open: {current_open}, Range: {r_range}, Upper: {upper_bound}, Lower: {lower_bound}")
# 5. 交易逻辑
# 突破上轨,做多
if current_price > upper_bound:
if ContextInfo.pos != 1:
# 如果持有空单,先平空
if ContextInfo.pos == -1:
print(f"价格突破上轨 {upper_bound},平空")
buy_close_tdayfirst(ContextInfo.symbol, 1, 'LATEST', current_price, ContextInfo, ContextInfo.account_id)
# 开多
print(f"价格突破上轨 {upper_bound},开多")
buy_open(ContextInfo.symbol, 1, 'LATEST', current_price, ContextInfo, ContextInfo.account_id)
ContextInfo.pos = 1
# 跌破下轨,做空
elif current_price < lower_bound:
if ContextInfo.pos != -1:
# 如果持有多单,先平多
if ContextInfo.pos == 1:
print(f"价格跌破下轨 {lower_bound},平多")
sell_close_tdayfirst(ContextInfo.symbol, 1, 'LATEST', current_price, ContextInfo, ContextInfo.account_id)
# 开空
print(f"价格跌破下轨 {lower_bound},开空")
sell_open(ContextInfo.symbol, 1, 'LATEST', current_price, ContextInfo, ContextInfo.account_id)
ContextInfo.pos = -1
# 辅助函数:简单的下单封装 (QMT自带了 buy_open 等函数,这里直接调用即可)
# 注意:实盘中建议配合 get_trade_detail_data 来判断真实持仓,而不是仅依赖 ContextInfo.pos 变量
数据获取 (get_market_data):
count = N + 1 根K线。history_df 取的是 iloc[-(N+1):-1](过去N天),而 current_open 取的是 iloc[-1](今天)。实时价格 (get_full_tick):
handlebar 中,如果是实盘运行,get_market_data 获取的最新收盘价可能滞后(取决于刷新频率),使用 get_full_tick 获取最新的 Tick 数据(lastPrice)能更灵敏地触发突破信号。交易函数:
buy_open: 买入开仓(做多)sell_close_tdayfirst: 卖出平仓(平多,优先平今)sell_open: 卖出开仓(做空)buy_close_tdayfirst: 买入平仓(平空,优先平今)'LATEST' (最新价),您也可以改为 'COMPETE' (对手价) 以提高成交率。持仓管理 (ContextInfo.pos):
pos 来记录策略状态。get_trade_detail_data(ContextInfo.account_id, 'future', 'position') 来查询真实的账户持仓,以防止策略重启后 pos 变量重置导致重复下单。ContextInfo.account_id 为您的真实资金账号。ContextInfo.symbol 为您想交易的合约(如 IF2312.IF)。