🚀 AI 一键生成 ptrade 策略代码

如何在 PTrade 中构建一个能根据市场状态在动量和反转策略间切换的模型?

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

问题描述

请帮我编写一个能自动在动量和反转策略之间切换的模型

解决方案

这是一个非常经典的量化交易思路,通常被称为**“市场状态识别”“体制转换(Regime Switching)”**策略。

策略核心逻辑

该模型的核心在于识别当前市场是处于“趋势状态”还是“震荡状态”,从而选择不同的交易逻辑:

  1. 市场状态过滤器(Switching Mechanism)

    • 使用 ADX (平均趋向指标) 来判断趋势强度。
    • ADX > 25:判定为强趋势市场,启用动量策略。
    • ADX <= 25:判定为震荡/弱趋势市场,启用反转策略。
  2. 动量策略(Momentum/Trend)

    • 当市场处于强趋势时使用。
    • 逻辑:双均线交叉。短期均线上穿长期均线买入,下穿卖出。
  3. 反转策略(Reversal/Mean Reversion)

    • 当市场处于震荡时使用。
    • 逻辑:RSI (相对强弱指标)。RSI < 30 超卖时买入(博反弹),RSI > 70 超买时卖出(博回调)。

PTrade 策略代码实现

以下是完整的策略代码。代码中使用了 talib 库来计算 ADX,这是 PTrade 平台支持的标准第三方库。

import numpy as np
import talib
import pandas as pd

def initialize(context):
    """
    初始化函数,设置参数和股票池
    """
    # 1. 设置要操作的股票(示例:恒生电子)
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 2. 策略参数设置
    # ADX 参数
    g.adx_period = 14       # ADX 计算周期
    g.adx_threshold = 25    # 趋势/震荡的分界线
    
    # 动量策略参数 (均线)
    g.ma_short = 5          # 短期均线
    g.ma_long = 20          # 长期均线
    
    # 反转策略参数 (RSI)
    g.rsi_period = 14       # RSI 周期
    g.rsi_lower = 30        # 超卖阈值 (买入)
    g.rsi_upper = 70        # 超买阈值 (卖出)
    
    # 3. 设置手续费和滑点 (回测用)
    set_commission(commission_ratio=0.0003, min_commission=5.0, type="STOCK")
    set_slippage(slippage=0.002)

def handle_data(context, data):
    """
    盘中运行函数,每日或每分钟执行
    """
    security = g.security
    
    # 1. 获取历史数据
    # 我们需要足够的数据来计算 ADX 和 MA,这里取 60 天
    # 注意:talib 需要 numpy.ndarray 类型的数据
    hist_len = 60
    h = get_history(hist_len, frequency='1d', field=['high', 'low', 'close'], security_list=security, fq='pre')
    
    # 如果数据不足,直接返回
    if h is None or len(h) < hist_len:
        return
    
    # 提取数据为 numpy 数组 (talib 需要 float 类型)
    # 注意:get_history 返回的 DataFrame 列名是小写的
    high_data = h['high'].values.astype(float)
    low_data = h['low'].values.astype(float)
    close_data = h['close'].values.astype(float)
    
    # 2. 计算技术指标
    
    # A. 计算 ADX (趋势强度)
    # talib.ADX(high, low, close, timeperiod)
    adx_array = talib.ADX(high_data, low_data, close_data, timeperiod=g.adx_period)
    current_adx = adx_array[-1]
    
    # B. 计算均线 (动量指标)
    ma_short_array = talib.MA(close_data, timeperiod=g.ma_short, matype=0)
    ma_long_array = talib.MA(close_data, timeperiod=g.ma_long, matype=0)
    curr_ma_short = ma_short_array[-1]
    curr_ma_long = ma_long_array[-1]
    prev_ma_short = ma_short_array[-2]
    prev_ma_long = ma_long_array[-2]
    
    # C. 计算 RSI (反转指标)
    # 也可以使用 PTrade 自带的 get_RSI,这里为了统一使用 talib
    rsi_array = talib.RSI(close_data, timeperiod=g.rsi_period)
    current_rsi = rsi_array[-1]
    
    # 3. 获取当前持仓和资金
    position = get_position(security).amount
    cash = context.portfolio.cash
    current_price = data[security]['close']
    
    # 4. 策略逻辑核心:状态切换
    
    # 打印当前状态日志
    log.info("日期: %s | ADX: %.2f | RSI: %.2f | MA5: %.2f | MA20: %.2f" % (
        context.blotter.current_dt, current_adx, current_rsi, curr_ma_short, curr_ma_long
    ))
    
    if current_adx > g.adx_threshold:
        # ---------------------------------------------------
        # 场景 A: 强趋势市场 (ADX > 25) -> 使用动量策略 (均线)
        # ---------------------------------------------------
        log.info(">>> 市场状态:强趋势 (使用动量策略)")
        
        # 金叉买入 (短期均线上穿长期均线)
        if curr_ma_short > curr_ma_long and prev_ma_short <= prev_ma_long:
            if position == 0:
                log.info("动量信号:金叉买入")
                order_value(security, cash)
        
        # 死叉卖出 (短期均线下穿长期均线)
        elif curr_ma_short < curr_ma_long and prev_ma_short >= prev_ma_long:
            if position > 0:
                log.info("动量信号:死叉卖出")
                order_target(security, 0)
                
    else:
        # ---------------------------------------------------
        # 场景 B: 震荡市场 (ADX <= 25) -> 使用反转策略 (RSI)
        # ---------------------------------------------------
        log.info(">>> 市场状态:震荡 (使用反转策略)")
        
        # 超卖买入 (RSI < 30)
        if current_rsi < g.rsi_lower:
            if position == 0:
                log.info("反转信号:RSI超卖买入")
                order_value(security, cash)
        
        # 超买卖出 (RSI > 70)
        elif current_rsi > g.rsi_upper:
            if position > 0:
                log.info("反转信号:RSI超买卖出")
                order_target(security, 0)

def after_trading_end(context, data):
    """
    盘后处理
    """
    pass

代码关键点解析

  1. 数据获取 (get_history)

    • 我们获取了 high, low, close 三种价格,因为计算 ADX 指标必须用到最高价和最低价,而不仅仅是收盘价。
    • fq='pre' 表示使用前复权数据,这在回测中非常重要,可以避免分红派息导致的价格跳空影响指标计算。
  2. 指标计算 (talib)

    • PTrade 原生支持 talib 库,这是一个非常高效的金融技术指标计算库。
    • talib.ADX:计算趋势强度。
    • talib.MA:计算移动平均线。
    • talib.RSI:计算相对强弱指数。
  3. 状态切换逻辑 (if current_adx > g.adx_threshold)

    • 这是模型的“大脑”。它决定了当前时刻是听从“均线”的指挥,还是听从“RSI”的指挥。
    • 为什么这样做? 均线策略在震荡市中会频繁止损(左右打脸),而 RSI 在单边大趋势中会过早卖出(踏空)或逆势抄底(接飞刀)。通过 ADX 将两者结合,可以互补短板。
  4. 交易执行

    • order_value(security, cash):全仓买入(使用当前所有可用资金)。
    • order_target(security, 0):清仓卖出。

如何优化此模型?

如果你想进一步改进这个策略,可以考虑以下方向:

  • 增加止损:无论处于哪种模式,增加一个固定比例(如 -5%)的硬止损,防止反转策略在暴跌中持续抄底。
  • 调整 ADX 阈值:25 是一个经验值,你可以根据回测结果调整为 20 或 30。
  • 多因子确认:在反转模式下,除了 RSI 超卖,还可以增加布林带(Bollinger Bands)下轨支撑作为双重确认。

Q&A

Q1: 为什么选择 ADX 作为切换指标?
A1: ADX (Average Directional Index) 是专门设计用来衡量趋势强度而非方向的指标。它能很好地量化当前市场是“有趋势”还是“无趋势”,因此是动量与反转策略切换的最佳裁判。

Q2: 代码中的 talib 需要我自己安装吗?
A2: 不需要。PTrade 的云端环境(回测和实盘)已经预装了 talib 库,直接 import talib 即可使用。

Q3: 这个策略适合什么级别的频率?
A3: 该代码默认设置为日线级别 (frequency='1d')。如果需要用于分钟级别(如 15分钟或 60分钟),只需在回测设置中更改频率,并适当调整 g.adx_period 等参数即可。