3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在使用 QMT 平台或迅投 xtdata 模块获取 A 股市场数据时,部分开发者会遇到数据异常(如出现负数或极小值),这通常是由于**整数溢出(Integer Overflow)**引起的。正如您所分析的,问题的根源在于底层数据库和 C++ 接口的数据类型定义。
在 A 股市场中,热门股票的单日成交量(Volume)或成交额(Amount)往往非常巨大。例如,某只股票单日成交量可能达到 30 亿股。
client.get_market_data3 是经过 pybind 封装的 C++ 底层函数。在 C++ 中,标准的有符号 32 位整数(int32)的最大值约为 21.47 亿($2^{31}-1$)。当成交量超过这个数值时,就会发生溢出,变成负数。xtdata 或 QMT 内部通过 np.frombuffer(b, fi) 将 C++ 传回的内存 buffer 直接映射为 Numpy 数组。如果底层 fi(数据类型描述符)被定义为 32 位整数,那么 Numpy 解析出来的数据就已经溢出了。在 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
)
既然我们无法直接修改底层 C++ 编译好的动态链接库,我们需要在 Python 业务层进行显式的类型转换和异常处理。
在获取到 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 亿),上述修复方法也会失效。此时建议:
volume 和 amount 的数据类型升级为 int64 或 double。1h 或 30m 的分钟线数据(单根 Bar 成交量较小,不易溢出),然后在 Pandas 中自行 resample 聚合为日线数据。您对 xtdata.get_market_data_ex() 源码的追踪非常准确。在量化投研中,处理这种底层数据类型的边界问题(Edge Cases)是保证策略稳健性的关键一步。在官方修复底层 buffer 类型之前,建议在策略的数据清洗模块中加入针对大市值/高换手股票的溢出检测逻辑。