🚀 AI 一键生成 ptrade 策略代码

PTrade 策略中如何对均线等技术指标进行复权处理以消除除权除息影响?

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

问题描述

当股票发生除权除息后,我的均线指标会产生跳空,如何修正这个问题?

解决方案

在PTrade中,当股票发生除权除息(分红、送股、配股等)时,股价会自然回落。如果您使用默认的“不复权”数据计算均线,历史的高价和除权后的低价混合会导致均线出现非市场因素的“跳空”下跌,从而产生错误的交易信号。

要修正这个问题,需要在获取历史数据时使用 “前复权”(Forward Adjustment)

核心解决方案

在调用 get_historyget_price 函数时,设置参数 fq='pre'

  • fq=None (默认):不复权。真实记录当时的成交价格,除权日会出现缺口。
  • fq='pre' (前复权):以当前价格为基准,向下调整历史价格。这是策略回测和实盘中最常用的模式,因为它能保持K线形态的连续性,且最新价格与盘面实时价格一致。
  • fq='post' (后复权):以历史上市价格为基准,向上调整当前价格。通常用于计算长期累计收益,但不适合实盘交易逻辑(因为计算出的价格与盘口挂单价格不一致)。

策略代码示例

以下是一个完整的双均线策略示例,展示了如何正确使用 fq='pre' 来获取平滑的均线数据。

def initialize(context):
    """
    初始化函数
    """
    # 设置我们要操作的股票,以恒生电子为例
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 设定均线周期
    g.short_window = 5
    g.long_window = 10

def handle_data(context, data):
    """
    盘中运行函数
    """
    security = g.security
    
    # ------------------------------------------------------------------
    # 核心修正点:获取历史数据时,务必添加 fq='pre' 参数
    # ------------------------------------------------------------------
    # 获取过去15天的收盘价(多取几天以防停牌或计算余量)
    # 如果不加 fq='pre',一旦遇到除权除息,ma_long 的计算结果将瞬间暴跌
    history_data = get_history(count=15, frequency='1d', field='close', 
                               security_list=security, fq='pre', include=False)
    
    # 提取收盘价数组
    close_prices = history_data['close']
    
    # 确保数据长度足够计算长周期均线
    if len(close_prices) < g.long_window:
        return

    # 计算短期均线 (5日)
    ma_short = close_prices[-g.short_window:].mean()
    
    # 计算长期均线 (10日)
    ma_long = close_prices[-g.long_window:].mean()
    
    # 获取当前最新价格
    current_price = data[security]['close']
    
    # 获取当前持仓
    position = get_position(security)
    
    # 打印日志方便观察复权后的均线数值
    # log.info("日期:%s, 5日均线:%.2f, 10日均线:%.2f, 现价:%.2f" % (context.blotter.current_dt, ma_short, ma_long, current_price))

    # 简单的金叉死叉逻辑
    # 金叉:短均线上穿长均线,且无持仓 -> 买入
    if ma_short > ma_long and position.amount == 0:
        log.info("金叉出现 - 买入 %s" % security)
        order_target_value(security, context.portfolio.cash)
        
    # 死叉:短均线下穿长均线,且有持仓 -> 卖出
    elif ma_short < ma_long and position.amount > 0:
        log.info("死叉出现 - 卖出 %s" % security)
        order_target(security, 0)

常见问题 Q&A

Q: 为什么实盘交易推荐用前复权(pre)而不是后复权(post)?
A: 后复权会将当前股价向上调整(例如某股票现价10元,后复权后可能是500元)。如果您在策略中使用 limit_price(限价单)或判断当前价格是否触发止损,使用后复权计算出的500元去和交易所实时的10元做对比,会导致严重的逻辑错误。前复权则保证了历史K线形态连续,同时当前价格就是交易所的实时价格。

Q: get_price 函数也支持复权吗?
A: 支持。get_price 的参数用法与 get_history 完全一致,同样设置 fq='pre' 即可。

Q: 如果我只做日内Tick级别的交易,需要复权吗?
A: 如果您的策略只依赖当天的Tick数据(如 get_snapshottick_data),不需要复权,因为当天之内没有除权缺口。但如果您在Tick策略中需要参考过去几天的日线均线作为趋势判断,那么获取日线数据的那部分代码必须复权。

Q: 为什么有时候使用了 fq='pre' 数据还是不对?
A: 请检查 count 数量。如果 count 设置得太小(例如刚好等于均线周期),且刚好遇到停牌,可能会导致有效数据不足。建议 get_historycount 设置得比所需均线周期稍大一些(如计算10日均线,取15或20条数据),利用切片(如 [-10:])来计算,这样更稳健。