问题描述
对下面这个链接中的分红送股数据有些疑问:
https://www.joinquant.com/help/api/help#Stock:上市公司分红送股(除权除息)数据
Img
以上图为例,想确定这个数据是否存在未来函数:
格力电器 report_date=2022-12-31这条分红记录在聚宽数据库中最早几号可以获取?是board_plan_pub_date(董事会预案公告日期)之后一天吗?
如果今天是 2023-05-01,是否能获取到board_plan_pub_date=2023-04-29 的board_plan_bonusnote数据?还是空值?截图中index=4这一行会提取的到吗?
如果今天是2023-05-01,但 shareholders_plan_pub_date(股东大会预案公告日期)是 2023-07-01,那么shareholders_plan_pub_date和shareholders_plan_bonusnote会怎么显示?这两列会是空值吗?
bonus_amount_rmb的数值是根据表里面的哪个日期确定的?如果今天是 2023-05-01,那么能获取到的最新的bonus_amount_rmb是哪个报告期的?依据下面哪个公告日期确定的?
board_plan_pub_date(董事会预案公告日期)
shareholders_plan_pub_date(股东大会预案公告日期)
implementation_pub_date(实施方案公告日期)
解决方案
在量化回测中,处理财务和分红数据时最核心的原则就是避免未来函数(Look-ahead Bias)。针对您对 JoinQuant(聚宽)平台 finance.STK_DIVIDEND(上市公司分红送股数据)表提出的疑问,以下是详细的解答:
1. 记录最早获取时间
问:格力电器 report_date=2022-12-31这条分红记录在聚宽数据库中最早几号可以获取?是board_plan_pub_date(董事会预案公告日期)之后一天吗?
答:是的。
在聚宽的底层数据库中,这类公告数据通常是在公告日当天的盘后或夜间进行更新。因此,在回测环境中,为了严格避免未来函数,您最早能在 board_plan_pub_date 的**下一个交易日(T+1)**的盘前或盘中安全地获取并使用这条记录。如果您在 board_plan_pub_date 当天盘中去获取,可能会因为公告尚未发布或数据库尚未落库而获取不到,或者即使获取到了也属于使用了未来数据(因为盘中可能公告还未发出)。
2. 历史日期的提取情况
问:如果今天是 2023-05-01,是否能获取到board_plan_pub_date=2023-04-29 的board_plan_bonusnote数据?还是空值?截图中index=4这一行会提取的到吗?
答:可以获取到,且不是空值。
因为 2023-05-01 晚于 2023-04-29,该董事会预案已经公告并落入数据库。只要您在查询时没有加上比 2023-04-29 更早的日期限制,您完全可以提取到 index=4 这一行的数据,并且 board_plan_bonusnote 会显示真实的预案内容。
3. 静态表与未来函数陷阱
问:如果今天是2023-05-01,但 shareholders_plan_pub_date(股东大会预案公告日期)是 2023-07-01,那么shareholders_plan_pub_date和shareholders_plan_bonusnote会怎么显示?这两列会是空值吗?
答:这取决于您是如何查询的,这里是极易产生未来函数的地方!
聚宽的 finance 模块提供的是静态快照表(即数据库当前最新的状态)。如果您在回测中(假设回测当前时间 context.current_dt 是 2023-05-01)直接查询这张表,这两列不会是空值,而是会显示 2023-07-01 的真实数据。这就产生了未来函数!
如何避免?
在使用 finance.run_query 查询时,您必须手动在 SQL 过滤条件中加入时间限制。例如:
# 错误写法(包含未来函数):
q = query(finance.STK_DIVIDEND).filter(finance.STK_DIVIDEND.code=='000651.XSHE')
# 正确写法(避免未来函数):
current_date = context.current_dt.strftime('%Y-%m-%d')
q = query(finance.STK_DIVIDEND).filter(
finance.STK_DIVIDEND.code=='000651.XSHE',
finance.STK_DIVIDEND.board_plan_pub_date <= current_date # 确保董事会预案已公告
)
即使您加了 board_plan_pub_date <= current_date,提取出来的行中 shareholders_plan_pub_date 依然会显示 2023-07-01。因此,在您的代码逻辑中,必须额外判断 shareholders_plan_pub_date <= current_date,如果为 False,则在您的策略逻辑中应当将其视为空值或不可用数据。
4. bonus_amount_rmb 的确定依据
问:bonus_amount_rmb的数值是根据表里面的哪个日期确定的?如果今天是 2023-05-01,那么能获取到的最新的bonus_amount_rmb是哪个报告期的?依据下面哪个公告日期确定的?
答:
bonus_amount_rmb(派息金额)在数据库中通常是随着公告的推进而更新的最终值(或最新预案值)。
- 依据哪个日期? 它的数值最早在
board_plan_pub_date(董事会预案)中提出,随后在shareholders_plan_pub_date(股东大会)中决议,最后在implementation_pub_date(实施公告)中确认。通常这三者的数据是一致的(除非股东大会否决或修改了预案)。 - 如果今天是 2023-05-01: 您能获取到的最新
bonus_amount_rmb是**董事会预案公告日(board_plan_pub_date)**在 2023-05-01 及之前的最新一个报告期(通常是 2022-12-31 的年报分红)。 - 安全使用建议: 为了确保您在 2023-05-01 使用的
bonus_amount_rmb是合法的,您应该以board_plan_pub_date <= '2023-05-01'作为查询条件。只要董事会预案已经发布,市场就已经知道了这个分红金额的预期,此时使用该金额进行股息率计算等操作是符合逻辑且没有未来函数的。
总结:
在使用 finance 库中的数据时,平台不会自动帮您屏蔽未来的数据。您必须在 query().filter() 中严格使用 context.current_dt 与各项 pub_date(公告日期)进行比对,才能彻底杜绝未来函数。