3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在使用 QMT 的 xtquant.xtdata 获取 A 股指数(如上证指数、深证成指等)的历史行情时,确实经常会遇到 volume(成交量)字段出现负数或异常小的值。这通常是由于**整数溢出(Integer Overflow)**引起的。
A 股主要指数的日成交量非常巨大。例如,上证指数的日成交量经常在几十亿手(或百亿股)级别。如果底层 C++ 接口或 Pandas 转换时使用了 32 位有符号整数(int32),其最大能表示的值为 2,147,483,647。当真实成交量超过这个数值时,就会发生溢出,导致最高位符号位被改变,从而在 Python 中显示为负数。
如果底层数据是以 32 位无符号整数传输,但在某处被错误解析为 32 位有符号整数,你可以通过加上 $2^{32}$ (即 4294967296) 来还原真实的成交量。在合并 DataFrame 后,可以加上这样一步处理:
import pandas as pd
import numpy as np
# 假设 df 是你合并后的 DataFrame
# 修复 volume 溢出问题 (将负数转换回真实的无符号整数值)
df['volume'] = np.where(df['volume'] < 0, df['volume'] + 4294967296, df['volume'])
# 确保数据类型为 float64 或 int64 以容纳大数值
df['volume'] = df['volume'].astype('float64')
amount (成交额) 替代在很多量化分析场景中,对于指数而言,成交额 (amount) 往往比成交量 (volume) 更具参考价值,且 amount 字段底层通常使用双精度浮点数 (double / float64) 存储,不会发生溢出问题。如果你的策略允许,建议直接使用 amount 字段。
迅投官方在不断迭代 xtquant 库,部分新版本可能已经将底层的数据结构升级为 64 位整数 (int64 或 long long) 来彻底解决大盘指数成交量溢出的问题。建议通过 pip 检查并更新到最新版本的 xtquant:
pip install --upgrade xtquant
结合你的代码,优化后的安全版本如下:
import pandas as pd
import numpy as np
from tqdm import tqdm
from xtquant import xtdata
import time
# 获取指数列表
INDEX_LIST = xtdata.get_stock_list_in_sector('沪深指数')
ALL_CODE = INDEX_LIST
if __name__ == '__main__':
start = time.time()
# 1. 下载历史数据
print("开始下载历史数据...")
for code in tqdm(ALL_CODE):
xtdata.download_history_data(code, '1d')
# 2. 获取数据
print("开始获取并处理数据...")
df_d = xtdata.get_market_data_ex([], stock_list=ALL_CODE, period='1d')
df_l = []
for code, df in df_d.items():
if not df.empty:
# 获取合约名称
detail = xtdata.get_instrument_detail(stock_code=code)
name = detail['InstrumentName'] if detail else '未知'
# 赋值新列
df = df.assign(stock_code=code, name=name)
df_l.append(df)
if df_l:
# 3. 合并数据
final_df = pd.concat(df_l, axis=0).dropna()
# 4. 修复 volume 整数溢出问题
# 将负数加上 2^32,并转换为 float64 防止再次溢出
final_df['volume'] = np.where(
final_df['volume'] < 0,
final_df['volume'] + 4294967296,
final_df['volume']
)
final_df['volume'] = final_df['volume'].astype('float64')
print(final_df[['stock_code', 'name', 'close', 'volume', 'amount']].tail())
else:
print("未获取到有效数据")
print(f'Elapsed time: {time.time()-start:.2f} seconds')
通过上述 np.where 的处理,可以有效修正因 32 位有符号整数限制导致的指数成交量负数问题。