🚀 AI 一键生成 qmt 策略代码

QMT 平台 get_full_tick 获取数据时 lastClose 为 NULL 怎么办?

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

问题描述

2024/6/27 通过get_full_tick取数,600039.SH的lastClose全是NULL

早上通过get_full_tick,一直能用的数据突然报错。发现600039的lastClose全是NULL,无法处理。
tick数据提供都能出错,你们公司做的太烂了。而且取了几次,全都是这个原因。

解决方案

在量化交易实盘中,遇到接口返回 NULLNaN(缺失值)是比较常见的情况。虽然底层数据源的偶发异常会给策略运行带来困扰,但作为一个成熟的量化策略,必须在代码层面做好充分的容错处理,以应对各种极端的数据异常情况。

针对您提到的 get_full_tick 接口获取 600039.SHlastClose(昨收价)返回 NULL 的问题,以下是原因分析及代码层面的解决方案。

一、 为什么 lastClose 会返回 NULL?

  1. 交易所数据推送延迟或异常:在早盘集合竞价期间或刚开盘时,个别股票的 Tick 数据包可能存在字段缺失,导致平台接收到的原始数据中就没有昨收价。
  2. 除权除息日/停牌/复牌:如果该股票当天正好是除权除息日,或者处于停牌、临时停牌状态,数据源在处理复权基准价时可能会出现短暂的空值。
  3. 本地数据未同步:如果本地客户端的静态数据(如合约基础信息)未完全同步,也可能导致某些基础字段解析为 NULL。

二、 解决方案 1:在代码中增加容错处理(推荐)

在处理任何行情数据时,都不要假设数据是 100% 完美的。在提取 lastClose 参与计算前,必须进行 NoneNaN 的判断。

import math

def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    
    stock_code = '600039.SH'
    tick_data = ContextInfo.get_full_tick([stock_code])
    
    if stock_code in tick_data:
        stock_tick = tick_data[stock_code]
        last_close = stock_tick.get('lastClose', None)
        
        # 容错处理:判断是否为 None 或 NaN
        if last_close is None or math.isnan(last_close) or last_close == 0:
            print(f"警告:{stock_code} 的 lastClose 数据异常 ({last_close}),跳过本次计算或使用替代方案。")
            # 可以在这里 return 跳过,或者调用替代方案获取昨收价
            return
        
        # 正常逻辑
        current_price = stock_tick.get('lastPrice', 0)
        print(f"{stock_code} 最新价: {current_price}, 昨收价: {last_close}")

三、 解决方案 2:使用替代接口获取昨收价

如果 get_full_tick 中的 lastClose 缺失,但您的策略又强依赖昨收价(例如计算涨跌幅),可以通过 get_market_data_exget_history_data 获取该股票前一个交易日的收盘价作为替代。

def get_fallback_last_close(ContextInfo, stock_code):
    """
    当 tick 数据缺失昨收价时,通过日线数据获取真实的昨收价
    """
    # 获取最近2天的日线数据(包含今天和昨天)
    # 注意:如果在盘中获取,最后一条是今天的未完成日线,倒数第二条是昨天的日线
    df = ContextInfo.get_market_data_ex(
        fields=['close'], 
        stock_code=[stock_code], 
        period='1d', 
        count=2,
        dividend_type='none' # 根据需要选择复权方式
    )
    
    if stock_code in df and not df[stock_code].empty:
        # 如果当前时间已经开盘,df会有两条数据,取第一条(昨天)的收盘价
        if len(df[stock_code]) >= 2:
            return df[stock_code]['close'].iloc[-2]
        elif len(df[stock_code]) == 1:
            # 如果只有一条,说明可能是盘前,这条就是昨天的
            return df[stock_code]['close'].iloc[0]
    return None

def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    
    stock_code = '600039.SH'
    tick_data = ContextInfo.get_full_tick([stock_code])
    
    if stock_code in tick_data:
        last_close = tick_data[stock_code].get('lastClose', None)
        
        # 如果 lastClose 异常,启用替代方案
        if last_close is None or last_close == 0 or last_close != last_close: # last_close != last_close 是判断 NaN 的简便方法
            print(f"Tick数据 lastClose 异常,尝试从日线获取...")
            last_close = get_fallback_last_close(ContextInfo, stock_code)
            
        if last_close is None:
            print("彻底无法获取昨收价,放弃本次交易逻辑。")
            return
            
        print(f"最终获取到的 {stock_code} 昨收价为: {last_close}")

四、 总结建议

  1. 防御性编程:在量化实盘中,任何字段(最新价、买卖盘、昨收等)都可能因为各种不可抗力出现异常值。策略中加入 if value is None or math.isnan(value): 是必不可少的防御性编程习惯。
  2. 数据补充:请检查 QMT 客户端左上角的“数据管理”,确保该股票的基础数据和日线数据已经补充完整,这有助于底层接口正确计算和返回数据。