🚀 AI 一键生成 qmt 策略代码

QMT 量化实战:Bull Power 与 Bear Power 指标策略实现

qmt | 发布时间: 2025/12/29
以下内容由 EasyQuant 生成。

问题描述

怎么写 Elder-Ray 指标的量化策略?

解决方案

这是一个基于 Elder-Ray Index (艾达透视指标) 的量化策略设计与代码实现。

1. 策略原理 (Elder-Ray Index)

Elder-Ray 指标由 Alexander Elder 博士开发,用于衡量市场中多头(买方)和空头(卖方)的力量。它由三个部分组成:

  1. EMA (指数移动平均线):通常使用 13 日 EMA,代表市场的价值共识。
  2. Bull Power (多头力量)最高价 - EMA。衡量多头将价格推高至平均价值之上的能力。
  3. Bear Power (空头力量)最低价 - EMA。衡量空头将价格压低至平均价值之下的能力。

交易逻辑设计:

  • 买入条件 (做多)
    1. 趋势向上:收盘价 > EMA(或者 EMA 处于上升状态)。
    2. 空头力竭:Bear Power 为负值(价格回落),但 Bear Power 开始回升(即当前的 Bear Power > 上一根 K 线的 Bear Power)。这通常代表上升趋势中的回调买点。
  • 卖出条件 (平仓)
    1. 趋势反转:收盘价 < EMA。
    2. 或者 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. 代码关键点解析

  1. 数据获取 (get_market_data_ex):

    • 我们使用了 get_market_data_ex 接口,这是 QMT 中获取历史数据最高效的方式。
    • 获取了 high (最高价), low (最低价), close (收盘价) 三个字段。
    • dividend_type='front' 设置为前复权,这对于技术指标计算非常重要,避免分红除权导致 EMA 出现断层。
  2. 指标计算 (pandas):

    • QMT 内置了 pandas 库。
    • df['close'].ewm(span=N, adjust=False).mean():这是计算 EMA 的标准 Pandas 写法。span 对应 EMA 的周期。
    • Bull PowerBear Power 直接通过列向量减法计算,速度很快。
  3. 信号逻辑:

    • 策略的核心在于捕捉“上升趋势中的回调”。
    • current_close > current_ema 保证大方向是向上的。
    • current_bear < 0 保证当前价格并没有偏离均线过高,甚至可能低于均线(因为 Bear Power 是 Low - EMA),这是一个相对安全的买点。
    • current_bear > prev_bear 是关键,意味着空头打压的力量正在减弱,价格可能即将反弹。
  4. 交易执行 (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 改为您想要的数字即可。