问题描述
解决方案
这是一个基于 Elder-Ray Index (艾达透视指标) 的量化策略设计与代码实现。
1. 策略原理 (Elder-Ray Index)
Elder-Ray 指标由 Alexander Elder 博士开发,用于衡量市场中多头(买方)和空头(卖方)的力量。它由三个部分组成:
- EMA (指数移动平均线):通常使用 13 日 EMA,代表市场的价值共识。
- Bull Power (多头力量):
最高价 - EMA。衡量多头将价格推高至平均价值之上的能力。 - Bear Power (空头力量):
最低价 - EMA。衡量空头将价格压低至平均价值之下的能力。
交易逻辑设计:
- 买入条件 (做多):
- 趋势向上:收盘价 > EMA(或者 EMA 处于上升状态)。
- 空头力竭:Bear Power 为负值(价格回落),但 Bear Power 开始回升(即当前的 Bear Power > 上一根 K 线的 Bear Power)。这通常代表上升趋势中的回调买点。
- 卖出条件 (平仓):
- 趋势反转:收盘价 < EMA。
- 或者 Bull Power 开始减弱(本策略为了简化,主要使用趋势反转作为平仓信号)。
2. QMT 策略代码实现
以下代码是一个完整的单标的策略示例。它使用了 pandas 计算 EMA,并使用 order_target_percent 进行仓位管理。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
'''
策略名称:Elder-Ray (艾达透视指标) 交易策略
策略逻辑:
1. 计算 13周期 EMA。
2. 计算 Bull Power = High - EMA。
3. 计算 Bear Power = Low - EMA。
4. 买入:收盘价 > EMA (趋势向上) 且 Bear Power < 0 (有回调) 且 Bear Power 上升 (回调结束迹象)。
5. 卖出:收盘价 < EMA (趋势破坏)。
'''
def init(ContextInfo):
# 1. 设置策略参数
ContextInfo.N = 13 # EMA 周期,Elder 建议 13
ContextInfo.trade_code = '600000.SH' # 示例标的:浦发银行
ContextInfo.account_id = 'YOUR_ACCOUNT_ID' # 请替换为您的资金账号
# 2. 设置股票池
ContextInfo.set_universe([ContextInfo.trade_code])
# 3. 设置交易账号 (实盘/回测都需要)
ContextInfo.set_account(ContextInfo.account_id)
# 4. 初始化全局变量
ContextInfo.holding = False # 简单的持仓标记
def handlebar(ContextInfo):
# 获取当前正在处理的 K 线位置
index = ContextInfo.barpos
# 获取当前图表的代码
stock_code = ContextInfo.trade_code
# 1. 获取历史行情数据
# 我们需要 High, Low, Close 来计算指标
# 获取足够长的数据以确保 EMA 计算准确,这里取 N + 50 根
data_len = ContextInfo.N + 50
# 使用 get_market_data_ex 获取数据 (效率更高)
# 注意:period='1d' 表示日线,根据实际运行周期调整
market_data = ContextInfo.get_market_data_ex(
['high', 'low', 'close'],
[stock_code],
period='1d',
count=data_len,
dividend_type='front' # 前复权
)
if stock_code not in market_data:
return
df = market_data[stock_code]
# 确保数据量足够
if len(df) < ContextInfo.N:
return
# 2. 计算 Elder-Ray 指标
# 计算 EMA (Exponential Moving Average)
df['ema'] = df['close'].ewm(span=ContextInfo.N, adjust=False).mean()
# 计算 Bull Power 和 Bear Power
df['bull_power'] = df['high'] - df['ema']
df['bear_power'] = df['low'] - df['ema']
# 3. 获取信号判断所需的数值
# 我们基于上一根 K 线收盘后的数据来决定当下的操作 (避免未来函数)
# iloc[-1] 是当前正在走的 K 线(如果是盘中),iloc[-2] 是上一根走完的 K 线
# 在回测模式下,handlebar 也是逐根运行,通常取 -1 代表当前回测时间点的数据
# 为了稳健,我们取最近两根数据进行比较
current_close = df['close'].iloc[-1]
current_ema = df['ema'].iloc[-1]
current_bear = df['bear_power'].iloc[-1]
prev_bear = df['bear_power'].iloc[-2]
# 4. 生成交易信号
# --- 买入逻辑 ---
# 条件 A: 价格在 EMA 之上 (趋势向上)
condition_trend_up = current_close > current_ema
# 条件 B: Bear Power 为负 (价格处于回调区域,非追高)
condition_dip = current_bear < 0
# 条件 C: Bear Power 正在增强 (空头力量减弱,柱状图向上)
condition_momentum = current_bear > prev_bear
buy_signal = condition_trend_up and condition_dip and condition_momentum
# --- 卖出逻辑 ---
# 条件: 收盘价跌破 EMA (趋势反转)
sell_signal = current_close < current_ema
# 5. 执行交易
# 获取当前持仓 (这里使用简单的 ContextInfo.holding 标记,实盘建议用 get_trade_detail_data 查询真实持仓)
# 如果是最后一根K线(实盘) 或者 回测模式
if not ContextInfo.is_last_bar() and not ContextInfo.do_back_test:
# 如果不是回测且不是最新K线,为了加快速度可以跳过,或者只计算指标不交易
return
# 执行买入
if buy_signal and not ContextInfo.holding:
print(f"[{stock_code}] 触发买入信号: Close={current_close:.2f}, EMA={current_ema:.2f}, Bear={current_bear:.2f}")
# 全仓买入 (目标仓位 100%)
order_target_percent(stock_code, 1.0, ContextInfo, ContextInfo.account_id)
ContextInfo.holding = True
# 执行卖出
elif sell_signal and ContextInfo.holding:
print(f"[{stock_code}] 触发卖出信号: Close={current_close:.2f}, EMA={current_ema:.2f}")
# 清仓 (目标仓位 0%)
order_target_percent(stock_code, 0.0, ContextInfo, ContextInfo.account_id)
ContextInfo.holding = False
3. 代码关键点解析
-
数据获取 (
get_market_data_ex):- 我们使用了
get_market_data_ex接口,这是 QMT 中获取历史数据最高效的方式。 - 获取了
high(最高价),low(最低价),close(收盘价) 三个字段。 dividend_type='front'设置为前复权,这对于技术指标计算非常重要,避免分红除权导致 EMA 出现断层。
- 我们使用了
-
指标计算 (
pandas):- QMT 内置了
pandas库。 df['close'].ewm(span=N, adjust=False).mean():这是计算 EMA 的标准 Pandas 写法。span对应 EMA 的周期。- Bull Power 和 Bear Power 直接通过列向量减法计算,速度很快。
- QMT 内置了
-
信号逻辑:
- 策略的核心在于捕捉“上升趋势中的回调”。
current_close > current_ema保证大方向是向上的。current_bear < 0保证当前价格并没有偏离均线过高,甚至可能低于均线(因为 Bear Power 是 Low - EMA),这是一个相对安全的买点。current_bear > prev_bear是关键,意味着空头打压的力量正在减弱,价格可能即将反弹。
-
交易执行 (
order_target_percent):- 使用了
order_target_percent函数。 - 传入
1.0代表买入至满仓(根据可用资金)。 - 传入
0.0代表清仓。 - 这种函数比
order_lots更方便,因为它自动计算需要买卖的数量。
- 使用了
4. 注意事项
- 账号设置:请务必将代码中的
YOUR_ACCOUNT_ID替换为您真实的资金账号。 - 运行周期:Elder-Ray 指标通常用于日线级别。如果在分钟级别使用,信号可能会过于频繁,建议调大 EMA 的周期(例如 N=26 或 N=60)。
- 实盘与回测:代码中加入了
ContextInfo.is_last_bar()的判断,这是为了在实盘运行时,只在最新的 K 线数据更新时触发逻辑,避免对历史 K 线重复下单。
Q&A
Q: 如何在 QMT 中查看 Bear Power 和 Bull Power 的数值?
A: 您可以使用 ContextInfo.paint() 函数在 handlebar 中将计算出的数值画在副图上。例如:
ContextInfo.paint('BearPower', current_bear, -1, 0)
ContextInfo.paint('BullPower', current_bull, -1, 0)
Q: 这个策略适合什么类型的股票?
A: Elder-Ray 是一种趋势跟随策略,适合波动率适中且有明显趋势的个股或 ETF。在横盘震荡市中,EMA 会走平,导致价格频繁穿梭于 EMA 上下,从而产生虚假信号。
Q: 如何修改 EMA 的周期?
A: 在 init 函数中修改 ContextInfo.N = 13 这一行,将 13 改为您想要的数字即可。