🚀 AI 一键生成 ptrade 策略代码

如何在 PTrade 中构建一个能自动切换动量和反转模式的自适应策略?

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

问题描述

如何编写一个策略,让它在市场趋势明显时跟随趋势,在市场震荡时进行高抛低吸?

解决方案

这是一个非常经典的量化交易需求,通常被称为**“市场状态识别”“双模策略”**(Regime Switching Strategy)。

要实现这个策略,核心难点在于如何定义“趋势”和“震荡”

在本策略中,我将使用**布林带(Bollinger Bands)及其衍生指标布林带宽度(Bandwidth)**来实现:

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

    • 趋势模式:当布林带开口扩大(带宽变大)时,意味着波动率上升,市场可能进入趋势行情。
    • 震荡模式:当布林带开口收窄(带宽变小)时,意味着波动率下降,市场处于横盘整理。
  2. 交易逻辑:

    • 在趋势模式下(追涨杀跌): 价格突破上轨买入,跌破中轨卖出(止盈/止损)。
    • 在震荡模式下(高抛低吸): 价格触及下轨买入(抄底),触及上轨卖出(高抛)。

策略代码实现

以下是基于 PTrade 框架的完整策略代码。

import numpy as np
import pandas as pd

def initialize(context):
    """
    初始化函数,设置策略参数和股票池
    """
    # 设置要操作的股票,这里以恒生电子为例
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 策略参数设置
    g.n = 20          # 布林带计算周期(通常为20)
    g.k = 2           # 布林带标准差倍数(通常为2)
    
    # 核心参数:用于判断趋势还是震荡的阈值
    # 逻辑:计算过去一段时间的平均带宽,如果当前带宽 > 平均带宽 * 1.2,认为是趋势;否则为震荡
    # 这个系数需要根据具体标的波动性进行调整
    g.trend_threshold_ratio = 1.2 
    
    # 设置滑点和手续费(回测时建议设置,更接近实盘)
    set_commission(commission_ratio=0.0003, min_commission=5.0, type="STOCK")
    set_slippage(slippage=0.002)

def get_bollinger_bands(security, n, k):
    """
    计算布林带数据的辅助函数
    返回: upper(上轨), middle(中轨), lower(下轨), bandwidth(带宽)
    """
    # 获取过去 n+5 天的数据,保证计算均线时数据充足
    # include=True 包含当前未完成的K线,用于实时计算
    hist = get_history(n + 5, '1d', 'close', security, fq='pre', include=True)
    
    if len(hist) < n:
        return None, None, None, None
    
    # 提取收盘价序列
    close_prices = hist['close']
    
    # 计算中轨(N日移动平均线)
    middle = close_prices.rolling(window=n).mean()
    
    # 计算标准差
    std = close_prices.rolling(window=n).std()
    
    # 计算上轨和下轨
    upper = middle + k * std
    lower = middle - k * std
    
    # 计算带宽 (Bandwidth) = (上轨 - 下轨) / 中轨
    # 带宽反映了市场的波动率
    bandwidth = (upper - lower) / middle
    
    # 返回最新的值(序列的最后一个)以及带宽序列(用于计算历史平均带宽)
    return upper.iloc[-1], middle.iloc[-1], lower.iloc[-1], bandwidth

def handle_data(context, data):
    """
    盘中运行函数,每个周期执行一次
    """
    security = g.security
    
    # 1. 获取布林带数据
    up, mid, low, bandwidth_series = get_bollinger_bands(security, g.n, g.k)
    
    if up is None:
        return # 数据不足,暂不交易

    # 2. 获取当前价格
    current_price = data[security]['close']
    
    # 3. 判断市场状态 (Regime Switching)
    # 计算当前带宽
    current_bandwidth = bandwidth_series.iloc[-1]
    # 计算过去20天的平均带宽(作为基准)
    avg_bandwidth = bandwidth_series[-20:].mean()
    
    # 定义状态:如果当前波动率显著高于近期平均水平,判定为趋势模式
    is_trend_mode = current_bandwidth > (avg_bandwidth * g.trend_threshold_ratio)
    
    # 获取当前持仓
    position = get_position(security).amount
    cash = context.portfolio.cash
    
    # 4. 执行交易逻辑
    if is_trend_mode:
        # ====================
        # 模式 A: 趋势跟随 (Trend Following)
        # ====================
        # 逻辑:波动率放大,市场选择了方向,我们要顺势而为
        
        # 买入信号:价格突破上轨,且空仓
        if current_price > up and position == 0:
            log.info("【趋势模式】价格突破上轨,波动率放大,追涨买入 %s" % security)
            order_value(security, cash)
            
        # 卖出信号:价格跌破中轨(趋势减弱或反转),清仓止损/止盈
        # 注意:趋势策略中,通常不等到跌破下轨才卖,中轨往往是趋势的生命线
        elif current_price < mid and position > 0:
            log.info("【趋势模式】价格跌破中轨,趋势可能结束,平仓卖出 %s" % security)
            order_target(security, 0)
            
    else:
        # ====================
        # 模式 B: 震荡回归 (Mean Reversion)
        # ====================
        # 逻辑:波动率低,价格在布林带通道内运行,高抛低吸
        
        # 买入信号:价格触及下轨(超卖),且空仓
        if current_price < low and position == 0:
            log.info("【震荡模式】价格触及下轨,波动率平稳,抄底买入 %s" % security)
            order_value(security, cash)
            
        # 卖出信号:价格触及上轨(超买),清仓止盈
        elif current_price > up and position > 0:
            log.info("【震荡模式】价格触及上轨,回归压力位,高抛卖出 %s" % security)
            order_target(security, 0)

    # 记录日志方便调试
    # log.info("当前价格: %.2f, 上轨: %.2f, 下轨: %.2f, 状态: %s" % (
    #     current_price, up, low, "趋势" if is_trend_mode else "震荡"
    # ))

策略逻辑详解

1. 核心指标:布林带宽度 (Bandwidth)

  • 计算公式(上轨 - 下轨) / 中轨
  • 作用:这是衡量市场“活跃度”的关键。
    • 当带宽很窄时,说明股价波动很小,市场处于蓄势整理阶段(震荡)。
    • 当带宽突然变大时,说明股价波动剧烈,通常伴随着突破,市场进入单边行情(趋势)。

2. 状态切换机制

代码中使用了 g.trend_threshold_ratio = 1.2

  • 我们计算当前的带宽,并与过去20天的平均带宽做比较。
  • 如果 当前带宽 > 平均带宽 * 1.2,说明现在的波动率比平时高出20%,我们认为市场“醒了”,开始走趋势。
  • 否则,认为市场还在“睡觉”或正常波动,适合高抛低吸。

3. 为什么趋势模式下卖出逻辑不同?

  • 震荡时:我们在上轨卖出,因为预期价格会回落。
  • 趋势时:价格突破上轨是买入信号(强者恒强),此时不能卖出。在趋势模式下,我们通常用中轨作为移动止损线,只要价格还在中轨上方,就一直持有,直到跌破中轨才认为趋势结束。

优化建议

  1. 参数调整g.trend_threshold_ratio 是最敏感的参数。如果你发现策略频繁在震荡中追高被套,可以调高这个值(例如 1.5),让策略更难进入趋势模式。
  2. 增加过滤:在趋势买入时,可以增加成交量的判断(如:volume > 20日均量),确保突破是有效的。
  3. 止损保护:震荡策略最怕的是“假震荡真突破”。例如在下轨买入后,价格没有反弹反而暴跌(单边下跌趋势开始)。建议在震荡买入后设置一个固定的硬止损(如亏损5%强平)。

Q&A

Q: 这个策略适合什么周期的回测?
A: 建议使用日线级别。分钟级别噪音较大,布林带的假突破会更多,导致频繁止损。

Q: 为什么使用 order_valueorder_target
A: order_value(security, cash) 表示用当前所有现金买入。order_target(security, 0) 表示将持仓调整为0(即清仓)。这是 PTrade 中最常用的仓位管理函数。

Q: 如何在 PTrade 中查看策略表现?
A: 将上述代码复制到 PTrade 的“策略研究”或“回测”模块中,点击“运行回测”,选择一个较长的时间段(如2020年至今),即可看到收益曲线和交易明细。