问题描述
解决方案
在 JoinQuant 聚宽平台中,要实现持仓股票分红时收到通知并执行操作,主要通过 on_event(context, event) 回调函数来实现。
当账户中的持仓标的发生分红送股(DividendsEvent)或强行平仓(ForcedLiquidationEvent)时,系统会自动触发该函数。
实现步骤
- 定义
on_event函数:在策略代码中添加此函数。 - 判断事件类型:检查
event是否为分红事件。 - 获取分红信息:从
event对象中提取股票代码、现金分红金额或送股比例。 - 执行操作:编写逻辑发送通知(如
send_message)或进行交易(如红利再投资)。
策略代码示例
以下是一个完整的策略示例。该策略会买入一只股票,并在该股票发生分红时打印日志、发送微信消息(仅模拟交易生效),并演示如何利用分红现金进行再投资。
# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
# 开启真实价格模式(强烈建议开启,否则分红处理可能不准确)
set_option('use_real_price', True)
# 设置基准
set_benchmark('000300.XSHG')
# 设置手续费
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 定义一个全局变量,用于演示买入股票
g.security = '000001.XSHE'
# 每天开盘运行,确保有持仓
run_daily(market_open, time='09:30')
def market_open(context):
# 如果没有持仓,则买入,以便测试分红事件
if g.security not in context.portfolio.positions:
order_value(g.security, 10000)
log.info("买入股票: %s" % g.security)
# ----------------------------------------------------------------------
# 核心函数:事件回调
# ----------------------------------------------------------------------
def on_event(context, event):
"""
当账户中持仓的标的发生特定事件时被调用
"""
# 判断是否为分红送股事件
# event.name 的值为 'Dividends' 代表分红
if event.name == 'Dividends':
# 获取标的代码
security = event.security
# 获取分红具体信息
# event.dividends 是一个列表,通常包含一个字典
# bonus_pre_tax: 税前现金分红
# scale_factor: 送转股比例
div_info = event.dividends[0]
cash_div = div_info.get('bonus_pre_tax', 0)
stock_div = div_info.get('scale_factor', 0)
# 1. 构建通知消息
msg = "【分红通知】股票 {} 发生分红!现金分红: {}, 送转股比例: {}".format(
security, cash_div, stock_div
)
# 2. 打印日志
log.info(msg)
# 3. 发送微信消息 (仅在模拟交易中绑定微信后生效,回测中会被忽略)
send_message(msg)
# 4. 执行相应操作 (示例:红利再投资)
# 注意:分红的现金通常在当天收盘后或第二天到账,具体取决于交易所规则和回测机制
# 在回测中,现金通常在分红日直接可用
if cash_div > 0:
# 计算分红总金额 (每股分红 * 持仓数量)
# 注意:这里只是估算,实际资金会自动加到 available_cash 中
position = context.portfolio.positions[security]
estimated_cash = cash_div * position.total_amount
log.info("收到现金分红,尝试执行红利再投资操作...")
# 尝试用可用资金买入更多该股票
# 注意:需要判断当前可用资金是否足够买入一手
current_price = position.price
if context.portfolio.available_cash > current_price * 100:
order_value(security, context.portfolio.available_cash)
log.info("红利再投资下单完成")
else:
log.info("可用资金不足以买入一手,跳过再投资")
代码解析
on_event(context, event): 这是聚宽提供的标准回调函数。你不需要在run_daily中调用它,系统会在事件发生时自动触发。event对象:event.name: 用于判断事件类型,分红事件为'Dividends'。event.security: 发生分红的股票代码。event.dividends: 包含分红详情的列表。
send_message(msg): 这是一个非常实用的函数。在聚宽官网的模拟交易中,如果你绑定了微信,调用此函数会直接推送到你的手机微信上。在回测中调用此函数不会报错,但不会发送消息。set_option('use_real_price', True): 在处理分红和历史数据时,强烈建议开启真实价格模式(动态复权),这样账户的资金和持仓数量变化才符合真实市场的除权除息逻辑。
Q&A
Q: 回测和模拟交易中,分红到账的时间一致吗?
A: 在回测中,分红现金通常在除权除息日当天处理并反映在账户现金中。在模拟交易中,系统会根据交易所的实际公告和结算流程处理,通常也是在除权日进行除权处理,现金到账可能会遵循 T+N 的规则,具体视券商结算规则而定,但聚宽模拟盘通常简化处理,在除权日调整账户。
Q: 如果我不写 on_event 函数,分红还会自动到账吗?
A: 会的。on_event 只是一个通知机制,用于让你知道发生了分红并执行额外逻辑(如发消息、再投资)。即使不写这个函数,聚宽的交易引擎也会自动处理分红,你的持仓数量会根据送股比例增加,账户现金会根据分红金额增加(扣除税费后)。
Q: send_message 有发送限制吗?
A: 有限制。在模拟交易中,自定义消息(即通过 send_message 发送的)每个账号每天最多发送 5 条。如果超出限制,消息将发送失败。下单通知(系统自动发送的成交提醒)每天最多 60 条。
Q: 如何计算实际到手的现金分红?
A: event.dividends 中提供的是 bonus_pre_tax(税前分红)。在回测和模拟中,系统通常会按照默认税率(如 20% 或根据持仓时间计算的差异化税率,具体取决于平台当前的税费设置)自动扣除红利税。因此,账户实际增加的现金可能会少于 bonus_pre_tax * 持仓股数。