问题描述
解决方案
在 QMT 策略中设置每天固定时间运行的定时任务,主要使用 ContextInfo.run_time 函数来实现。该函数允许你注册一个回调函数,按照设定的周期和起始时间自动执行。
核心函数说明
ContextInfo.run_time(funcName, period, startTime, market)
- funcName (str): 回调函数的名称(字符串格式)。
- period (str): 运行周期。例如
"1d"表示每天运行一次,"5nSecond"表示每5秒运行一次。 - startTime (str): 定时器的首次启动时间,格式为
"YYYY-MM-DD HH:MM:SS"。为了确保任务能在当天的指定时间运行,通常将日期设置为过去的一个时间点,时间设置为你想要的时刻。 - market (str): 市场代码(如
"SH"或"SZ"),通常用于驱动定时器,一般填"SH"即可。
代码实现
以下是一个完整的策略示例。该策略设定了一个名为 daily_task 的函数,并在每天的 14:55:00(收盘前5分钟)自动执行。
# -*- coding: gbk -*-
def init(ContextInfo):
"""
初始化函数
"""
print("策略初始化...")
# 设定定时任务
# 参数1: 回调函数名 "on_scheduled_run"
# 参数2: 周期 "1d" 表示每天执行一次
# 参数3: 起始时间 "2023-01-01 14:55:00"。
# 注意:日期部分建议设置为过去的时间,时间部分设置为你希望每天运行的时刻。
# 参数4: 市场代码 "SH"
ContextInfo.run_time("on_scheduled_run", "1d", "2023-01-01 14:55:00", "SH")
def on_scheduled_run(ContextInfo):
"""
定时任务回调函数
当到达设定时间时,平台会自动调用此函数
"""
print("=" * 30)
print("触发定时任务!当前时间为每天的 14:55:00")
# 在这里编写你的定时逻辑,例如:
# 1. 检查持仓
# 2. 尾盘选股
# 3. 调整仓位
# 示例:打印当前账号ID
print(f"当前交易账号: {ContextInfo.accid}")
print("=" * 30)
def handlebar(ContextInfo):
"""
K线/Tick驱动函数
"""
# 定时任务由 run_time 独立驱动,不需要在 handlebar 中写逻辑
pass
注意事项
- 回测模式无效:
ContextInfo.run_time函数仅在实盘或模拟交易(运行模式)下生效。在回测模式下,该函数不会被触发。如果需要在回测中模拟定时任务,需要在handlebar中判断ContextInfo.barpos对应的时间戳。 - Start Time 设置:
startTime参数决定了定时器的“相位”。如果你希望每天 09:30 执行,日期可以填过去的任意一天(如2020-01-01),只要时间部分是09:30:00即可。平台会根据这个基准时间和周期(1d)计算出下一次触发的时间。 - 函数定义:回调函数(如上例中的
on_scheduled_run)必须在策略代码中定义,且必须接收ContextInfo作为参数。 - 多任务设置:如果需要多个不同时间的定时任务,可以在
init中多次调用ContextInfo.run_time,指向不同的回调函数或设置不同的startTime。
Q&A
Q: 如果我想每隔 5 分钟运行一次任务,而不是每天固定时间,该怎么改?
A: 将 period 参数修改为 "5nMinute" 即可。例如:ContextInfo.run_time("my_task", "5nMinute", "2023-01-01 09:30:00", "SH")。
Q: 定时任务触发时,能下单交易吗?
A: 可以。在回调函数中可以直接调用 passorder、order_shares 等交易函数。但请注意,如果是非交易时间(如中午休市或收盘后),下单可能会被柜台拒单或暂存。
Q: 为什么我在回测里看不到定时任务的日志?
A: 如注意事项所述,run_time 不支持回测。回测中请在 handlebar 函数里通过 ContextInfo.get_bar_timetag(ContextInfo.barpos) 获取当前 K 线时间,并将其转换为日期字符串进行比对来实现类似逻辑。