问题描述
解决方案
在PTrade策略中,您可以非常方便地通过在 initialize 函数中多次调用 run_daily 或 run_interval 函数来实现同时运行多个不同频率的定时任务。
核心实现逻辑
- 日内不同时刻的任务:多次调用
run_daily(context, func, time='HH:MM'),设置不同的time参数。 - 高频/秒级间隔任务:多次调用
run_interval(context, func, seconds=N),设置不同的seconds间隔(注意:run_interval仅在交易/实盘模式下可用,回测中不可用)。 - 混合使用:可以同时结合使用上述两种函数。
策略代码示例
以下是一个完整的策略示例,展示了如何设置:
- 每天上午 10:30 执行一次选股。
- 每天下午 14:50 执行一次尾盘处理。
- (仅实盘)每隔 10 秒执行一次行情监控。
def initialize(context):
"""
初始化函数,设置股票池和定时任务
"""
# 1. 设置股票池(示例:平安银行)
g.security = '000001.SZ'
set_universe(g.security)
# 2. 设置定时任务 A:每天上午 10:30 运行
# 适用于:盘中选股、早盘数据更新
run_daily(context, morning_task, time='10:30')
# 3. 设置定时任务 B:每天下午 14:50 运行
# 适用于:尾盘清仓、收盘前信号确认
run_daily(context, tail_task, time='14:50')
# 4. 设置定时任务 C:每隔 10 秒运行一次
# 注意:run_interval 仅在【交易/实盘】模式下生效,回测中不会触发
# 适用于:高频监控、条件单触发、风控扫描
run_interval(context, monitor_task, seconds=10)
def handle_data(context, data):
"""
必须实现的函数,按策略频率(分钟或日线)运行
"""
# 这里是默认的主循环,可以留空,也可以写常规逻辑
pass
def morning_task(context):
"""
上午 10:30 执行的任务
"""
log.info("【定时任务A】现在是上午 10:30,开始执行早盘选股逻辑...")
# 示例逻辑:获取当前价格
# snapshot = get_snapshot(g.security) # 注意:回测不支持get_snapshot
# log.info(snapshot)
def tail_task(context):
"""
下午 14:50 执行的任务
"""
log.info("【定时任务B】现在是下午 14:50,开始执行尾盘处理逻辑...")
# 示例逻辑:检查持仓
position = get_position(g.security)
if position.amount > 0:
log.info("当前持有 %s,数量:%s" % (g.security, position.amount))
def monitor_task(context):
"""
每 10 秒执行一次的任务(仅实盘有效)
"""
# 注意:此函数在回测中不会被调用
log.info("【定时任务C】高频监控运行中...")
# 实盘中获取快照示例
# snapshot = get_snapshot(g.security)
# if snapshot:
# current_price = snapshot[g.security].get('last_px')
# log.info("当前 %s 价格: %s" % (g.security, current_price))
注意事项
- 回测与实盘的区别:
run_daily:在回测和实盘中均可使用。但在日线级别回测中,无论time设置为几点,它通常都会在当天的 bar 结束时(即 15:00)统一执行逻辑(除非是分钟级回测)。run_interval:仅在交易(实盘)模块可用。如果在回测中使用,该代码不会报错,但对应的函数永远不会被触发。
- 并发与线程:
run_interval在实盘中是以多线程方式并行运行的。如果多个定时任务操作同一个全局变量(如g.count),需要注意线程安全问题,或者确保逻辑互不冲突。
- 时间精度:
run_interval的最小间隔为 3秒。如果设置小于3秒,系统默认按3秒执行。
- 函数定义:
- 被调用的任务函数(如上例中的
morning_task)必须接收context作为第一个参数。
- 被调用的任务函数(如上例中的
Q&A
Q: 在回测模式下,我设置了 run_daily(..., time='10:00'),为什么日志显示是在 15:00 打印的?
A: 这取决于您的回测频率。如果您选择的是日线级别回测,策略每天只运行一次(通常是收盘后),因此所有 run_daily 任务都会在那个时间点触发。如果您选择分钟级别回测,run_daily 就会在指定的 10:00 准时触发。
Q: run_interval 可以设置多个不同的间隔吗?
A: 可以。例如,您可以设置一个 run_interval(..., seconds=3) 用于极速监控价格,同时设置另一个 run_interval(..., seconds=60) 用于每分钟检查一次账户风控状态。它们会独立运行。
Q: 如果定时任务执行时间超过了间隔时间怎么办?
A: 对于 run_interval,如果函数逻辑非常复杂,执行时间超过了设定的间隔(例如设定3秒,但执行了5秒),下一次触发可能会顺延或在当前执行完毕后立即触发。建议保持 run_interval 中的逻辑尽可能轻量高效。