🚀 AI 一键生成 qmt 策略代码

QMT 平台 get_market_data_ex 获取日线数据为何会产生未来函数?

qmt | 发布时间: 2026/5/27
以下内容由 EasyQuant 生成。

问题描述

关于get_market_data_ex获取历史数据的问题。

get_market_data_ex函数:
在获取5分钟收盘价时,取的是上一个5分钟K线的收盘价。如在9:30:00至9:34:59 取的都是昨天的收盘价,不是未来函数。
在获取30分钟收盘价时,取的是上一个5分钟K线的收盘价,也不是未来函数。
在获取日线收盘价时,取的则是当天的收盘价,这是个未来函数,会导致回测不准确。
请问为什么这样设置?为什么不把日线和分钟线的逻辑统一一下啊。

解决方案

您好!您提到的这个问题在量化回测中非常经典,属于典型的跨周期引用导致的未来函数问题。QMT 平台这样设置并非有意制造不一致,而是由底层数据存储和检索机制决定的。以下是详细的逻辑解析及解决方案:

1. 为什么会出现这种差异?

分钟线的逻辑(同周期或小跨度周期)

当您在 9:30 到 9:34 之间运行策略(假设主图是 1 分钟或 5 分钟),此时当天的第一个 5 分钟 K 线(09:35)尚未走完get_market_data_ex 在检索数据库时,只能找到已经闭合的上一根 5 分钟 K 线(即昨天的最后一根),因此它返回了昨天的收盘价。这符合真实的盘中情况,没有未来函数。

日线的逻辑(大跨度跨周期引用)

当您在盘中(如 9:35)请求 period='1d' 的数据时,底层数据库是按**日期标签(Date)**来检索的。只要当天开盘,数据库中就已经生成了“今天”这条日线记录。由于 get_market_data_ex 是一个底层的数据查询接口,它匹配到“今天”的日期标签后,会直接返回该标签下的完整数据(在回测环境中,这通常是当天的最终收盘价)。

为什么不统一逻辑?
平台没有在底层接口中强制对所有大周期数据做“盘中时间截断”处理,主要原因有两点:

  1. 性能开销:如果每次请求日线,底层都要根据当前的分钟级时间戳去动态合成一个“截至当前分钟的伪日线”,会极大消耗计算资源,拖慢回测速度。
  2. 接口通用性get_market_data_ex 被设计为“所求即所得”的静态数据查询接口。它只负责根据给定的时间范围和周期去拿数据,而不负责处理复杂的跨周期对齐逻辑。

2. 如何解决并规避未来函数?

为了保证回测的准确性,在日内(分钟级别)策略中引用日线数据时,必须在代码层面手动规避当天未走完的日线。通常的做法是:获取日线数据后,强制取上一根(即昨天的日线)。

解决方案:手动偏移获取昨天的日线

在使用 get_market_data_ex 获取日线数据时,将 count 设为 2,然后取倒数第二根数据(即昨天的收盘价);或者在获取到的 DataFrame 中使用 .shift(1)

def handlebar(ContextInfo):
    # 假设当前主图是 5 分钟周期
    if not ContextInfo.is_last_bar():
        return
    
    stock_code = '000300.SH'
    
    # 获取日线数据,count=2,确保能拿到昨天的完整数据
    daily_data = ContextInfo.get_market_data_ex(
        fields=['close'], 
        stock_code=[stock_code], 
        period='1d', 
        count=2, 
        dividend_type='front'
    )
    
    if stock_code in daily_data and len(daily_data[stock_code]) >= 2:
        # 倒数第一根 [-1] 是当天的日线(包含未来函数)
        # 倒数第二根 [-2] 是昨天的日线(安全,无未来函数)
        yesterday_close = daily_data[stock_code]['close'].iloc[-2]
        print(f"安全的昨日收盘价: {yesterday_close}")

总结

在量化开发中,“小周期引用大周期”天然容易引入未来函数。理解了 QMT 底层按标签检索数据的逻辑后,我们在编写策略时,只要养成“跨大周期引用时主动取前一周期(如 iloc[-2])”的习惯,就可以完美解决回测不准确的问题。