问题描述
解决方案
在聚宽(JoinQuant)的真实价格(动态复权)回测模式中,处理股票分红送股的逻辑其实非常简便,因为平台底层已经为您完成了大部分繁琐的账户调整工作。
以下是关于如何在策略逻辑中处理分红送股的详细说明:
核心机制说明
当您在 initialize 函数中开启了 set_option('use_real_price', True) 后:
-
账户自动调整(无需手动干预):
- 送股/拆股:系统会自动调整您持仓的股票数量(
total_amount),并相应降低持仓成本,保持总市值不变。 - 现金分红:系统会自动将分红金额(扣税后)加到您的账户可用资金(
available_cash)中。 - 时间点:这些调整通常发生在除权除息日的开盘前或收盘后,具体取决于回测引擎的处理逻辑(通常在当日数据更新时体现)。
- 送股/拆股:系统会自动调整您持仓的股票数量(
-
行情数据连续性(关键点):
- 在真实价格模式下,调用
history、attribute_history或get_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)。