问题描述
from xtquant import xtdatacenter as xtdc
from xtquant import xtdata
xt_trader = None
acc = None
print("Hellow world!")
token = 'xxxxxx'
addr_list = '115.231.218.73:55310,115.231.218.79:55310'
port = 51615
try:
print(f"token={token} addr_list_str={addr_list}")
xtdc.set_token(token)
xtdc.set_quote_time_mode_v2(True)
# addr_list = ['115.231.218.73:55310', '115.231.218.79:55310']
addr_list = addr_list.split(',')
xtdc.set_allow_optmize_address(addr_list)
xtdc.set_index_mirror_enabled(True)
# xtdc.set_kline_mirror_enabled(True)
xtdc.set_future_realtime_mode(True)
print("Hellow world!111111111111111")
xtdc.init(False)
print("Hellow world!22222222222222222222")
# global port
xtdc.listen(port=port)
xtdata.connect(port=port)
print("Hellow world!33333333333333333")
# port += 1
except Exception as e:
print(e)
traceback.print_exc()
上面的 python 程序 运行没有问题。
但是将上面 python 程序用 Pyinstaller 成功的 打包成一个 exe 可执行程序。 运行的时候,在 xtdc.init(False)就出错了。 直接退出。产生一个 dmp 文件。
通过 windbg 分析, 如下错误。 加载 datacenter_shared.dll 出错。 (这个 dll 已经已经打包进 exe 程序了)
Unable to load image C:\Users\yckj2864\AppData\Local\Temp\onefile_67776_134070838860205280\datacenter_shared.dll, Win32 error 0n2
*** WARNING: Unable to verify timestamp for datacenter_shared.dll
麻烦帮忙看一下,是什么问题, 谢谢!
解决方案
在使用 PyInstaller 将包含 xtquant 模块的 Python 程序打包成 exe 时,在 xtdc.init(False) 处崩溃并提示 Unable to load image ... datacenter_shared.dll, Win32 error 0n2 是一个非常典型的问题。
问题原因分析
Win32 error 0n2 的含义是 “系统找不到指定的文件”。
虽然你提到 datacenter_shared.dll 已经被打包进去了,但 xtquant 底层依赖了大量的 C++ 动态链接库(DLL)以及特定的目录结构。当你使用 PyInstaller 的单文件模式(--onefile 或 -F)打包时,程序运行时会将所有文件解压到一个临时的 _MEIxxxxxx 目录中。xtdatacenter 在初始化时,可能因为以下原因导致加载失败:
- 路径定位问题:底层 C++ 代码在加载依赖 DLL 时,使用的是相对路径或当前工作目录(CWD),而不是 PyInstaller 的临时解压目录。
- 依赖缺失:
datacenter_shared.dll本身可能还依赖其他的 DLL 或配置文件,而这些文件没有被 PyInstaller 自动识别并打包进去。
解决方案
要解决这个问题,请按照以下步骤进行调整:
方案一:改用单目录模式打包(强烈推荐)
对于像 xtquant 这样包含大量底层 DLL 和复杂依赖的库,极其不建议使用单文件模式(-F)。请改用单目录模式(-D 或 --onedir)。
打包命令示例:
pyinstaller -D -w your_script.py
说明:打包后会生成一个文件夹,里面包含 exe 和所有依赖的 dll。这样底层库在加载时路径结构是完整的,通常能直接解决找不到 DLL 的问题。
方案二:在代码中动态修正工作目录(如果必须用单文件模式)
如果你必须使用单文件模式(-F),需要在 Python 脚本的最开头(在 import xtquant 之前)动态修改当前工作目录,让底层 C++ 库能正确找到解压后的临时目录:
import os
import sys
# 动态修正 PyInstaller 运行时的路径
if getattr(sys, 'frozen', False):
# 运行的是打包后的 exe
application_path = sys._MEIPASS
os.chdir(application_path) # 将工作目录切换到临时解压目录
else:
# 运行的是 Python 脚本
application_path = os.path.dirname(os.path.abspath(__file__))
# 在修正路径之后再导入 xtquant
from xtquant import xtdatacenter as xtdc
from xtquant import xtdata
import traceback
# ... 你的后续代码 ...
方案三:显式添加 xtquant 的整个包目录
PyInstaller 有时无法自动追踪到 xtquant 内部所有的非 Python 文件(如 .dll, .so, 配置文件等)。你需要在打包时通过 --add-data 参数将整个 xtquant 目录强制包含进去。
- 找到你 Python 环境中
xtquant的安装路径(例如:C:\Python39\Lib\site-packages\xtquant)。 - 使用以下命令打包(注意 Windows 下路径分隔符用分号
;):
pyinstaller -F --add-data "C:\你的Python路径\Lib\site-packages\xtquant;xtquant" your_script.py
总结建议
最稳妥的做法是结合方案二和方案三,或者直接退退一步使用方案一(单目录模式)。量化交易接口底层通常非常复杂,单目录模式不仅运行更稳定,启动速度也比单文件模式快得多(因为不需要每次运行都解压几十MB的临时文件)。