ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Trade.dll与TradeX.dll调用避坑指南:通达信程序化交易接口实战

Trade.dll与TradeX.dll调用避坑指南:通达信程序化交易接口实战 简介面向量化交易与程序化交易开发者的通达信交易接口资料包整合老版Trade.dll与新一代TradeX.dll行情交易二合一接口通过TdxTradeServer将交易请求封装为HTTP REST API解决DLL直连的跨语言调用与远程接入难题适用于自动下单、策略执行、行情订阅等场景。压缩包共78个文件约61.61MB主要包含15个DLL动态库、授权文件、头文件与lib静态库以及CSharp、C、Python多语言示例源码与可执行Demo另附服务器列表、使用说明和多个子压缩包目录结构按接口版本与开发语言划分便于快速定位。已有6596人学习下载。借助TradeX.dll演示工程、CSharp API Demo和Python27 API示例开发者可以快速理解接口初始化、回调及交易流程减少从零排查成本同时附带的交易服务器与行情服务器列表可直接用于配置本地自动交易环境是进入通达信程序化交易实践的高效参考。1. Trade.dll 交易接口和 TradeX.dll 行情交易二合一接口先搞清楚你要的是哪一半做程序化交易的人早晚都会撞上“接通达信”这道坎。要在通达信环境里做自动下单、自动撤单、查持仓绕不开的就是动态库。Trade.dll 负责交易这一侧下单、撤单、查资金、查持仓都走它TradeX.dll 则在交易接口之上把股票行情接口也包了进来行情快照、逐笔成交、实时盘口和下单通道放进了同一个动态库里所以叫“行情交易二合一接口”。这篇文章不聊策略怎么写只聊一件事这套接口怎么加载、怎么调用、哪些参数能改、哪些坑你不能踩。适合的人群很明确手里有通达信客户端想写 Python 或 C 程序替代手工下单的量化初学者以及被 DLL 调用折腾过的老手。2. 把下单链路拆开看Trade.dll 的接口结构与调用方式2.1 交易接口只做一件事下单、撤单、查账户Trade.dll 在通达信体系里的定位很纯粹它不是一个行情源也不负责指标计算它只做交易柜台和你的程序之间的翻译。你的程序告诉它“我要买 100 股平安银行”它把这条指令打包成通达信认识的格式送进客户端再通过客户端的交易通道提交给券商柜台。反过来柜台返回的成交回报、持仓变动、资金余额也由它翻译回你能读的结构体。这个设计决定了你写程序时的边界Trade.dll 不会告诉你当前价格是多少你也不知道这笔单有没有成交、成交在什么价位除非主动去查。所以要写一个完整的自动交易程序通常是另一条链路先拿行情算好买卖点再调 Trade.dll 把单子发出去。很多第一次接触接口的人会误以为它带行情结果发现怎么调都拿不到盘口这不是接口坏了是职责划分就是这样。调用方式上Trade.dll 遵循 Windows 动态库的标准导出。你用 Python 的 ctypes、C 的 LoadLibrary、甚至 C# 的 P/Invoke 都能加载。难点不在于加载而在于它的函数约定多数函数要求你先登录、再取连接状态、最后才能下单顺序错了返回码全是乱的。2.2 加载 Trade.dll 并完成一笔买入最小可用 Python 代码下面这段代码是调用 Trade.dll 的最小流程假设 DLL 已经放在当前目录函数签名以你拿到的接口定义文件为准。我这里用的是常见命名实际使用时把函数名替换成你这份资源的导出表即可。import ctypes from ctypes import c_char_p, c_int, c_double, Structure # 定义交易回报结构体字段按接口文档顺序排列 class OrderResult(Structure): _fields_ [ (order_id, c_char_p), # 柜台返回的委托编号 (status, c_int), # 0成功, 非0失败 (err_msg, c_char_p), # 失败原因描述 ] # 加载动态库注意 DLL 和 Python 进程位数必须一致 trade_dll ctypes.WinDLL(./Trade.dll) # 声明函数参数类型避免 ctypes 默认按 int 传参导致地址截断 trade_dll.TDX_Login.argtypes [c_char_p, c_char_p, c_char_p] trade_dll.TDX_Login.restype c_int trade_dll.TDX_OrderBuy.argtypes [c_char_p, c_char_p, c_double, c_int] trade_dll.TDX_OrderBuy.restype OrderResult # 登录账号、密码、通讯密码有的券商不需要通讯密码 login_code trade_dll.TDX_Login(byour_account, byour_password, b) if login_code ! 0: print(f登录失败返回码 {login_code}) exit() # 买入证券代码、价格、数量 result trade_dll.TDX_OrderBuy(b000001, 10.50, 100) print(f委托编号: {result.order_id}, 状态: {result.status}, 信息: {result.err_msg})登录函数的返回码是第一个要检查的点0 表示成功非 0 是失败。这里的失败原因五花八门常见的是密码错误、账号被锁定、客户端没有保持在线状态。注意 Trade.dll 大多数实现要求通达信客户端处于登录状态它本身不维护交易会话只是帮你把消息送进去这一点和后面要讲的 TradeX.dll 有本质区别。买入下单时价格参数我用了 c_double因为大多数接口定义里价格是浮点数数量用 c_int但要注意有些券商接口对数量字段是 int32超过 2 的 31 次方的单量直接报错实际交易也用不到那个量级。真正容易翻车的是证券代码的编码方式有的接口要“000001”这种 6 位字符串有的要“SZ000001”带交易所前缀拿不准就先把两种都试一次看回执状态区分。2.3 参数与返回码看懂下单失败在哪里很多新手拿到接口后第一个疑问是“为什么我下单没反应”。绝大多数情况不是接口没加载而是返回码或状态字段没被检查。我整理了一个通用排查表这套思路对所有版本的 Trade.dll 基本适用返回值含义排查方向0调用成功查委托回报确认是否进入等待状态1001未登录或会话失效重新调用登录函数检查客户端在线状态1002证券代码格式错误确认是否缺少交易所前缀1003价格精度非法价格的小数位超过该市场最小变动单位1004可用资金不足查询资金接口核对冻结金额2001客户端未启动通达信主程序没打开DLL 找不到通道2002请求超时券商柜台繁忙重试或检查网络这里要特别提一下“未登录”这个状态。Trade.dll 的登录和通达信客户端登录不是一回事你手动打开通达信并登录了一次不等于 DLL 的会话有效。多数实现里DLL 需要独立调用一次登录函数来绑定账户。我见过有人写了一个定时任务明明客户端开着、账户也正常但每天凌晨第一次下单必然失败最后发现是客户端隔天自动断线重连DLL 的会话没有跟着恢复。解决办法是在每笔下单前检查连接状态如果失效就重新登录。提示下单前的“会话检查”不是可选项。投资有风险漏一次检查带来的可能不是报错而是静默失败——看起来下单成功了实际柜台没收到。另外返回码为 0 不代表已经成交只代表委托已进入交易通道。成交与否需要单独查询这又回到了 Trade.dll 的定位它是交易接口不是“成交确认接口”。你需要在发单后轮询查询持仓或当日委托确认状态变更。3. TradeX.dll 行情交易二合一一条链路读完行情再下单3.1 为什么需要二合一进程、内存与锁开销做交易的人都有个体验以前用 Trade.dll 下单行情用另一个接口比如通达信的行情 DLL 或者第三方推送一个程序里同时挂着两套连接。这套方案不是不能用但有两个麻烦。第一是进程和内存开销每个 DLL 都维护自己的数据缓冲区和网络连接两套叠加就是双倍占用第二是时间戳不同步行情接口拿到的 tick 时间和交易接口记录的委托时间各走各的时钟回测时回放订单还勉强能对齐实盘判断“这笔成交发生在哪一笔行情之后”就成了玄学。TradeX.dll 把行情接入和交易通道打包到同一个动态库相当于一条链路里同时维护行情快照和交易会话。它最大的变化是你可以直接在行情回调函数里拿到最新价然后调用下单函数而无需跨进程或跨接口同步数据。这种设计被一些开源交易框架采用后实盘中的延迟测试普遍比“双接口方案”低一个数量级——不是因为 TradeX.dll 跑得快而是因为你省掉了把行情数据从一个接口搬到另一个接口的时间。另外要说明的是二合一接口里的行情来源仍然是通达信行情服务器它不改变你的网络链路也不提供什么高速通道。它的优势是整合和时序一致不是帮你绕过什么限制。3.2 初始化与行情回调用 Python 拿到实时快照TradeX.dll 的调用方式和 Trade.dll 相似但多了一个“初始化-订阅-回调”的流程。它的核心机制是你传入一个回调函数指针DLL 在每次行情刷新时调用它把快照数据通过结构体指针递给你。下面是最小可用的 Python 示例import ctypes from ctypes import c_char_p, c_int, c_double, CFUNCTYPE, POINTER, Structure # 行情快照结构体字段按二合一接口文档定义 class MarketSnapshot(Structure): _fields_ [ (code, c_char_p), # 证券代码6位字符串 (price, c_double), # 最新价 (volume, c_int), # 成交量手 (bid1, c_double), # 买一价 (ask1, c_double), # 卖一价 (timestamp, c_long), # 行情时间戳 ] # 回调函数类型DLL 每次刷新时调用 CALLBACK CFUNCTYPE(None, POINTER(MarketSnapshot)) # 回调实现打印快照内容 def on_tick(snapshot_ptr): snap snapshot_ptr.contents print(f{snap.code} 最新价 {snap.price} 买一 {snap.bid1} 卖一 {snap.ask1}) # 加载 DLL 并声明函数签名 tdx ctypes.WinDLL(./TradeX.dll) tdx.TDX_Init.argtypes [c_char_p, c_char_p] tdx.TDX_Init.restype c_int tdx.TDX_Subscribe.argtypes [c_char_p, CALLBACK] tdx.TDX_Subscribe.restype c_int # 初始化传账号和行情服务器配置 init_code tdx.TDX_Init(byour_account, bconfig_path) if init_code ! 0: print(f初始化失败: {init_code}) exit() # 订阅两只股票回调里就能实时收到快照 callback CALLBACK(on_tick) tdx.TDX_Subscribe(b000001, callback) tdx.TDX_Subscribe(b600036, callback) # 保持进程运行等待回调触发 import time time.sleep(60)初始化函数的第二个参数是配置文件路径很多版本里允许传空字符串表示使用默认配置但我建议显式指定后面避坑章节会说为什么。回调函数里要特别注意DLL 的行情刷新频率通常很高如果你的回调里有耗时操作比如写数据库、打印大量日志会直接拖慢 DLL 内部的行情处理线程严重的会触发通达信客户端的“连接超时”保护。订阅函数返回的 int 代表订阅是否成功。成功了不会持续返回数据数据全部进回调失败了会返回非 0 的错误码常见原因是代码格式错误或该股票不在当前行情源覆盖范围内。这里有一个新手常犯的错误试图在回调里改订阅列表比如条件满足就取消订阅某只股票。大多数实现里在回调线程中调用订阅或取消订阅函数会导致死锁因为订阅函数要等回调线程空闲才能执行。正确做法是回调里只做数据缓存把“改订阅”的操作放到主线程。3.3 行情触发下单把回调数据接到交易函数二合一接口的最终形态是在回调里直接触发交易。理想的做法是回调里更新全局价格数据主循环或独立策略线程检查条件后就调下单函数。我用一个极简的示例说明这个接线过程# 全局变量保存最近快照 latest_price 0.0 def on_tick(snapshot_ptr): global latest_price snap snapshot_ptr.contents latest_price snap.price # 这里不直接下单只记录价格 # 在主循环里检查条件 target_price 10.00 while True: if latest_price 0 and latest_price target_price: # 价格满足调用 TradeX.dll 的交易函数下单 result tdx.TDX_OrderBuy(b000001, latest_price, 100) if result.status 0: print(f已委托价格 {latest_price}) break time.sleep(0.1)这里需要理解“为什么不在回调里直接下单”。回调线程的优先级很高且 DDL 内部可能持有锁如果你在回调里执行下单、查资金等耗时操作轻则阻塞其他股票的回调重则导致 DLL 内部状态错乱。把下单放到主循环里即使价格条件判断延迟了几十毫秒对普通日频或分钟级策略完全够用但如果你在做高频得先确认你的 TradeX.dll 是否支持回调内直接下单支持的版本通常会在接口文档里明确说明“线程安全”这四个字。注意回调里只更新状态主循环里做决策。这是二合一接口用得稳不稳的分水岭。4. 选型与混用Trade.dll 和 TradeX.dll 的边界在哪里4.1 一张表看清两套接口的分工很多人会把 Trade.dll 和 TradeX.dll 当成同一个东西的两个版本实际上它们是两个定位的接口。用一张表对比就清楚了对比维度Trade.dllTradeX.dll行情接收不提供需另接行情源内置行情订阅与快照回调交易会话复用通达信客户端通道独立维护会话下单接口只负责下单/撤单/查询下单 行情二合一适用场景已有行情源只需补交易链路从零搭建单一动态库搞定依赖客户端必须保持客户端在线初始化后对客户端依赖较低线程安全下单函数通常线程安全回调内下单需确认版本支持这个表不是绝对的不同分发版本的实现有差异但大致边界是这样。Trade.dll 像一把螺丝刀只拧螺丝你得自己准备其他工具TradeX.dll 像一套工具箱开箱能干活但每个工具的灵活性可能不如单独的专业工具。4.2 什么时候混用什么时候只用一个如果你已经有一套稳定运行的行情源比如从第三方数据服务商拿实时行情那么 Trade.dll 是更好的选择。你的程序里行情数据处理逻辑已经成熟不需要再引入一套回调机制只需要把下单这一环补上代码改动最小。反过来如果你是从零写一个自动交易程序目标就是跑通“看到价格-触发条件-自动下单”这条链路那么 TradeX.dll 更合适。它省掉了行情接口和交易接口之间的数据搬运也避免了两套连接各自为政带来的问题。但有一种情况我会建议混用你的策略需要历史分钟 K 线或日线数据做盘前计算而你的 TradeX.dll 只提供实时 tick 快照。常见做法是用另一套数据接口读历史数据做计算把结论作为交易条件交给 Trade.dll 或 TradeX.dll 去执行。这里的“混用”不是指两个 DLL 一起加载而是指“历史数据接口 实时交易接口”的组合。4.3 混用时的会话与线程约束如果确实要同时加载 Trade.dll 和 TradeX.dll有几个约束你要提前确认。第一两者能否同时连接同一个通达信账号取决于通达信客户端的“允许同一账号多端登录”设置很多券商的默认配置是不允许的双加载会出现后一个挤掉前一个的诡异现象。第二两个 DLL 的行情时间戳和交易时间戳可能来自不同的时钟判断“最新价突破后下单”这类逻辑时要统一到同一时间源。我在实际项目里见过最经典的翻车案例程序同时加载了两个 DLLTrade.dll 负责查持仓TradeX.dll 负责行情触发的自动买入。一开始很正常跑了半个月后开始偶发“下单后查不到持仓”定位了一个下午发现是账号被通达信判定为异地登录两个 DLL 的登录会话互相顶掉了。解决办法很简单——把查持仓的逻辑也迁到 TradeX.dll 里那个项目再也不敢同时加载两套接口了。5. 避坑指南五条 Trade 系列接口的真实踩坑记录5.1 同时加载两个 DLL下单偶尔乱序现象程序同时使用 Trade.dll 和 TradeX.dll快速连续下单时柜台收到的顺序和程序发送的顺序不一致明明先发的买单后成交后发的卖单先挂出去。原因两个 DLL 各自维护自己的连接通道通达信客户端和柜台之间的消息不是严格串行的。更麻烦的是两个 DLL 可能各自缓存了一部分状态你的程序以为“已经发送成功”实际上还在某个缓冲区里排队。解决不要在同一进程里双加载。把交易相关的全部操作收敛到一个 DLL 里面要么全用 Trade.dll要么全用 TradeX.dll。如果必须混用给所有下单操作加一个全局串行锁主线程统一发出指令避免两个 DLL 同时写入。5.2 行情回调里直接下单界面卡死现象第一次在 TradeX.dll 的回调函数里调用下单函数程序直接失去响应通达信客户端也同时卡住任务管理器里能看到进程 CPU 占用不高但完全无响应。原因回调函数运行在 DLL 的行情处理线程中。下单函数内部需要与客户端通信通信过程可能会等待某个锁而该锁正被行情线程持有导致死锁。我的搭档当时把这叫作“自己锁自己”。解决回调里只做三件事——解析快照、更新全局变量、置一个标志位。下单操作在主循环或独立线程中完成。后来我们把所有回调函数都改成了纯数据通道再也没出现过卡死。5.3 配置路径带中文初始化静默失败现象TradeX.dll 初始化时传入了一个带中文的配置路径返回码是 0看起来成功了但订阅行情没有任何回调。查接口文档、调日志全部正常就是不出数据。原因DLL 内部使用的是 ANSI 编码的字符串处理函数中文路径在转换时被截断或乱码初始化实际上在读取配置阶段就打断了只是这个版本的 DLL 没有向上报错。解决所有配置文件、日志文件、临时目录的路径一律使用纯英文字符最好放到盘符根目录下。这个血泪经验让我养成了一个习惯项目根目录永远是一段无空格的英文路径。5.4 64 位进程加载 32 位 DLL返回码全是 0现象Python 3.964 位加载 Trade.dll 后调用登录返回 0但后续所有下单操作返回的都是 0没有任何错误信息可委托列表里就是没有新单。原因Windows 上 64 位进程不能直接加载 32 位 DLL。ctypes 的 WinDLL 在加载时不会明确拒绝但后续调用时参数按 64 位方式传递DLL 内部用 32 位方式解释结果就是函数“成功”但实际什么都没发生。解决先把 Python 换成 32 位版本或者要求分发方提供 64 位版本的 DLL。判断方法是看 DLL 文件的机器类型用dumpbin /headers查看x86 对应 32 位x64 对应 64 位。从那以后我每次拿到新的 DLL 都会先花十秒确认位数因为这个坑实在太隐蔽。5.5 行情时间戳和交易时间戳错位现象策略判断“最新价小于 10 元就买入”回测没问题实盘却经常出现买入价高于触发价的情况。日志里显示触发时价格确实低于 10 元但成交价高出一截。原因行情 tick 的时间戳和委托回报的时间戳不是同一台服务器的时间。行情源可能有几秒钟的延迟而交易柜台的时间是相对准的。你的程序看到 9.98 元时市场实际价格可能已经是 10.02 元了。解决行情触发的下单不要使用浮点实时价在触发判断时设置一个缓冲偏移比如“价格小于 10 元且比买一价低 0.02 元才下单”。更稳妥的做法是使用限价单价格设为你愿意接受的最高买入价而不是用 tick 的最新价直接下单。6. 做一个“行情触发单”骨架把两套接口串成一套策略这节给一个可复用的触发式买入骨架集中体现 TradeX.dll 回调更新数据、Trade.dll 单独下单的协作方式。这里的思路是把行情和交易分开避免前面说过的死锁和乱序问题。# 订单管理类隔离回调线程和交易线程 class ExecutionManager: def __init__(self, trade_dll): self.trade trade_dll self.price 0.0 self.signal False def update_price(self, new_price): self.price new_price # 主线程调用检查触发条件并下单 def check_and_order(self, code, trigger_price, qty): if self.price 0 and self.price trigger_price and not self.signal: result self.trade.TDX_OrderBuy(code.encode(), self.price, qty) if result.status 0: self.signal True print(f触发买入: {code} {self.price})把 TradeX.dll 的回调只用来调用update_price更新最新价把 Trade.dll 的下单放到主循环的check_and_order里。这样回调线程永远只做赋值操作主线程控制所有交易动作两个 DLL 各司其职不在同一个线程里互相等待。验证这套骨架按三步走。第一步只跑行情部分连续观察十分钟确认回调频率稳定、快照字段解析正确第二步把下单函数换成打印日志模拟触发条件确认信号能按预期产生第三步用一手的小单量实盘跑一天重点核对委托时间、成交价和触发价之间的偏差。我每次搭新接口都会强制走完这三步不跑完不接策略。这套流程救过我太多次之前做算法交易改造时跳过第三步直接实盘当天就吃了亏损单。从那以后我给自己定了个规矩任何交易接口先跑模拟、再跑一手单、最后才放量。希望帮到你。本文还有配套的精品资源点击获取
返回列表