问题描述
使用get_fundamentals 查询 流通市值circulating_market_cap和 每股收益eps,进行股票筛选
get_fundamentals(q)时,回测收益为200%+
如果改为date为昨天:
get_fundamentals(q,date=context.previous_date)时,
回测收益大幅下降为140%
按照API文档的说法:
“当 date 和 statDate 都不传入时, 相当于使用 date 参数, date 的默认值下面会描述.
date: 查询日期, 一个字符串(格式类似'2015-10-15')或者[datetime.date]/[datetime.datetime]对象, 可以是None, 使用默认日期. 这个默认日期在回测和研究模块上有点差别:
回测模块: 默认值会随着回测日期变化而变化, 等于 context.current_dt 的前一天(实际生活中我们只能看到前一天的财报和市值数据, 所以要用前一天)
”
按照API文档我的理解是:date没传时,使用默认值为默认值会随着回测日期变化而变化,为context.current_dt 的前一天。这不就等于context.previous_date昨天吗?
使用date为昨天
get_fundamentals(q,date=context.previous_date),收益大幅下降,筛选出的股票也不一样。
不知道是不是我理解错了?还是说回测时get_fundamentals(q)没传date可能会取到未来数据?
解决方案
明确结论:不传 date 不会引入未来数据
在 JoinQuant 回测中,调用 get_fundamentals(q)(即不传 date 参数)不会引入未来数据。您观察到的回测收益差异,是由于 “前一个自然日” 与 “前一个交易日” 的区别造成的。
为什么结果会有巨大差异?
核心原因在于 context.current_dt 的前一天(默认行为)和 context.previous_date 的定义不同:
-
默认行为(不传 date):
API 文档指出,默认的date是context.current_dt的前一天(自然日)。例如,如果今天是周一,context.current_dt的前一天是周日。 -
传入
context.previous_date:
context.previous_date的定义是前一个交易日。如果今天是周一,context.previous_date是上周五。
差异产生的影响(以周末为例):
- 很多上市公司会选择在**周末(周六或周日)**发布财务报表。
- 如果您不传
date,系统在周一回测时,查询的date是周日。此时,您可以获取到周末刚刚发布的最新财报数据。这完全符合现实逻辑(周一开盘前,您确实能看到周末发布的财报),没有未来函数。 - 如果您传入
context.previous_date,系统在周一回测时,查询的date是上周五。此时,系统只会返回上周五收盘后能看到的数据,完美错过了周末发布的最新财报。
由于财务数据(如 EPS)的更新被延迟获取,您的选股逻辑在周一做出了基于旧数据的决策,从而导致了选股结果的不同和收益的大幅下降。
官方文档解读
文档中提到:
“回测模块: 默认值会随着回测日期变化而变化, 等于 context.current_dt 的前一天(实际生活中我们只能看到前一天的财报和市值数据, 所以要用前一天)”
这里的“前一天”指的是 context.current_dt - datetime.timedelta(days=1),即自然日的前一天,而不是交易日的前一天。聚宽的底层逻辑已经为您处理好了时间对齐,确保您在任何时刻调用 get_fundamentals 默认获取到的都是该时刻在现实世界中能看到的最新数据。
最佳实践建议
- 推荐做法:在回测和模拟交易中,查询财务数据和市值数据时,直接使用
get_fundamentals(q),不要手动传入date参数。聚宽的默认机制是最贴近真实交易场景的,能确保您及时获取节假日发布的财报,且绝对不会引入未来数据。 - 避免使用
context.previous_date:除非您的策略有极其特殊的逻辑(例如明确要求只使用上一个交易日的数据,刻意忽略周末信息),否则不要将date设置为context.previous_date。 - 防范未来数据:如果您仍然担心未来数据,可以在
initialize中开启防未来函数模式:set_option("avoid_future_data", True)。开启后,如果您的代码逻辑真的触碰了未来数据,系统会直接报错拦截。