问题描述
回测或实时模拟交易时,run_daily('time =every_bar')的情况下,用check_date = context.current_dt获取到的技术分析指标是当日开盘时的数据还是未来数据?
解决方案
结论:会引入未来数据。
在 JoinQuant(聚宽)平台中,如果在盘中(如 run_daily(time='every_bar'),即每天 9:30 或每分钟运行)使用 check_date = context.current_dt 去获取日线级别的技术分析指标(如 MACD、BOLL、RSI 等),获取到的是包含当天最终收盘价计算出的未来数据,而不是当日开盘时的数据。
为什么会产生未来数据?
- 技术指标的计算基础:绝大多数日线级别的技术分析指标(如
jqlib.technical_analysis库中的函数)是基于日 K 线(包含当天的开盘价、最高价、最低价、收盘价)计算的。 - 时间错位:当策略在盘中(例如 9:30 或 10:00)运行时,当天的交易尚未结束,真实的“今日收盘价”还不存在。但如果你强行传入
check_date = context.current_dt(包含今天的日期),底层数据接口为了计算指标,会直接提取当天的最终日线数据(即下午 15:00 的收盘价)。 - 后果:你的策略在早上 9:30 就提前“偷看”了下午 15:00 的收盘结果,这会导致回测收益曲线极其完美,但在实盘或模拟盘中完全失效。
正确的解决方案
为了避免未来函数,你应该使用前一个交易日的日期来获取技术指标,或者开启平台的防未来数据机制。
方法一:使用 context.previous_date(推荐)
在盘中做决策时,只能基于昨天及昨天之前的指标数据。将 check_date 设置为 context.previous_date。
from jqlib.technical_analysis import *
def handle_data(context, data):
# 错误写法:会引入未来数据!
# macd = MACD('000001.XSHE', check_date=context.current_dt)
# 正确写法:使用前一个交易日
macd_jax, macd_ylx, macd_zcx = MACD('000001.XSHE', check_date=context.previous_date)
# 基于昨天的指标进行今天的交易决策
if macd_jax['000001.XSHE'] > 0:
order('000001.XSHE', 100)
方法二:开启避免未来数据模式
强烈建议在 initialize 函数中开启聚宽提供的防未来数据选项。开启后,如果你的代码尝试获取未来数据,程序会直接报错(抛出 FutureDataError),从而帮你及时发现 Bug。
def initialize(context):
# 开启避免未来数据模式
set_option("avoid_future_data", True)
# 建议同时开启动态复权(真实价格)模式
set_option('use_real_price', True)
方法三:如果确实需要当天的盘中指标
如果你需要基于当天的盘中实时价格计算指标(例如 10:00 的实时 MACD),你不能直接调用日线级别的技术指标库。你需要:
- 使用
get_bars或history获取分钟级别的数据(unit='1m')。 - 结合当天的分钟数据和历史的日线数据,自己编写逻辑计算实时的技术指标。