问题描述
解决方案
在 JoinQuant(聚宽)策略开发中,精确的日期和时间判断主要依赖于 context.current_dt 属性。这是一个 Python 的 standard datetime.datetime 对象,代表了回测或模拟交易当前的逻辑时间。
以下是关于如何在策略中进行精确日期和时间判断的详细指南。
核心对象:context.current_dt
无论是在 handle_data、before_trading_start 还是定时运行的函数中,context.current_dt 都是获取当前时间的最标准方式。
它包含以下常用属性:
context.current_dt.year:年context.current_dt.month:月context.current_dt.day:日context.current_dt.hour:时(24小时制)context.current_dt.minute:分context.current_dt.second:秒context.current_dt.isoweekday():星期几(1是周一,7是周日)
1. 日期判断(年、月、日、星期)
适用于日频和分钟频策略,常用于判断调仓日、节假日或特定日期逻辑。
判断特定日期
def handle_data(context, data):
# 获取当前日期字符串
current_date = context.current_dt.strftime("%Y-%m-%d")
# 判断是否为 2023年1月5日
if current_date == '2023-01-05':
log.info("到达指定日期,执行操作")
判断星期几
def handle_data(context, data):
# isoweekday() 返回 1-7,代表周一到周日
weekday = context.current_dt.isoweekday()
# 如果是周五 (5)
if weekday == 5:
log.info("今天是周五,准备收盘操作")
判断月初或年末
def handle_data(context, data):
# 判断是否是1月份
if context.current_dt.month == 1:
pass
# 判断是否是每月1号 (注意:回测中1号可能是非交易日,通常配合 get_trade_days 使用更准确)
if context.current_dt.day == 1:
pass
2. 时间判断(时、分、秒)
主要用于分钟频(Minute)或 Tick 频策略,用于控制日内交易时机,如早盘抢筹、尾盘平仓等。
判断具体时间点
def handle_data(context, data):
hour = context.current_dt.hour
minute = context.current_dt.minute
# 判断是否在 14:50 之后 (尾盘)
if hour == 14 and minute >= 50:
log.info("进入尾盘阶段")
# 判断是否在早盘 10:00 之前
if (hour == 9 and minute >= 30) or (hour < 10):
# 注意:9:30 开盘
pass
避开午休时间
A股交易时间包含 11:30 - 13:00 的午休,分钟回测中通常不会触发这段时间的 handle_data,但在编写通用逻辑时可以显式过滤。
import datetime
def handle_data(context, data):
# 获取当前时间部分
current_time = context.current_dt.time()
# 定义午休时间段
lunch_break_start = datetime.time(11, 30)
lunch_break_end = datetime.time(13, 0)
if lunch_break_start <= current_time < lunch_break_end:
return # 午休时间不操作
3. 高级应用:判断交易日逻辑
结合 get_trade_days 函数,可以判断当前是否为月初第一个交易日、月末最后一个交易日等复杂逻辑。
判断是否为当月第一个交易日
from jqdata import *
def handle_data(context, data):
# 获取当前日期
current_date = context.current_dt.date()
# 获取本月的所有交易日
# 获取范围:本月1号到下个月1号之间
start_of_month = datetime.date(current_date.year, current_date.month, 1)
# 简单处理:取当前日期往后32天内的交易日,然后筛选本月的
trade_days = get_trade_days(start_date=start_of_month, count=30)
# 过滤出本月的交易日
this_month_days = [d for d in trade_days if d.month == current_date.month]
if len(this_month_days) > 0:
first_day = this_month_days[0]
last_day = this_month_days[-1]
if current_date == first_day:
log.info("今天是本月第一个交易日")
elif current_date == last_day:
log.info("今天是本月最后一个交易日")
4. 时间差计算(Timedelta)
使用 Python 的 datetime.timedelta 计算时间间隔,例如判断持仓是否超过 N 天。
import datetime
def handle_data(context, data):
security = '000001.XSHE'
position = context.portfolio.positions[security]
# 如果有持仓
if position.total_amount > 0:
# position.init_time 是建仓时间 (datetime对象)
# 计算持仓时长
duration = context.current_dt - position.init_time
# 如果持仓超过 5 天 (5 * 24小时)
if duration > datetime.timedelta(days=5):
log.info(f"{security} 持仓已超过5天")
总结与注意事项
- 对象类型:
context.current_dt是datetime.datetime对象,context.current_dt.date()返回的是datetime.date对象。比较时类型必须一致。 - 回测频率:
- 按天回测:
handle_data中的context.current_dt通常显示为当天的09:30:00或00:00:00(取决于具体设置),无法进行分钟级的精确判断。 - 按分钟回测:
context.current_dt会随每分钟变化(如 09:31, 09:32...),可以进行精确的时间判断。
- 按天回测:
- 时区:JoinQuant 平台统一使用北京时间。
- 库导入:进行复杂时间运算时,建议在代码开头导入
import datetime。
Q&A
Q: 如何在策略中只在每天收盘前5分钟交易?
A: 可以在 initialize 中使用 run_daily(func, time='14:55') 定时运行,或者在分钟回测的 handle_data 中判断 if context.current_dt.hour == 14 and context.current_dt.minute == 55:。
Q: context.current_dt 和 datetime.datetime.now() 有什么区别?
A: context.current_dt 是回测系统的逻辑时间(即回测进行到的那个历史时刻),而 datetime.datetime.now() 是服务器的物理时间(当前现实世界的时间)。在回测中必须使用 context.current_dt,否则会引入未来函数或导致逻辑错误。
Q: 如何判断当前是否是交易时间?
A: 在分钟回测中,handle_data 只在交易时间触发,因此不需要额外判断。如果在 run_daily 或其他逻辑中需要判断,可以检查时间是否在 9:30-11:30 或 13:00-15:00 之间。