问题描述
在处理财务数据时,我们都知道常规做法都是采用公告日期作为数据变更的节点,而不是采用报告日期。这是一个典型的未来函数,当然这也是最常规的处理方式。
接下来的一个问题,才是关键所在:财报数据出现了修正财务报表,怎么处理?这是最近一个初学的小伙伴问我的问题、也是他代码中出现的bug,我觉得对于很多初学都比较细节、重要而且可能忽略的问题,所以我这里专门写一篇文章,来说说这种类似问题的处理办法,请一定耐心看完。

问题解析
为什么这么数据比较麻烦呢?主要是这是需要修正过去的数据,如果不注意,这就很容易在你的交易系统回测的时候,出现未来的数据,这是很多小伙伴可能没有注意到的问题。
原则:
我们在交易系统里,严禁修改过去的任何值。除非把昨天的值全部shift到公告日期使用,过去的数值保持不变。
场景与解决办法
场景一:计算ROE_DELTA
我们在之前的某些策略里面,调用了过去5个季度的q_roe(季度roe)作为基础数据。
类似于这样的因子,如果今天晚上出现了修正财务报表, 应该如何处理?
按照我们之前的处理原则:这样的roe_delta因子就需要完成下面的两步:
1、关于pub_date,公告日期做groupoby,这是一个处理细节:groupby时仅仅需要关于pub_date去计算数据,因为只有新的报告产生才会需要更新数据.
2、读取相应报告期的的所有roe数据,也就是rpt_date在需要使用的财报里面的。
3、剔除pub_date大于当前日期的所有财报数据。
4、使用rpt_date、pub_date从小到排序。
5、关于rpt_date去重复,然后取last数据。
6、使用trade_date,对上面的groupby后的数据,做reindex,然后fillna即可。
具体代码:
# index为pub_date ,values 为roe_delta
fina_group= df_fina_need_group.groupby('pub_date',group_keys=False).apply(self.calculate_fina_indicator_pub,
df_fina_one_stock = df_fina,
include_groups=False)
# ============= 具体处理函数部分代码
_pub_date = group.name # 获取公告时间
df_fina_pub = df_fina_one_stock[df_fina_one_stock['pub_date'] <= _pub_date].copy() # 筛选出已发布的公告。
# 去重复,先排序,如果有最新的报告就取最新的
df_fina_pub.sort_values(['rpt_date', 'pub_date'], ascending=[True, True], inplace=True, ignore_index=True)
# 关于发布日期是正序,所以取值为最后一条
df_fina_pub.drop_duplicates(subset=['rpt_date'], keep='last', inplace=True, ignore_index=True)
场景二:计算TTM、同比、环比数据
这里的步骤其实跟上面的保持一致,只是需要根据报告的日期,精准的找到之前的4个季度的财报数据即可.
总结:需要使用过去数据计算的,都可以使用上一步的步骤与代码
场景三:直接使用过去的数据
这种最简单,但是也是最容易出错。这里不能直接在处理完groupby之后,直接使用shift去调用。
这是有可能引入未来函数的
比如:今天收盘发了修正报告,那么按说今天的数据在调用过去的财报数据时,需要采用最新的财报数据。但是昨天的数据不能采用最新的财报数据,只能采用截止到昨天所能使用到的历史数据。
如果你将这个数据今天修正了,那么在回测的时候就只能使用最新数据,这就是未来函数。
所以,准备的做法,如果你的因子里面需要用到过去报告其的数据,那么就必须重新按照一个新的因子,按照第一种情形去计算、保存、调用数据。
总结
本期的内容比较简单,源自于一个小伙伴的提问、代码bug问题。但是非常关键、也是很多初学者容易出错的地方,属于未来函数范畴。
而且我发现很多现成的因子库,没有做我们类似的处理,这里肯定是有问题,具体你使用的平台有没有类似的处理,你需要仔细看他的开发文档以及调用函数。
比如:你需要调用上一期的财报数据时,没有调用日期参数啥,那基本上有问题。按道理来讲,财报数据,每一个报告期每一个因子数据,都应该是一个时序数列,因为未来他有可能出现修正,也就是时序上会变化。如果调用时都没有时间参数,那就需要你仔细审查了。


