问题描述
可以多个回调函数吗?对效率有影响吗?比如一个期货品种对应一个回调函数。
解决方案
1. 可以有多个回调函数吗?
可以的。 在 QMT 平台中,您完全可以定义并使用多个回调函数。例如,在使用 ContextInfo.subscribe_quote() 订阅行情时,您可以为不同的期货品种指定不同的回调函数;或者在使用 ContextInfo.run_time() 设置定时器时,指定不同的定时回调函数。
2. 对效率有影响吗?
会有影响,具体取决于回调函数的数量、触发频率以及回调函数内部的逻辑复杂度。
如果您为每一个期货品种都单独对应一个回调函数(例如订阅了 50 个品种的 Tick 行情,对应 50 个回调函数),在行情剧烈波动时,会对效率产生以下影响:
- Python GIL(全局解释器锁)限制:QMT 的 Python 策略运行在单进程/单线程模型下(受限于 Python 的 GIL)。当多个品种的行情同时到达时,底层 C++ 会将这些回调事件排队,Python 只能串行地逐个执行这些回调函数。
- 事件积压与延迟:如果您的回调函数内部包含耗时操作(如复杂的指标计算、大量的日志打印、同步的本地文件读写等),一个回调函数的执行阻塞会导致后续排队的回调函数延迟执行。在 Tick 级别的高频行情下,这极易造成“行情处理不过来”的现象,导致信号延迟甚至错过交易时机。
- 内存与调度开销:虽然注册多个函数指针本身的内存开销不大,但频繁的 C++ 到 Python 的上下文切换和函数调用调度,在极高频并发下也会带来一定的性能损耗。
3. 最佳实践与优化建议
为了保证策略的运行效率,建议采取以下做法:
方案 A:复用同一个回调函数(推荐)
如果不同期货品种的处理逻辑是相似的,强烈建议只定义一个统一的回调函数。subscribe_quote 的回调函数会接收一个 datas 字典,您可以通过字典的 key(即合约代码)来区分当前是哪个品种触发了回调。
def on_quote(datas):
# datas 格式为 {code: data}
for code, data in datas.items():
if code == 'IF00.IF':
# 处理股指期货逻辑
pass
elif code == 'ag00.SHF':
# 处理白银期货逻辑
pass
# ...
def init(ContextInfo):
# 多个品种复用同一个回调函数
ContextInfo.subscribe_quote('IF00.IF', 'tick', 'none', on_quote)
ContextInfo.subscribe_quote('ag00.SHF', 'tick', 'none', on_quote)
方案 B:保持回调函数“轻量化”
无论您使用一个还是多个回调函数,回调函数内部的代码必须尽可能精简。只做最必要的状态更新或简单的条件判断。如果需要进行复杂的矩阵运算或耗时的分析,建议将数据缓存起来,在 handlebar 中或者通过定时器批量处理。
方案 C:合理选择订阅周期
如果您的策略不需要 Tick 级别的极高频响应,可以考虑订阅 1m(1分钟)或更高周期的行情,这样可以大幅降低回调函数的触发频率,从而减轻系统负担。