🚀 AI 一键生成 ptrade 策略代码

PTrade 策略开发:如何构建一个能自动切换动量和反转模式的策略?

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

问题描述

如何编写一个自适应策略,使其能够在趋势市场中采用动量策略,在震荡市场中采用反转策略?

解决方案

这是一个非常经典的量化交易思路。要实现一个“自适应”策略,核心在于市场状态的识别(Market Regime Classification)

我们需要一个指标来判断当前是“趋势市”还是“震荡市”,然后根据判断结果动态切换交易逻辑。

策略设计思路

  1. 市场状态识别(核心)

    • 我们将使用 考夫曼效率系数 (Kaufman Efficiency Ratio, ER) 来衡量趋势强度。
    • ER计算公式ER = 价格总位移 / 价格总路程
    • 逻辑:ER值在0到1之间。ER越接近1,表示趋势越强(价格走直线);ER越接近0,表示震荡越剧烈(价格上下波动但位移很小)。
    • 阈值:设定一个阈值(例如 0.3),大于该值视为趋势,小于该值视为震荡。
  2. 趋势策略(当 ER > 阈值)

    • 采用 双均线策略
    • 逻辑:短期均线 > 长期均线 -> 买入;短期均线 < 长期均线 -> 卖出。
  3. 震荡/反转策略(当 ER <= 阈值)

    • 采用 RSI均值回归策略
    • 逻辑:RSI < 30 (超卖) -> 买入;RSI > 70 (超买) -> 卖出。

PTrade 策略代码实现

以下是完整的策略代码,您可以直接复制到 PTrade 的回测环境中运行。

import numpy as np
import pandas as pd

def initialize(context):
    """
    初始化函数,设置参数和股票池
    """
    # 1. 设置要操作的标的(这里以沪深300ETF为例,也可以换成具体股票)
    g.security = '510300.SS'
    set_universe(g.security)
    
    # 2. 市场状态识别参数 (效率系数 ER)
    g.er_period = 20       # 计算ER的时间窗口
    g.er_threshold = 0.25  # ER阈值,大于此值判断为趋势,小于为震荡
    
    # 3. 趋势策略参数 (双均线)
    g.ma_short_len = 10    # 短期均线
    g.ma_long_len = 30     # 长期均线
    
    # 4. 震荡策略参数 (RSI)
    g.rsi_period = 14      # RSI周期
    g.rsi_low = 30         # 超卖阈值(买入)
    g.rsi_high = 70        # 超买阈值(卖出)
    
    # 设置手续费(可选,模拟真实交易成本)
    set_commission(commission_ratio=0.0003, min_commission=5.0, type="ETF")

def get_efficiency_ratio(close_prices):
    """
    计算考夫曼效率系数 (Efficiency Ratio)
    ER = |总位移| / 总路程
    """
    if len(close_prices) < 2:
        return 0
    
    # 计算总位移:终点 - 起点 的绝对值
    net_change = abs(close_prices[-1] - close_prices[0])
    
    # 计算总路程:每天价格变化幅度的绝对值之和
    diffs = np.diff(close_prices)
    sum_of_changes = np.sum(np.abs(diffs))
    
    if sum_of_changes == 0:
        return 0
        
    er = net_change / sum_of_changes
    return er

def handle_data(context, data):
    """
    盘中运行函数,每日或每分钟调用
    """
    security = g.security
    
    # -------------------------------------------------------
    # 1. 数据获取
    # -------------------------------------------------------
    # 获取足够长的历史数据以计算指标
    # 需要的最大长度是长期均线长度 + 一些缓冲
    lookback = max(g.er_period, g.ma_long_len, g.rsi_period) + 5
    
    # 获取历史收盘价
    history_df = get_history(lookback, '1d', 'close', security, fq='pre', include=True)
    
    if len(history_df) < lookback:
        return # 数据不足,暂不交易
        
    close_prices = history_df['close'].values
    
    # -------------------------------------------------------
    # 2. 市场状态识别 (计算ER)
    # -------------------------------------------------------
    # 取最近 g.er_period 天的数据计算ER
    er_data = close_prices[-g.er_period:]
    current_er = get_efficiency_ratio(er_data)
    
    # 获取当前持仓
    position = get_position(security).amount
    cash = context.portfolio.cash
    current_price = data[security]['close']
    
    # 记录日志以便调试
    # log.info("日期: %s, ER值: %.4f" % (context.blotter.current_dt, current_er))
    
    # -------------------------------------------------------
    # 3. 策略分流
    # -------------------------------------------------------
    
    if current_er > g.er_threshold:
        # === 场景 A: 趋势市场 (动量策略) ===
        # 使用双均线逻辑
        
        ma_short = close_prices[-g.ma_short_len:].mean()
        ma_long = close_prices[-g.ma_long_len:].mean()
        
        # 趋势策略逻辑:金叉买入,死叉卖出
        if ma_short > ma_long:
            # 趋势向上,满仓买入
            if cash > 0:
                order_value(security, cash)
                log.info("[趋势模式] 金叉买入: ER=%.2f, MA_S=%.2f, MA_L=%.2f" % (current_er, ma_short, ma_long))
        
        elif ma_short < ma_long:
            # 趋势向下,清仓
            if position > 0:
                order_target(security, 0)
                log.info("[趋势模式] 死叉卖出: ER=%.2f" % (current_er))
                
    else:
        # === 场景 B: 震荡市场 (反转策略) ===
        # 使用 RSI 逻辑
        
        # PTrade 内置 get_RSI 接口,直接调用
        # 注意:get_RSI 返回的是 numpy array,取最后一个值
        rsi_values = get_RSI(close_prices, g.rsi_period)
        current_rsi = rsi_values[-1]
        
        # 震荡策略逻辑:超卖买入,超买卖出
        if current_rsi < g.rsi_low:
            # 超卖,预期反弹,买入
            if cash > 0:
                order_value(security, cash)
                log.info("[震荡模式] RSI超卖买入: ER=%.2f, RSI=%.2f" % (current_er, current_rsi))
                
        elif current_rsi > g.rsi_high:
            # 超买,预期回调,卖出
            if position > 0:
                order_target(security, 0)
                log.info("[震荡模式] RSI超买卖出: ER=%.2f, RSI=%.2f" % (current_er, current_rsi))

代码关键点解析

  1. 市场状态过滤器 (get_efficiency_ratio):

    • 这是策略的大脑。如果价格在20天内从10元涨到20元,且中间几乎没有回调,ER值会接近1(强趋势)。
    • 如果价格在20天内反复上下跳动,最后收盘还是10元,ER值会接近0(强震荡)。
    • 通过 g.er_threshold (0.25) 来划分界限。您可以根据回测结果调整这个值。
  2. 数据获取 (get_history):

    • 我们使用了 get_history 获取收盘价序列。为了保证计算准确,count (lookback) 设置得比所需周期稍大一些。
    • include=True 确保包含当前周期的最新数据。
  3. 趋势模块 (Moving Average):

    • 当识别为趋势时,我们忽略 RSI 指标,只看均线。这是为了防止在单边大涨行情中,RSI 长期钝化(一直大于70)导致策略过早卖出踏空。
  4. 震荡模块 (RSI):

    • 当识别为震荡时,我们忽略均线交叉(因为震荡市中均线会频繁发出错误信号,即“左右打脸”),转而使用 RSI 捕捉高抛低吸的机会。

优化建议

  • 参数调优g.er_threshold 是最敏感的参数。对于波动率大的标的(如科技股),可能需要调高阈值;对于大盘股,可能需要调低。
  • 止损机制:上述代码是纯信号驱动的。在实盘中,建议加入固定比例止损(例如亏损5%强制平仓),以防止市场状态判断错误(例如误判震荡为趋势,或者在暴跌时RSI持续超卖)。
  • 替代指标
    • 除了ER,您还可以使用 ADX (平均趋向指标) 来判断趋势强度(ADX > 25 为趋势)。
    • 也可以使用 布林带带宽 (Bandwidth),带宽收窄通常意味着震荡,开口扩大意味着趋势开始。

Q&A

Q: 为什么不直接同时使用均线和RSI,而要分状态?
A: 因为指标之间存在互斥性。在强趋势中,RSI会失效(长期超买/超卖);在震荡中,均线会失效(频繁金叉死叉造成磨损)。分状态处理可以扬长避短。

Q: 这个策略适合什么频率?
A: 代码中默认使用的是日线级别 ('1d')。对于自适应策略,日线级别通常比分钟级别更稳定,因为分钟级别的噪音(Noise)更大,ER系数容易失真。

Q: 如何在PTrade中查看ER值的变化情况?
A: 代码中有一行注释掉的 log.info。您可以取消注释,回测时在日志面板中查看每天的ER值,以此来辅助您调整阈值。