问题描述
基本面的经济或者企业财务数据,往往按月或者按季披露,且往往是数据是xxxx-03-31,但真正拿到数据已经是xxxx-04-22;
那么在做单因子检验时,数据处理时时点怎么选呢,T期和T+1期要怎么划分和界定呢?
解决方案
基本面量化中财务数据延迟与T期划分指南
在基本面量化和单因子检验中,财务数据的**“统计截止日”(Stat Date)与“实际披露日”(Pub Date / Watch Date)之间存在时间差是一个非常核心的问题。如果处理不当,极易引入未来函数(Look-ahead Bias)**,导致回测结果虚高但在实盘中失效。
1. 核心原则:Point-in-Time (PIT) 原则
在定义T期时,绝对不能使用财务报表的统计截止日(如03-31)作为T期。T期必须是你实际进行调仓或计算因子的交易日。在T期这个时点上,你只能使用该时点之前已经公开发布的数据。
- T期(调仓日/计算日):假设你每月月底调仓,那么4月的T期就是4月30日。
- T期可用的数据:在4月30日,你可以看到4月22日披露的一季报(截至03-31的数据)。但在4月15日作为T期时,你看不到一季报,只能使用上一年的年报或三季报数据。
- T+1期(收益观测期):如果你是月度调仓,T+1期就是5月31日。你计算的是从4月30日到5月31日的收益率,用来与4月30日计算出的因子值做相关性检验(IC分析)。
2. 在 JoinQuant 平台上的具体实现
JoinQuant 平台在底层API设计上已经充分考虑了这个问题,提供了非常便捷的规避未来函数的取数方式。
方法一:使用 get_fundamentals 的 date 参数(推荐)
在调用 get_fundamentals 时,强烈建议使用 date 参数,而不是 statDate 参数。
date参数:当你传入date='2023-04-15'时,JoinQuant 引擎会自动去数据库中查找在 2023-04-15 收盘后已经实际披露的最新一期财务数据。如果一季报在4月22日才披露,那么在4月15日取数时,系统会自动返回上一年的年报数据。这完美契合了 PIT 原则,绝对不会产生未来函数。statDate参数:如果你传入statDate='2023q1',系统会强制返回2023年一季报的数据。如果你在4月15日的逻辑中使用了这个数据,就引入了未来函数。
# 正确做法:按实际交易日(T期)取数
q = query(valuation.code, income.net_profit)
# 假设 context.current_dt 是 2023-04-15
# 引擎会自动获取 2023-04-15 当天能看到的最新财报(可能是2022年报)
df = get_fundamentals(q, date=context.current_dt)
方法二:使用 get_history_fundamentals 的 watch_date 参数
如果你需要获取历史多个季度的财务数据进行计算(例如计算TTM或者同比增长率),可以使用 get_history_fundamentals 并指定 watch_date。
# 指定观察日期 watch_date,系统只会返回该日期前(包含该日期)发布的报表数据
df = get_history_fundamentals(
security=['000001.XSHE'],
fields=[income.total_operating_revenue],
watch_date='2023-04-15',
count=4,
interval='1q'
)
3. 单因子检验的标准流程
- 确定调仓频率与T期序列:例如每个自然月的最后一个交易日作为 T_0, T_1, T_2...
- 在 T_n 期截面取数:遍历你的T期序列,在每个 T_n 日,调用
get_fundamentals(date=T_n)获取因子暴露度(Factor Exposure)。 - 计算 T_n 到 T_n+1 的远期收益(Forward Return):获取 T_n 日收盘价和 T_n+1 日收盘价,计算区间收益率。
- 对齐与检验:将 T_n 期的因子值与 T_n 到 T_n+1 期的收益率进行截面对齐,计算 Rank IC、分组收益等指标。
总结
界定T期的核心在于**“站在当前看历史”**。不要被财报的“所属期(03-31)”迷惑,永远以“交易日(04-22之后)”为时间锚点。在 JoinQuant 中,只要坚持使用 date 或 watch_date 参数进行截面取数,平台底层会自动帮你处理好披露延迟的映射,从而确保单因子检验的严谨性。