问题描述
有时候防不胜防,而且手工检查起来很麻烦。
如果用set_option('avoid_future_data',True),可能有些数据就无法使用,比如技术分析。
既然这样,我把交易时间放在收盘前一秒,比如14:59:59,并假设流动性最够大且标的能买到。
那么,是不是就可以很接近真实的交易情况?未来函数的影响也可以降到最低?
解决方案
你的思路非常敏锐,“尾盘交易”确实是量化交易中用来规避日线级别未来函数、同时尽可能利用当日行情信息的一种非常经典且有效的策略。
不过,针对你提到的 14:59:59 交易以及 avoid_future_data 的使用,在 JoinQuant 回测机制和 A 股实际交易规则中,有一些细节需要特别注意。
一、 你的思路可行吗?
结论是:大方向完全可行,但时间点建议提前到 14:50 或 14:55,而不是 14:59:59。
为什么尾盘交易能避免未来函数?
在日线级别的策略中,最常见的未来函数就是“用今天的收盘价计算出买卖信号,然后又以今天的开盘价或收盘价买入”。
如果你将策略运行频率设置为分钟级,并在每天的尾盘(例如 14:50)运行:
- 此时当天的 K 线形态(开、高、低)已经基本确定,此时的“最新价”非常接近最终的“收盘价”。
- 你用截至 14:50 的数据计算技术指标(如 MA、MACD),这属于已发生的数据,没有任何未来函数。
- 你在 14:50 下单,回测引擎会以 14:50 之后的分钟 Bar 进行撮合,逻辑严密,完全符合实盘情况。
为什么不建议用 14:59:59?
- A股交易规则限制:A股在
14:57 - 15:00是收盘集合竞价时间。在这个时间段内,订单是集中撮合的,且通常不允许撤单。如果你在14:59:59下单,实盘中极有可能因为网络延迟直接废单,或者只能以最终的收盘价成交(且不保证能成交,取决于你的报价和对手盘)。 - 回测引擎的限制:JoinQuant 的分钟回测每天执行 240 次,最后一次执行是在
14:59:00(包含 14:58-14:59 的数据)。如果你设定在14:59运行,下单后引擎只能尝试用最后一分钟(14:59-15:00)的 Bar 来撮合,这与实盘集合竞价的复杂性存在微小差异。
最佳实践:将交易时间定在 14:50 或 14:55。此时处于连续竞价阶段,流动性好,价格接近收盘价,且有足够的时间完成订单撮合。
二、 如何在 JoinQuant 中实现尾盘交易?
你可以放弃 handle_data,改用 run_daily 定时运行函数。这样可以精准控制计算信号和下单的时间。
import jqdata
def initialize(context):
set_benchmark('000300.XSHG')
set_option('use_real_price', True)
g.security = '000001.XSHE'
# 每天 14:50 运行策略逻辑
run_daily(tail_trading, time='14:50')
def tail_trading(context):
# 获取截至 14:50 的过去 5 天的日线数据(注意:这里不包含今天)
# 如果要包含今天,可以使用 get_bars 获取分钟数据合成,或者直接用当前价格近似
hist = attribute_history(g.security, 5, '1d', ['close'])
# 获取当前时刻(14:50)的最新价
current_data = get_current_data()
current_price = current_data[g.security].last_price
# 近似计算包含今天的 5 日均线
# 将前 4 天的收盘价与今天的最新价结合
ma5_approx = (hist['close'][-4:].sum() + current_price) / 5
# 产生信号并交易
if current_price > ma5_approx:
order_target_value(g.security, context.portfolio.available_cash)
log.info(f"尾盘买入 {g.security}")
else:
order_target(g.security, 0)
log.info(f"尾盘卖出 {g.security}")
三、 关于 set_option('avoid_future_data', True)
你提到开启这个选项会导致技术分析等数据无法使用。这是因为该选项的机制非常严格:它会拦截所有试图获取当前逻辑时间之后数据的 API 调用。
- 如果你在
09:30运行策略,试图用get_price获取当天的close,它会报错,因为当天的收盘价在 09:30 是未知的。 - 如果你改用尾盘交易(如 14:50),此时你获取截至 14:50 的数据,本身就不涉及未来数据,因此即使开启
avoid_future_data,只要你调用的 API 参数正确(不跨越当前时间),依然可以正常使用。
四、 总结:彻底告别未来函数的 4 个原则
- 开启真实价格回测:必须在
initialize中设置set_option('use_real_price', True),防止复权因子变动带来的价格穿越。 - 分离“信号计算”与“交易执行”:
- 模式A(盘前/开盘):用昨天的收盘价计算信号,今天
09:30以开盘价买入。 - 模式B(尾盘):今天
14:50用最新价计算信号,今天14:50以现价买入。
- 模式A(盘前/开盘):用昨天的收盘价计算信号,今天
- 慎用
history和attribute_history取当天数据:在日线回测中,这两个 API 默认不包含当天数据。如果需要盘中实时数据,请使用get_current_data()或get_bars(..., include_now=True)。 - 财务数据注意发布日期:JoinQuant 的
get_fundamentals默认按date查询,会自动返回该日期之前已发布的最新财报,平台底层已经帮你规避了财务数据的未来函数,直接放心使用即可。