问题描述
解决方案
在聚宽(JoinQuant)平台中,要实现每天在不同特定时间点(如 9:30、10:30、14:50)执行不同的操作,最推荐的方法是在 initialize 函数中使用 run_daily API 进行定时任务注册。
这种方法比在 handle_data 中判断时间更高效,代码结构也更清晰。
以下是完整的策略代码示例:
# -*- coding: utf-8 -*-
import jqdata
def initialize(context):
"""
初始化函数,设定基准、复权模式,并注册定时运行函数
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 注册定时任务
# 任务1:每天 9:30 执行
run_daily(market_open_job, time='9:30')
# 任务2:每天 10:30 执行
run_daily(mid_day_job, time='10:30')
# 任务3:每天 14:50 执行
run_daily(market_close_job, time='14:50')
def market_open_job(context):
"""
9:30 执行的操作
"""
log.info("当前时间: %s,执行 9:30 的开盘操作" % context.current_dt)
# 在这里编写 9:30 需要执行的逻辑,例如获取开盘价、判断高开低开等
# 示例:打印平安银行的开盘价
current_data = get_current_data()
p = current_data['000001.XSHE'].day_open
log.info("平安银行今日开盘价: %s" % p)
def mid_day_job(context):
"""
10:30 执行的操作
"""
log.info("当前时间: %s,执行 10:30 的盘中操作" % context.current_dt)
# 在这里编写 10:30 需要执行的逻辑,例如盘中调整仓位
def market_close_job(context):
"""
14:50 执行的操作
"""
log.info("当前时间: %s,执行 14:50 的尾盘操作" % context.current_dt)
# 在这里编写 14:50 需要执行的逻辑,例如尾盘选股、清仓或调仓
# 示例:获取当前持仓并打印
positions = context.portfolio.positions
if len(positions) > 0:
log.info("当前持仓标的: %s" % list(positions.keys()))
else:
log.info("当前为空仓")
# 注意:如果使用了 run_daily 注册了所有需要的逻辑,handle_data 可以省略
# 或者仅用于处理 tick 级别的实时风控
# def handle_data(context, data):
# pass
代码解析
-
run_daily(func, time='HH:MM'):- 这是实现定时任务的核心函数。
func: 你自定义的函数名(如market_open_job),该函数必须接收context作为参数。time: 指定执行的时间字符串,格式为'HH:MM'(24小时制)。- 注意:
run_daily必须在initialize中调用。
-
执行逻辑:
- 9:30: 策略会在每天 9:30 调用
market_open_job。如果是按分钟回测,会在 9:30 这一分钟的 bar 开始时触发。 - 10:30: 策略会在每天 10:30 调用
mid_day_job。 - 14:50: 策略会在每天 14:50 调用
market_close_job。
- 9:30: 策略会在每天 9:30 调用
-
优势:
- 相比于在
handle_data中每分钟去if判断当前时间,使用run_daily效率更高,且逻辑解耦,便于维护。
- 相比于在
常见问题 (Q&A)
Q: run_daily 设置的时间如果是 9:30,在按天回测和按分钟回测中有什么区别?
A:
- 按分钟回测:会在 9:30:00 准时触发。
- 按天回测:由于按天回测只有一根日线 Bar,
run_daily设置为'9:30'时,通常会在策略逻辑上的开盘时刻(9:30)运行,但实际上是在处理当天的日线数据之前。
Q: 我可以在 run_daily 调用的函数里下单吗?
A: 可以。所有的交易函数(如 order, order_target 等)都可以在 run_daily 调用的自定义函数中使用。
Q: 如果我想在收盘后(例如 15:30)处理数据怎么办?
A: 可以使用 run_daily(func, time='15:30'),或者直接使用系统内置的回调函数 after_trading_end(context),它固定在每天交易结束后(15:30)运行。
Q: run_daily 可以设置多个相同时间的任务吗?
A: 可以。你可以写多行 run_daily(funcA, time='10:00') 和 run_daily(funcB, time='10:00'),它们会按注册顺序依次执行。