🚀 AI 一键生成 joinquant 策略代码

如何在 JoinQuant 真实价格模式下,通过 on_event 响应分红送股事件?

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

问题描述

在真实价格回测中,当股票分红送股时,我该如何处理我的策略逻辑?

解决方案

在聚宽(JoinQuant)的真实价格(动态复权)回测模式中,处理股票分红送股的逻辑其实非常简便,因为平台底层已经为您完成了大部分繁琐的账户调整工作。

以下是关于如何在策略逻辑中处理分红送股的详细说明:

核心机制说明

当您在 initialize 函数中开启了 set_option('use_real_price', True) 后:

  1. 账户自动调整(无需手动干预)

    • 送股/拆股:系统会自动调整您持仓的股票数量(total_amount),并相应降低持仓成本,保持总市值不变。
    • 现金分红:系统会自动将分红金额(扣税后)加到您的账户可用资金(available_cash)中。
    • 时间点:这些调整通常发生在除权除息日的开盘前或收盘后,具体取决于回测引擎的处理逻辑(通常在当日数据更新时体现)。
  2. 行情数据连续性(关键点)

    • 在真实价格模式下,调用 historyattribute_historyget_price 等API获取历史数据时,系统返回的是基于当前日期复权因子计算的前复权数据
    • 这意味着:即使发生了分红导致股价“跳水”,您获取的历史K线也会相应调整,使得技术指标(如均线 MA、MACD)保持平滑连续。您不需要为了技术指标的计算而去手动修正历史价格。

策略逻辑编写建议

基于上述机制,您的策略逻辑处理建议如下:

1. 基础策略(通常不需要额外代码)

对于大多数基于技术指标或基本面的策略,您不需要编写专门的代码来处理分红。

  • 原因:因为数据API已经帮您做了前复权处理,您的 if price > average_price 等逻辑判断依然有效,不会因为除权导致的股价下跌而产生误判。

2. 避免跨日缓存数据

切记:不要在全局变量(如 g.data_cache)中缓存历史行情数据超过一天。

  • 原因:今天的“前复权价格”和昨天的“前复权价格”在发生分红时是不一样的。如果您缓存了昨天的价格并在今天使用,可能会导致数据与今日的复权基准不匹配。
  • 做法:每次在 handle_data 或定时函数中实时调用 API 获取数据。

3. 监听分红事件(进阶需求)

如果您希望在分红发生时执行特定操作(例如:收到现金分红后立即再投资,或者记录日志),可以使用 on_event 回调函数。

代码示例

以下代码展示了如何开启真实价格模式,以及如何(可选地)监听分红事件:

# -*- coding: utf-8 -*-
from jqdata import *

def initialize(context):
    # 1. 必须开启真实价格模式
    set_option('use_real_price', True)
    
    # 设置基准
    set_benchmark('000300.XSHG')
    
    # 选取一只股票作为示例
    g.security = '000001.XSHE'

def handle_data(context, data):
    # 2. 正常的策略逻辑
    # 获取过去5天的收盘价(系统会自动返回基于当前日期的前复权数据,保证指标连续)
    hist = attribute_history(g.security, 5, '1d', ['close'])
    ma5 = hist['close'].mean()
    current_price = data[g.security].close
    
    # 即使发生分红除权,这里的逻辑也不会因为股价突然变低而失效
    if current_price > ma5:
        order_target_value(g.security, 10000)

# 3. (可选) 使用事件回调处理分红
def on_event(context, event):
    # 检查是否是分红送股事件
    if isinstance(event, DividendsEvent):
        # 打印分红信息
        log.info(f"股票 {event.security} 发生分红/送股")
        
        # event.dividends 是一个列表,包含具体信息
        for div in event.dividends:
            log.info(f"分红详情: {div}")
            # div 包含 keys: 'date', 'scale_factor'(送股比例), 'bonus_pre_tax'(税前红利) 等
            
        # 示例逻辑:如果有现金分红,尝试将多出的现金买入该股票(再投资)
        # 注意:这里只是简单示例,实际需考虑资金是否足够一手等
        if context.portfolio.available_cash > 1000:
            order_value(event.security, context.portfolio.available_cash)

总结

  • 开启设置:务必在 initialize 中调用 set_option('use_real_price', True)
  • 数据获取:放心使用 history 等API,它们会自动处理复权,保证指标连续。
  • 不要缓存:不要跨日缓存行情数据。
  • 特殊处理:只有当您需要针对“分红”这个动作本身做交易(如红利再投)时,才需要使用 on_event

Q&A

Q: 如果我不开启 use_real_price (即设为 False),分红时会发生什么?
A: 如果关闭真实价格模式,回测将使用“后复权”或“定点复权”逻辑(取决于回测开始时间)。在这种模式下,您看到的价格在整个回测期间是平滑的,但不是历史上当天的真实挂牌价格。这会导致您无法模拟真实的买入股数(因为真实交易是用当天的真实价格买入整百股),且在模拟交易中会产生价格差异,因此强烈建议开启真实价格模式。

Q: 真实价格模式下,分红的税费是如何计算的?
A: 在回测和模拟交易中,由于系统无法精确预知您何时卖出股票(从而确定持仓时长以计算差异化税率),聚宽通常统一按照 20% 的税率在分红到账时直接扣除红利税。

Q: on_event 函数在回测和模拟交易中都能用吗?
A: 是的,on_event 支持回测和模拟交易。除了分红事件 (DividendsEvent),它还支持强行平仓事件 (ForcedLiquidationEvent)。