问题描述
我发现不用『handle_data』函数,直接使用定时函数(类似如下代码),也可以进行回测,请问两者有何不同?
# 开盘前运行
run_daily(before_market_open, time='before_open', reference_security='000300.XSHG')
# 开盘时运行
run_daily(market_open, time='open', reference_security='000300.XSHG')
# 收盘后运行
run_daily(after_market_close, time='after_close', reference_security='000300.XSHG')
解决方案
在 JoinQuant(聚宽)平台上,回测并不是必须实现 handle_data 函数的。实际上,官方更推荐使用 run_daily 等定时函数来构建策略。
以下是 handle_data 与 run_daily 等定时函数的详细区别和使用建议:
1. 触发机制的不同
-
handle_data(context, data):- 这是一个被动触发的函数,它的执行频率完全依赖于你在回测页面设置的“回测频率”(按天或按分钟)。
- 如果按天回测,它每天在交易时间(通常是 9:30)调用一次;如果按分钟回测,它在交易时间内每分钟调用一次。
- 注意:Tick 频率的回测不支持
handle_data函数。
-
run_daily/run_weekly/run_monthly:- 这是主动设定的定时任务函数,具有极高的灵活性。
- 你可以精确指定函数在每天的哪个具体时间点执行(例如
time='09:30'、time='14:50'),甚至可以指定在开盘前(如09:00)或收盘后(如15:30)执行。 - 它允许你在同一个策略中注册多个不同的函数在不同的时间点运行,非常适合复杂的业务逻辑(如早盘选股、盘中风控、尾盘调仓)。
2. 参数传递的不同
-
handle_data(context, data):- 接收两个参数:
context(账户和上下文信息)和data(当前单位时间的标的数据)。 - 历史遗留问题:
data参数是为了早期兼容性保留的,官方文档指出,为了加速,data里面的数据是按需获取的。现在更推荐使用get_price、history、get_current_data等全局 API 来获取数据。
- 接收两个参数:
-
run_daily(func, time, ...)指定的回调函数:- 你自定义的回调函数(如你代码中的
market_open)只能有一个参数context。 - 获取数据时,直接在函数体内使用
history()、get_current_data()等 API 即可,代码更加清晰。
- 你自定义的回调函数(如你代码中的
3. 官方最佳实践建议
根据 JoinQuant 官方 API 文档的建议:
- 推荐使用
run_daily:为了避免换算错误和逻辑混乱,建议设置time为具体的时间(例如:time='9:30'),并使用run_daily等定时函数。 - 不要混用:一个策略中尽量不要同时使用
run_daily和handle_data,更不能写出run_daily(handle_data, "xx:xx")这样的代码。 - 可选实现:
initialize/before_trading_start/after_trading_end/handle_data都是可选的。如果不是必须的,不要实现这些函数,一个空函数反而会降低运行速度。
总结
你发现的用法是非常正确且现代的。使用 run_daily 配合自定义函数(如 before_market_open, market_open, after_market_close)能够让你的策略时间轴更加清晰,逻辑解耦得更好,这也是目前量化策略编写的主流和推荐方式。完全可以抛弃 handle_data。