问题描述
如图,这段是主程序,其他程序已经一一排查没有问题了。
设置的运行时间是2021-01-01至2021-01-15,
在2021-01-04时运行,却获取到了2021-04-23的数据,
后面每一天的运行结果皆如此。
请问是get_bars()中的问题吗,比如end_dt的设置?
解决方案
是的,您遇到的问题非常典型,这正是 get_bars() 函数中 end_dt 参数设置不当导致的“未来函数”问题。
问题原因分析
在 JoinQuant 平台中,get_bars 函数用于获取历史 K 线数据。关于它的 end_dt(查询截止时间)参数,官方文档有明确的说明:
- 默认值 (
None):在回测/模拟环境下,如果不传入end_dt,它默认等于context.current_dt(即回测引擎当前运行到的逻辑时间)。 - 常见错误:很多开发者在编写策略时,习惯性地将
end_dt设置为datetime.datetime.now()。datetime.now()获取的是现实世界中的当前系统时间(比如您运行代码时的 2021-04-23),而不是回测引擎的逻辑时间(2021-01-04)。
因此,当您在 2021-01-04 的回测逻辑中,要求获取截止到现实当前时间(2021-04-23)的数据时,系统就会把未来的数据返回给您,导致策略“偷看”了未来。
解决方法
要解决这个问题,您只需要修改 get_bars 的调用方式:
1. 推荐做法:不传入 end_dt 参数
让系统自动使用回测的当前逻辑时间,这是最安全、最简洁的做法。
# 正确写法:不写 end_dt,系统默认使用 context.current_dt
df = get_bars(security, count=5, unit='1d', fields=['date', 'open', 'close'])
2. 备选做法:显式传入 context.current_dt
如果您出于某种原因必须显式指定截止时间,请务必使用 context.current_dt。
# 正确写法:显式指定为回测逻辑时间
df = get_bars(security, count=5, unit='1d', fields=['date', 'open', 'close'], end_dt=context.current_dt)
错误示例(请检查您的代码是否是这样写的):
import datetime
# 错误写法:使用了现实世界的当前时间,引入了未来数据!
df = get_bars(security, count=5, unit='1d', fields=['date', 'open', 'close'], end_dt=datetime.datetime.now())
进阶建议:开启防未来数据模式
为了防止在编写策略时无意中引入未来数据,JoinQuant 提供了一个非常实用的设置项。建议您在 initialize 函数中开启它:
def initialize(context):
# 开启避免未来数据模式
set_option("avoid_future_data", True)
开启此功能后,如果您在策略中尝试通过 API 获取 current_dt 之后的数据,系统会直接抛出 FutureDataError 异常,从而帮助您在早期发现并定位代码中的未来函数问题。