问题描述
enddate = (context.current_dt - datetime.timedelta(days=1)).date()
roll_days = 90
stdev_up = 2
stdev_dn = 2
table = finance.STK_ML_QUOTA
q = query(
table.day, func.sum(table.buy_amount - table.sell_amount).label('net_amount')
).filter(
table.link_id.in(['310001', '310002']),
table.day >= end_date - datetime.timedelta(days=2 * roll_days),
table.day < = end_date
).group_by(
table.day
).order_by(table.day)
以上为代码一
end_date = context.current_dt.date()
roll_days = 90
stdev_up = 2
stdev_dn = 2
table = finance.STK_ML_QUOTA
q = query(
table.day, func.sum(table.buy_amount - table.sell_amount).label('net_amount')
).filter(
table.link_id.in_(['310001', '310002']),
table.day >= end_date - datetime.timedelta(days=2 * roll_days),
table.day < = end_date
).group_by(
table.day
).order_by(table.day)
以上代码二
以上两个代码片段只是在第一句有不同,代码一和代码二回测收益差了很多很多,
end_date 哪种写法可以确认为交易时/模拟时/回测时的前一交易日?
主要是为了避免未来函数的影响。
解决方案
核心解答:推荐使用 context.previous_date
在 JoinQuant 平台中,为了严谨地获取前一交易日并彻底避免未来函数,您提供的代码一和代码二都不是最佳选择。官方 API 提供了一个专门的属性来解决这个问题:context.previous_date。
为什么您的两段代码回测结果差异巨大?
您查询的表 finance.STK_ML_QUOTA 是沪深港通的日度额度数据。这类日频统计数据通常在当天收盘后才能确定最终数值。
-
代码二 (
end_date = context.current_dt.date()) 引入了未来函数:
如果您在盘中(例如 09:30)运行策略,context.current_dt.date()获取的是当天的日期。此时您去数据库查询当天的汇总数据,在回测引擎中可能会提前拿到当天收盘后才有的数据。这就引入了未来函数,导致回测收益看起来异常好,但实盘中根本无法实现。 -
代码一 (
end_date = (context.current_dt - datetime.timedelta(days=1)).date()) 避免了未来函数,但不严谨:
减去 1 天确实避免了拿到当天的数据,但它只是自然日的前一天。如果今天是周一,减去 1 天是周日(非交易日),这可能会导致您在某些逻辑计算或数据对齐时出现空值或错误。
正确的写法
为了确保获取的是前一个有效的交易日,请直接使用 context.previous_date。修改后的代码如下:
# 正确写法:使用 context.previous_date 获取前一交易日
end_date = context.previous_date
roll_days = 90
stdev_up = 2
stdev_dn = 2
table = finance.STK_ML_QUOTA
q = query(
table.day, func.sum(table.buy_amount - table.sell_amount).label('net_amount')
).filter(
table.link_id.in_(['310001', '310002']),
table.day >= end_date - datetime.timedelta(days=2 * roll_days),
table.day <= end_date
).group_by(
table.day
).order_by(table.day)
进阶建议:开启避免未来函数模式
为了在编写策略时更好地防范未来函数,建议在 initialize 函数中开启 JoinQuant 提供的避免未来数据选项:
def initialize(context):
# 开启避免未来数据模式
set_option("avoid_future_data", True)
开启此功能后,如果您在策略中尝试通过 API 获取未来数据(例如在盘中获取当天的收盘价或盘后财务数据),系统会抛出 FutureDataError 异常或自动剔除未来数据,从而帮助您及时发现代码中的漏洞。