问题描述
解决方案
梅斯线 (Mass Index) 趋势反转策略
梅斯线(Mass Index)是由 Donald Dorsey 开发的一种波动率指标,用于通过测量高低价范围的扩大和收缩来识别趋势反转。
该策略的核心逻辑是寻找**“反转凸起”(Reversal Bulge)**:
- 计算原理:基于最高价与最低价之差(波动幅度)的指数移动平均。
- 反转信号:当梅斯线向上突破 27(表示波动率扩大),随后又跌破 26.5(表示波动率收缩)时,即形成“凸起”,预示趋势可能反转。
- 方向确认:梅斯线本身不指示方向。通常结合 9 日均线(EMA)来判断方向:
- 买入:出现“凸起”信号,且价格收盘于 9 日 EMA 之上(看涨反转)。
- 卖出:出现“凸起”信号,且价格收盘于 9 日 EMA 之下(看跌反转)。
以下是基于 QMT 平台的完整 Python 策略代码。
策略代码
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
"""
策略初始化函数
"""
# 设置资金账号 (请替换为您自己的资金账号)
ContextInfo.accID = '6000000000'
ContextInfo.set_account(ContextInfo.accID)
# 策略参数设置
ContextInfo.ema_period = 9 # 计算波动率EMA的周期
ContextInfo.sum_period = 25 # 梅斯线求和周期
ContextInfo.trend_ema_period = 9 # 确认趋势方向的价格EMA周期
# 阈值设置 (Donald Dorsey 推荐值)
ContextInfo.upper_threshold = 27.0 # 凸起上限
ContextInfo.lower_threshold = 26.5 # 凸起下限
# 交易数量
ContextInfo.trade_vol = 100
# 设定股票池 (示例:平安银行)
ContextInfo.target = '000001.SZ'
ContextInfo.set_universe([ContextInfo.target])
def get_mass_index(high, low, ema_p, sum_p):
"""
计算梅斯线指标
Formula: Sum(EMA(H-L, 9) / EMA(EMA(H-L, 9), 9), 25)
"""
# 1. 计算高低价差
hl_range = high - low
# 2. 计算单重指数移动平均 (SEMA)
sema = hl_range.ewm(span=ema_p, adjust=False).mean()
# 3. 计算双重指数移动平均 (DEMA)
dema = sema.ewm(span=ema_p, adjust=False).mean()
# 4. 计算比率
# 注意:避免除以0
ratio = sema / dema.replace(0, np.nan)
# 5. 计算梅斯线 (滚动求和)
mass_index = ratio.rolling(window=sum_p).sum()
return mass_index
def handlebar(ContextInfo):
"""
K线周期运行函数
"""
# 获取当前主图代码
stock_code = ContextInfo.target
# 避免在历史K线未走完时重复计算,提高回测效率
# 如果是实时行情,is_last_bar() 为 True
if not ContextInfo.is_last_bar():
return
# 获取历史行情数据
# 需要足够的长度来计算 EMA 和 Rolling Sum
# 长度需求 approx: sum_period + ema_period * 3 (为了EMA收敛)
count = 100
data = ContextInfo.get_market_data_ex(
['high', 'low', 'close'],
[stock_code],
period=ContextInfo.period,
count=count,
dividend_type='front'
)
if stock_code not in data or len(data[stock_code]) < count:
print(f"数据不足: {stock_code}")
return
df = data[stock_code]
# --- 指标计算 ---
# 1. 计算梅斯线 (Mass Index)
df['mass_index'] = get_mass_index(
df['high'],
df['low'],
ContextInfo.ema_period,
ContextInfo.sum_period
)
# 2. 计算趋势确认均线 (9-period EMA of Close)
df['trend_ema'] = df['close'].ewm(span=ContextInfo.trend_ema_period, adjust=False).mean()
# 获取最新和前一根K线的数据
current_mass = df['mass_index'].iloc[-1]
prev_mass = df['mass_index'].iloc[-2]
current_close = df['close'].iloc[-1]
current_trend_ema = df['trend_ema'].iloc[-1]
# --- 绘图 (可选,用于界面观察) ---
ContextInfo.paint('Mass_Index', current_mass, -1, 0)
ContextInfo.paint('Threshold_Up', ContextInfo.upper_threshold, -1, 0, 'red')
ContextInfo.paint('Threshold_Down', ContextInfo.lower_threshold, -1, 0, 'green')
# --- 交易逻辑 ---
# 判定“反转凸起” (Reversal Bulge)
# 逻辑:前一根K线梅斯线大于27,且当前K线梅斯线跌破26.5
# 注意:实际交易中,这个形态可能跨越几根K线,这里简化为两根K线的穿越判断
is_bulge_pattern = (prev_mass >= ContextInfo.upper_threshold) and \
(current_mass < ContextInfo.lower_threshold)
# 获取当前持仓
positions = ContextInfo.get_trade_detail_data(ContextInfo.accID, 'stock', 'position')
current_pos = 0
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
current_pos = pos.m_nVolume
break
# 生成信号
if is_bulge_pattern:
print(f"检测到梅斯线凸起形态: 前值={prev_mass:.2f}, 现值={current_mass:.2f}")
# 1. 买入信号:凸起 + 收盘价在均线之上
if current_close > current_trend_ema:
if current_pos == 0:
print(f"趋势反转向上,触发买入: {stock_code}, 价格: {current_close}")
passorder(23, 1101, ContextInfo.accID, stock_code, 5, -1, ContextInfo.trade_vol, ContextInfo)
# 2. 卖出信号:凸起 + 收盘价在均线之下
elif current_close < current_trend_ema:
if current_pos > 0:
print(f"趋势反转向下,触发卖出: {stock_code}, 价格: {current_close}")
passorder(24, 1101, ContextInfo.accID, stock_code, 5, -1, current_pos, ContextInfo)
代码详解
-
数据获取 (
get_market_data_ex):- 我们获取了
high,low,close数据。 count=100是为了确保有足够的数据来计算 EMA 和 25 周期的滚动求和。如果数据太短,指标初期值为 NaN。
- 我们获取了
-
梅斯线计算 (
get_mass_index):- SEMA: 计算 (High - Low) 的 9 日指数移动平均。
- DEMA: 对 SEMA 再次计算 9 日指数移动平均。
- Ratio: SEMA / DEMA。
- Mass Index: 对 Ratio 进行 25 周期的滚动求和 (
rolling(window=25).sum())。
-
趋势确认:
- 代码中计算了收盘价的 9 日 EMA (
trend_ema)。这是梅斯线策略中至关重要的一步,因为梅斯线只告诉你“要变盘了”,但不告诉你“往哪变”。
- 代码中计算了收盘价的 9 日 EMA (
-
交易信号逻辑:
- 凸起判定 (
is_bulge_pattern): 严格遵循定义,检测指标是否从上方穿越 27 后跌破 26.5。prev_mass >= 27.0current_mass < 26.5
- 买入: 满足凸起条件 且
Close > EMA。 - 卖出: 满足凸起条件 且
Close < EMA。
- 凸起判定 (
-
绘图 (
paint):- 代码包含了
ContextInfo.paint函数,运行策略时,你可以在 QMT 的副图指标栏看到梅斯线的走势以及红绿两条阈值线,方便验证策略逻辑。
- 代码包含了
注意事项
- 频率选择: 梅斯线通常用于日线级别,但在分钟级别(如 60分钟)也可能有效。请在
ContextInfo.period中确认周期。 - 参数调整: 27 和 26.5 是经典参数,针对不同的市场或个股,可能需要调整这些阈值以过滤噪音或捕捉更多信号。
- 止损: 该策略属于反转策略,风险较高,建议在实盘中加入止损逻辑(例如价格跌破最近低点)。
Q&A
Q: 为什么梅斯线需要计算两次 EMA?
A: 计算两次 EMA (即 DEMA 的分母部分) 是为了平滑数据,消除随机波动,从而更准确地反映波动率的扩缩趋势。
Q: 这个策略适合震荡市还是趋势市?
A: 梅斯线主要用于捕捉趋势反转。它最适合在一段明显的趋势末端使用。如果在窄幅震荡市中,波动率变化不明显,可能很难触发 27 的阈值。
Q: 如何在回测中查看效果?
A: 将代码复制到 QMT 的策略编辑器中,点击“回测”,设置好时间段和标的。观察副图中的指标线是否在价格反转点附近出现了“凸起”形态。