🚀 AI 一键生成 qmt 策略代码
立即体验
👉 AI 生成代码 👈

深入解析 QMT/迅投 get_market_data_ex 函数的整数溢出问题及应对策略

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

问题描述

针对昨日发现的整数溢出问题,进行解析, 可以大概总结为是迅投数据库的原因

下面,我对 xtdata.get_market_data_ex()函数进行了分析,发现内部也并没有对 int 数据类型进行显式的预处理。

xtdata.get_market_data_ex()在获取日线(period = "1d")数据时,流程大概是这样的:

xtdata.get_market_data_ex() ->

内部通过 period 分支将函数指向_get_market_data_ex_ori_221207()

if spec_period in {'1m', '5m', '15m', '30m', '60m', '1h', '1d', '1w', '1mon', '1q', '1hy', '1y'}:
return _get_market_data_ex_ori_221207(field_list, stock_list, spec_period, start_time, end_time, count, dividend_type, fill_data)


然后在_get_market_data_ex_ori_221207()函数内部最终向服务器请求的时 client.get_market_data3()

通过转向该函数的声明可以发现,该函数是经过 pybind c++ 静态编译完成的,我们无法查看原始代码和断点测试,但可以知道,返回的数据是 buffer。

ret = client.get_market_data3(field_list, stock_list, period, start_time, end_time, count, dividend_type, fill_data, 'v4', enable_read_from_local,
enable_read_from_server, debug_mode, data_dir)

result = {}
for stock, index, npdatas in ret:
data = {field: np.frombuffer(b, fi) for field, fi, b in npdatas}
result[stock] = pd.DataFrame(data=data, index=index)
return result

解决方案

QMT/迅投 get_market_data_ex 整数溢出问题解析

在使用 QMT 平台或迅投 xtdata 模块获取 A 股市场数据时,部分开发者会遇到数据异常(如出现负数或极小值),这通常是由于**整数溢出(Integer Overflow)**引起的。正如您所分析的,问题的根源在于底层数据库和 C++ 接口的数据类型定义。

1. 问题根源分析

在 A 股市场中,热门股票的单日成交量(Volume)或成交额(Amount)往往非常巨大。例如,某只股票单日成交量可能达到 30 亿股。

  • 底层 C++ 限制client.get_market_data3 是经过 pybind 封装的 C++ 底层函数。在 C++ 中,标准的有符号 32 位整数(int32)的最大值约为 21.47 亿($2^{31}-1$)。当成交量超过这个数值时,就会发生溢出,变成负数。
  • Buffer 解析机制:在 Python 端,xtdata 或 QMT 内部通过 np.frombuffer(b, fi) 将 C++ 传回的内存 buffer 直接映射为 Numpy 数组。如果底层 fi(数据类型描述符)被定义为 32 位整数,那么 Numpy 解析出来的数据就已经溢出了。

2. QMT 平台中的对应接口

在 QMT 平台的策略编辑器中,对应的接口是 ContextInfo.get_market_data_ex()。其返回的数据结构为字典,值为 pandas.DataFrame

# QMT 平台获取数据示例
data = ContextInfo.get_market_data_ex(
    ['open', 'high', 'low', 'close', 'volume', 'amount'], 
    ['000001.SZ'], 
    period='1d', 
    count=10
)

3. 解决方案与应对策略

既然我们无法直接修改底层 C++ 编译好的动态链接库,我们需要在 Python 业务层进行显式的类型转换和异常处理

方法一:强制转换为 64 位浮点数或整数

在获取到 DataFrame 后,立即对可能溢出的列(如 volume, amount)进行类型转换。虽然如果底层传过来已经是负数,直接转 int64 无法恢复真实值,但如果是无符号 32 位溢出到有符号 32 位,可以通过位运算恢复。

import pandas as pd
import numpy as np

def fix_overflow(df, columns=['volume', 'amount']):
    """
    尝试修复 32 位有符号整数溢出问题
    如果数据本应是无符号 32 位(最大 42.9 亿),但被解析为有符号 32 位导致负数
    """
    for col in columns:
        if col in df.columns:
            # 将负数转换回无符号 32 位的真实值,并存储为 64 位整数
            df[col] = np.where(df[col] < 0, df[col] + 2**32, df[col]).astype(np.int64)
    return df

def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    
    data = ContextInfo.get_market_data_ex(
        ['volume', 'amount'], ['000001.SZ'], period='1d', count=5
    )
    
    df = data.get('000001.SZ')
    if df is not None and not df.empty:
        # 处理溢出
        df_fixed = fix_overflow(df)
        print(df_fixed)

方法二:使用财务数据或扩展数据交叉验证

如果单日成交量超过了无符号 32 位的上限(约 42.9 亿),上述修复方法也会失效。此时建议:

  1. 关注官方更新:向迅投/国信 QMT 官方反馈该 Bug,促使他们在底层将 volumeamount 的数据类型升级为 int64double
  2. 降频聚合:如果日线数据溢出,可以尝试拉取 1h30m 的分钟线数据(单根 Bar 成交量较小,不易溢出),然后在 Pandas 中自行 resample 聚合为日线数据。

总结

您对 xtdata.get_market_data_ex() 源码的追踪非常准确。在量化投研中,处理这种底层数据类型的边界问题(Edge Cases)是保证策略稳健性的关键一步。在官方修复底层 buffer 类型之前,建议在策略的数据清洗模块中加入针对大市值/高换手股票的溢出检测逻辑。