问题描述
解决方案
这是一个基于 斐波那契回调(Fibonacci Retracement) 的交易策略。
策略逻辑说明
- 趋势识别:策略使用一个滚动窗口(例如过去 60 根 K 线)来寻找最高价(High)和最低价(Low)。
- 如果 最高价出现的时间晚于最低价,判定为 上升趋势(低点 -> 高点),我们关注价格回调时的支撑位。
- 如果 最低价出现的时间晚于最高价,判定为 下降趋势(高点 -> 低点),我们关注价格反弹时的压力位。
- 斐波那契点位:计算关键比例位置:0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0。
- 重点关注 0.618 (黄金分割位) 和 0.5 作为进场信号。
- 绘图功能:使用
ContextInfo.paint()函数在主图上实时画出这些关键支撑/压力线,方便可视化观察。 - 交易信号:
- 做多:在上升趋势中,当价格回调触及 0.618 支撑位时买入。
- 平仓:当价格跌破 0.786(止损)或 反弹回 0.236(止盈)时平仓。
QMT 策略代码
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
# --- 策略参数设置 ---
ContextInfo.lookback = 100 # 回溯周期,用于寻找高低点
ContextInfo.account_id = '6000000000' # 请替换为您的资金账号
ContextInfo.account_type = 'STOCK' # 账号类型:'STOCK'股票, 'FUTURE'期货
# 斐波那契数列比例
ContextInfo.fib_ratios = [0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0]
# 设置股票池(示例)
ContextInfo.set_universe(['600000.SH'])
# 绑定账号(实盘必须)
ContextInfo.set_account(ContextInfo.account_id)
def handlebar(ContextInfo):
# 获取当前K线位置
index = ContextInfo.barpos
# 确保有足够的数据进行回溯计算
if index < ContextInfo.lookback:
return
# 获取当前主图品种代码
stock_code = ContextInfo.stockcode + '.' + ContextInfo.market
# 获取历史行情数据 (High, Low, Close)
# 注意:count=ContextInfo.lookback + 1 确保取到当前K线
data = ContextInfo.get_market_data_ex(
['high', 'low', 'close'],
[stock_code],
period=ContextInfo.period,
count=ContextInfo.lookback,
dividend_type='follow'
)
if stock_code not in data:
return
df = data[stock_code]
if df.empty:
return
# 计算窗口内的最高价和最低价及其位置
# argmax/argmin 返回的是相对位置,需要结合索引判断先后
high_prices = df['high'].values
low_prices = df['low'].values
close_prices = df['close'].values
max_price = np.max(high_prices)
min_price = np.min(low_prices)
max_idx = np.argmax(high_prices) # 最高价在窗口内的索引
min_idx = np.argmin(low_prices) # 最低价在窗口内的索引
current_price = close_prices[-1]
# --- 斐波那契计算逻辑 ---
# 定义趋势方向:
# 如果 最高价索引 > 最低价索引,说明是先低后高,为上升趋势(寻找回调支撑)
# 如果 最高价索引 < 最低价索引,说明是先高后低,为下降趋势(寻找反弹压力)
is_uptrend = max_idx > min_idx
price_range = max_price - min_price
fib_levels = {}
if is_uptrend:
# 上升趋势:0 是最高点,1 是最低点,回调是向下走
# Level = Max - Range * Ratio
for ratio in ContextInfo.fib_ratios:
level_price = max_price - (price_range * ratio)
fib_levels[ratio] = level_price
# 绘图:在主图绘制线条
# 0.618 和 0.5 重点显示为黄色和洋红色,其他为白色
color = 'white'
if ratio == 0.618: color = 'yellow'
if ratio == 0.5: color = 'magenta'
# paint(指标名, 数值, 位置(-1为主图), 线型(0为曲线), 颜色)
ContextInfo.paint(f'Fib_{ratio}', level_price, -1, 0, color)
else:
# 下降趋势:0 是最低点,1 是最高点,反弹是向上走
# Level = Min + Range * Ratio
for ratio in ContextInfo.fib_ratios:
level_price = min_price + (price_range * ratio)
fib_levels[ratio] = level_price
color = 'white'
if ratio == 0.618: color = 'yellow'
if ratio == 0.5: color = 'magenta'
ContextInfo.paint(f'Fib_{ratio}', level_price, -1, 0, color)
# --- 简单的交易逻辑示例 (仅针对上升趋势的回调做多) ---
# 实际交易请务必增加风控和仓位管理
if not ContextInfo.is_last_bar():
# 回测或历史K线只计算不交易,或者使用 passorder 的 quickTrade=0
# 这里为了演示逻辑,仅在最后一根K线判断
pass
# 获取当前持仓
positions = ContextInfo.get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
long_pos = 0
for pos in positions:
if pos.m_strInstrumentID == ContextInfo.stockcode:
long_pos = pos.m_nVolume
# 交易信号:上升趋势中,价格回调触及 0.618 支撑位
if is_uptrend:
buy_level = fib_levels[0.618]
stop_loss = fib_levels[0.786]
take_profit = fib_levels[0.236]
# 信号:当前价格跌破 0.618 但仍在 0.786 之上 (视为回调到位)
# 且当前没有持仓
if long_pos == 0 and current_price <= buy_level * 1.005 and current_price >= stop_loss:
print(f"触发买入信号: 价格 {current_price} 接近 Fib 0.618 {buy_level}")
# 全仓买入示例 (请根据实际情况调整 volume)
passorder(23, 1101, ContextInfo.account_id, stock_code, 5, -1, 100, ContextInfo)
# 止损/止盈逻辑
elif long_pos > 0:
if current_price < stop_loss:
print(f"触发止损: 价格 {current_price} 跌破 Fib 0.786 {stop_loss}")
passorder(24, 1101, ContextInfo.account_id, stock_code, 5, -1, long_pos, ContextInfo)
elif current_price > take_profit:
print(f"触发止盈: 价格 {current_price} 超过 Fib 0.236 {take_profit}")
passorder(24, 1101, ContextInfo.account_id, stock_code, 5, -1, long_pos, ContextInfo)
代码关键点解析
-
ContextInfo.paint:- 这是 QMT 特有的绘图函数。
f'Fib_{ratio}':给每条线起一个唯一的名字(如 Fib_0.618),这样在图表指标栏可以看到数值。-1:表示画在主图上(和 K 线叠加)。color:我将 0.618 设为黄色,0.5 设为洋红色,以便视觉上突出重点支撑位。
-
滚动窗口 (
lookback):- 斐波那契回调依赖于“高点”和“低点”的定义。代码中使用了
ContextInfo.lookback = 100,意味着它会动态计算过去 100 根 K 线内的最高和最低点。 - 注意:随着 K 线推进,高低点可能会发生变化,导致斐波那契线随之移动。这是动态策略的特性。
- 斐波那契回调依赖于“高点”和“低点”的定义。代码中使用了
-
趋势判断 (
is_uptrend):- 通过比较最高价索引 (
max_idx) 和最低价索引 (min_idx) 的大小来判断当前是处于上升波段后的回调,还是下降波段后的反弹。
- 通过比较最高价索引 (
-
交易执行 (
passorder):- 代码中包含了一个简单的做多逻辑:在上升趋势中,当价格回调到 0.618 附近(
buy_level * 1.005范围内)且未跌破 0.786 时买入。 - 止损设在 0.786,止盈设在 0.236。
- 代码中包含了一个简单的做多逻辑:在上升趋势中,当价格回调到 0.618 附近(
如何使用
- 打开 QMT 客户端,进入 模型研究/策略编辑器。
- 新建一个 Python 策略。
- 将上述代码复制粘贴进去。
- 修改
ContextInfo.account_id为你的实际资金账号。 - 点击 运行 或在图表上 叠加指标。你会看到主图上出现了多条斐波那契水平线,随着行情波动而变化。
常见问题 (Q&A)
Q: 为什么画出来的线是变动的?
A: 因为代码使用的是滚动窗口(过去100根K线)。当新的K线产生,旧的K线移出窗口,或者产生了新的最高/最低点时,计算基准就会改变。如果你需要固定某个日期的最高低点,需要修改代码指定固定的 start_time 和 end_time 来获取数据。
Q: 如何只画线不交易?
A: 你可以注释掉 passorder 相关的代码块,或者直接删除 if long_pos == 0 ... 之后的交易逻辑部分,这样策略就变成了一个纯粹的技术分析指标工具。
Q: get_market_data_ex 获取不到数据怎么办?
A: 请确保在 QMT 的“数据管理”中下载了对应品种和周期的历史数据。如果没有下载数据,API 返回的 DataFrame 可能是空的。