🚀 AI 一键生成 ptrade 策略代码

PTrade 量化策略如何应对股票分红、送股、配股等公司行为?

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

问题描述

我的策略在遇到股票分红送股时会出错,应该如何正确处理这些公司行为?

解决方案

在PTrade量化交易平台中,处理股票分红、送股(除权除息)导致的数据异常,最核心的方法是使用复权数据(Rehabilitation)

如果不使用复权数据,当股票发生分红或拆股时,股价会自然回落(产生缺口),导致技术指标(如均线、MACD)出现断崖式下跌,从而触发错误的卖出信号或止损逻辑。

以下是详细的解决方案和代码实现。

核心解决方案:使用前复权 (fq='pre')

在 PTrade API 中,获取历史行情的函数 get_historyget_price 都有一个参数 fq

  • fq='pre' (前复权):这是策略中最常用的模式。它以当前价格为基准,向下调整历史价格。这样可以保持K线的连续性,消除除权除息造成的“假跌”缺口,保证技术指标计算的准确性。
  • fq=None (不复权):默认值。返回交易所的原始价格。如果策略逻辑依赖真实成交价(如某些套利策略),才使用此项。
  • fq='post' (后复权):以股票上市价为基准,向上调整当前价格。通常用于计算长期的资产回报率,但在实盘交易信号生成中使用较少。

代码实现示例

下面的策略展示了如何正确获取前复权数据来计算均线,从而避免分红送股导致的错误信号。

def initialize(context):
    """
    初始化函数
    """
    # 设置我们要操作的股票,例如:贵州茅台
    g.security = '600519.SS'
    set_universe(g.security)
    
    # 设置均线周期
    g.short_window = 5
    g.long_window = 20

def handle_data(context, data):
    """
    盘中运行函数
    """
    security = g.security
    
    # ------------------------------------------------------------------
    # 关键点:获取历史数据时,务必设置 fq='pre' (前复权)
    # 如果不设置或设置为None,分红送股当天历史数据与当前价格会产生巨大缺口
    # ------------------------------------------------------------------
    # 获取过去20天的收盘价,使用前复权
    hist_data = get_history(g.long_window, '1d', 'close', security, fq='pre')
    
    # 确保数据长度足够
    if len(hist_data) < g.long_window:
        return
    
    # 提取收盘价数组
    # 注意:get_history返回的数据结构可能因版本不同略有差异,通常取values
    close_prices = hist_data['close'].values
    
    # 计算均线
    # 使用前复权数据计算出的均线是平滑的,不会受除权影响
    ma_short = close_prices[-g.short_window:].mean()
    ma_long = close_prices[-g.long_window:].mean()
    
    # 获取当前持仓
    position = get_position(security).amount
    
    # 获取当前可用资金
    cash = context.portfolio.cash
    
    # 获取当前价格 (data中的价格通常是实时的不复权价格,但在比较逻辑中,
    # 只要历史均线是复权的,趋势判断就是正确的)
    current_price = data[security]['close']
    
    # 简单的双均线策略逻辑
    # 金叉:短均线上穿长均线 -> 买入
    if ma_short > ma_long and position == 0:
        log.info("金叉出现,执行买入。MA5: %.2f, MA20: %.2f" % (ma_short, ma_long))
        order_value(security, cash)
        
    # 死叉:短均线下穿长均线 -> 卖出
    elif ma_short < ma_long and position > 0:
        log.info("死叉出现,执行卖出。MA5: %.2f, MA20: %.2f" % (ma_short, ma_long))
        order_target(security, 0)

def after_trading_end(context, data):
    """
    盘后处理(可选)
    """
    # 如果需要查询具体的除权除息信息,可以使用 get_stock_exrights
    # 这通常用于统计或特殊策略,不是常规趋势策略必须的
    ex_rights_info = get_stock_exrights(g.security, date=context.blotter.current_dt)
    if ex_rights_info is not None:
        log.info("今日 %s 存在除权除息信息: %s" % (g.security, ex_rights_info))

进阶:如何查询具体的除权除息信息?

如果你的策略是专门针对分红(例如“抢权”策略或“填权”策略),你需要知道具体的送股比例和分红金额。可以使用 get_stock_exrights 接口。

# 示例:检查某只股票在回测当日是否发生除权除息
def check_corporate_action(context, security):
    # 获取当日的除权除息信息
    # date参数传入当前回测日期
    info = get_stock_exrights(security, date=context.blotter.current_dt)
    
    if info is not None and not info.empty:
        # info 是一个 DataFrame
        # bonus_ps: 每股分红
        # allotted_ps: 每股送股
        # rationed_ps: 每股配股
        log.info("股票 %s 今日发生除权除息" % security)
        log.info(info)
        return True
    return False

总结

  1. 通用策略(趋势、均线、突破等)

    • 必须在 get_historyget_price 中显式指定 fq='pre'
    • 这是解决“分红送股导致策略出错”的最标准方法。
  2. 持仓处理

    • 在PTrade的回测引擎中,当发生分红送股时,系统会自动调整你的持仓数量(增加股数)和账户资金(增加现金分红)。你不需要手动写代码去调整 context.portfolio.positions,引擎会处理交割。
  3. 避免未来函数

    • 前复权数据在回测时会修改“过去”的价格以适应“现在”的价格。在实盘中,前复权是动态调整的。只要策略逻辑是基于相对趋势(如均线交叉、RSI超买超卖),使用前复权是安全的且正确的。

Q&A: 常见问题解答

Q: 为什么我的策略在分红那天突然全仓卖出了?
A: 很可能是因为你使用了默认的 fq=None(不复权)。分红或拆股会导致股价在除权日大幅“低开”。例如10送10,股价会减半。如果你的策略有“跌幅超过5%止损”的逻辑,使用不复权数据就会误触发止损。使用 fq='pre' 可解决此问题。

Q: 实盘交易中,get_history 获取的数据也是复权的吗?
A: 是的,只要设置了 fq='pre',实盘中获取的历史K线也是经过前复权处理的,能够与实时行情平滑衔接。

Q: 我可以直接修改 data[security]['close'] 的值吗?
A: 不可以。handle_data 中的 data 对象包含的是当下的快照数据(Snapshot),是交易所推送的真实价格。你无法改变它,也不应该改变它。你应该调整的是你用来计算指标的历史数据(即通过 get_history 获取的数据)。

Q: get_stock_exrights 返回 None 是什么意思?
A: 表示该股票在查询的日期没有发生除权除息行为。这是正常现象,绝大多数交易日都会返回 None。