
TDX这几个字母在A股量化圈子里基本就是“通达信交易接口”的代名词。你随便拉一个做个人量化、又没条件上券商极速柜台的朋友问问十有八九他的自动化下单脚本里就挂着这样一条TDX通道。我身边的不少策略研究员白天写因子、晚上跑回测真到上实盘那一步很多也是先从TDX接口开始接的。这篇文章不绕弯子直接把你从“听说过TDX接口”带到“能自己写一个可用的自动下单脚本”的位置重点拆解怎么把行情信号送到交易终端再让终端帮你把单子报出去。这个题目看起来窄其实水很深。我最早接触TDX接口的时候以为它就是一个DLL文件加几个函数照着说明书调一调就行。真做起来才发现里面牵扯到连接方式、协议封装、编码转换、资金与持仓状态同步、防重下单、异常恢复任何一个环节没处理好盘中出问题就是真金白银的代价。所以这篇我打算按自己的实操经历来写从架构设计讲到代码骨架再讲到排查技巧尽量把那些文档里不会写、但实战中一定会撞上的坑都给你标出来。如果你是一个想把自己那套策略脚本从“手工点单”升级成“全自动执行”的个人交易者或者正在评估自建下单通道的可行性这篇文章应该能帮你省掉至少两三个月的摸索时间。我也把话提前说清楚TDX接口适合中小资金、策略频率不高、不追求微秒级延迟的使用场景它解决的是“能不能自动交易”的问题不是“交易速度最快”的问题。别拿它跟券商给机构用的极速柜台比定位不一样。1. 先搞懂TDX接口到底是个什么东西很多教程一上来就甩代码结果读者连TDX接口的运行原理都搞不清楚换个环境就抓瞎。我建议你动手之前先花半小时把下面这些底层逻辑过一遍后面调起Bug来会顺手很多。1.1 通达信在A股交易里的特殊位置通达信TDX是国内使用面极广的行情和交易软件绝大多数券商都有通达信版本你开户之后券商默认给你的那套PC交易客户端很可能就是通达信内核的。对量化来说通达信真正的价值在于它把交易通道开放出来了允许第三方程序通过它提供的DLL接口读取实时行情、查询账户资金和持仓、提交买卖委托、执行撤单操作。这个开放的意义非常大。在TDX之前个人交易者想写程序自动下单基本只能靠模拟按键去点击客户端界面里的“买入”“卖出”按钮脆弱得很窗口一卡顿脚本就乱套。TDX接口相当于官方给你开了一扇门你不需要去碰那些脆弱的UI元素直接跟交易内核对话稳定性好得多。但这里要理解清楚TDX只是一个“通道”不是一个完整的量化交易系统。它不负责策略逻辑、不负责仓位管理甚至不负责把信号转换成买卖指令它只干一件事——把你给它的下单指令真实地报到交易所再把成交结果回传给你。所有的智能和风控都得你在自己的程序里自己实现。1.2 TDX接口到底能拿来做什么说得具体一点通过TDX接口你能做到的事情大概可以分成四类实时读取行情数据。包括五档盘口、最新价、涨跌幅、成交量、成交额甚至分时数据。虽然通达信本身是看盘软件但程序可以通过接口拿到这些数据用于策略计算不需要去另外接行情源。查询账户状态。可以读到当前可用资金、冻结资金、持仓列表、持仓成本、浮动盈亏等信息。这是做仓位控制的基础你至少得知道账上还有多少钱才敢开新仓。提交交易委托。买入、卖出、融资买入、融券卖出、担保品买入、担保品卖出A股的基本交易类型都可以通过接口提交。价格类型上限价单、市价单、五档即成剩撤比如“买一即可成废单”这类特殊委托也能覆盖大部分场景。撤销未成交委托。当你的限价单挂出去半天没成交或者行情突变想要撤单重挂接口也有对应的撤单功能。它的边界也很清楚不能代替你思考策略不能保证成交速度不能绕过券商的合规风控。接入券商交易系统前券商会检测你的登录环境是否正常、操作是否合规不要指望通过这个接口搞出什么绕过监管的操作那是把路走窄了。1.3 TDX接口不是万能钥匙我自己见过不少刚入门的朋友对TDX抱有不切实际的期待以为挂上接口就能像机构那样做高频交易结果一顿操作下来要么被券商风控判定为异常交易要么因为接口延迟太大导致策略逻辑失真。给你打个比方TDX接口就像一把还算好用的家用厨刀切菜炒菜够用了但你非要拿它去做分子料理的精细切割工具就不匹配了。具体来说它的限制主要有三个延迟偏高。从你程序发出指令到交易所收到中间经过的链路是策略程序 → TDX DLL → 通达信客户端 → 券商柜台 → 交易所。中间每一跳都有延迟总体下来通常会在几百毫秒甚至秒级。做趋势、做日内的中低频策略问题不大做高频套利就别想了。跟单标的覆盖有限。A股的正股、基金、可转债基本都能支持但股指期货、期权这类衍生品通达信本身就不侧重接口支持也很弱交易衍生品你得用别的通道。单笔并发能力有限。同一时间如果突然下发几十笔单子通达信客户端那边处理不过来很可能会排队或者丢弃。你需要在程序里自己做限流和重试。理解了这些边界你才知道怎么在合适的场景里用这个工具。接下来的架构设计、代码实现也都是在承认这些边界的前提下展开的。2. 量化下单的整体框架怎么搭很多刚接触量化的朋友一上来就急着写下单代码这是本末倒置。下单只是最后一公里在此之前你得想清楚信号从哪儿来、怎么判断仓位、怎么处理异常、怎么保障安全。这一节我会带你走一遍完整的架构设计再对比几种常见方案。2.1 从行情信号到自动下单的完整链路一个完整的TDX量化下单系统不管策略多复杂拆到底层就是这条链路策略信号生成 → 资金仓位计算 → 下单指令构造 → TDX接口执行 → 成交回报处理 → 状态同步与风控每一步都有自己独立的任务信号生成这是你的策略核心可能是均线交叉、MACD背离、布林带突破也可能是机器学习模型的输出。它的输出是一个方向判断“买还是卖”以及目标标的。资金仓位计算根据信号方向和强度计算出具体要买多少股、卖出多少股。这里面要扣除手续费、印花税还要考虑涨停无法买、跌停无法卖等行情约束。下单指令构造把资金仓位运算结果翻译成TDX接口能接受的参数包括证券代码、买卖方向、价格、数量。这一层最容易出错的地方是价格精度和数量精度A股最小交易单位是100股科创板可以200股起买以1股为单位递增价格精度是0.01元你在转换数据时要做好校验。TDX接口执行这是实际把单子报出去的一步也是我们这次工程的核心环节。后文会详细讲。成交回报处理单子提交后可能是全部成交、部分成交也可能完全没成交。你需要读取回报更新持仓状态、更新可用资金、记录成交明细。状态同步与风控缓存里的资金、持仓必须和真实账户保持一致差一分钱都可能导致下一笔单子计算出错。还要加一些硬性风控比如“单笔亏损超过X%就停止交易”、“一天最多交易X笔”之类的熔断机制。这六层缺一不可。我见过有人只写了信号生成和下单价结果账户里没钱还在猛开仓或者一个信号触发了两次重复下单这都是因为中间几层没做扎实。2.2 方案对比为什么绕不开TDX市场上有几条路可以实现个人量化自动下单我把它们的优劣摆出来一起看你才好判断为什么很多人的落脚点最终是TDX。方案一券商官方API通道。国内部分券商提供了官方的程序化交易API如中泰证券的XTP、华泰证券的MATIC等这些接口速度快、稳定、合规干净但门槛高一方面有资金门槛要求另一方面往往要求你单独申请程序化交易权限审批周期不短。适合资金量大、有相关资质的个人或机构。方案二模拟鼠标键盘操作客户端。这条路相当于用pyautogui这类库去控制通达信客户端的界面自动输入股票代码、价格和数量。实现门槛最低但极其脆弱任何界面变化都会导致脚本失灵而且你无法读取程序化回报数据属于最原始的方案。方案三通过TDX DLL接口。这就是本篇文章的主线。它不通过界面而是直接调用通达信交易内核的DLL导出函数。兼容性好、稳定性尚可、部署简单不需要单独申请额外权限用的是你已有的券商通达信客户端身份。虽然性能不如极速柜台但对大多数中低频个人策略来说已经足够用了。方案四自己写CTP接口直连期货柜台。这是CTA期货量化里常见路子但对股票交易不适用因为A股券商柜台不通用CTP协议没法拿它来下股票订单。综合来看TDX是在“功能可用”和“实现门槛低”之间取得平衡的方案。它不完美但把个人交易者送上了自动化交易的及格线。2.3 接入方式上的关键取舍用TDX接口还有一个绕不开的取舍是用官方标准DLL还是用第三方封装库标准DLL的方式就是你去通达信官网或者券商官网下载对应的DLL文件比如历史版本里常见的 TdxTrade.dll 这一类然后在你的代码里通过动态加载去调用它。优点是身份来源清晰、兼容性有保障缺点是接口函数不够友好参数多得吓人需要花大量时间翻看文档。第三方封装库的方式是用Python社区里已经有人写好的模块他们把DLL的复杂调用封装成了友好的Python函数你不用管底层参数怎么排列组合直接传“代码、价格、数量”就能下单。优点是上手快、开发效率高缺点是封装层可能滞后于DLL版本更新出问题之后你得自己去读源码排查。我个人的建议是初学阶段用第三方封装库快速跑通全流程跑通之后再深入一层去理解DLL的原理万一封装库维护不下去你还有能力自己接。真上了生产环境你大概率要基于官方DLL书写自己的薄封装只暴露你需要的功能把复杂度藏起来。这样既保证可控性又不影响开发效率。3. 实操从零跑通一个TDX自动下单前面把底层逻辑和方案取舍都交代完了这一章开始动真格的。我会沿着我自己当初做的时候的路径带你完整走一遍从环境准备到自动下单落地的过程。3.1 准备工作清单动手之前先确认下面几样东西是不是齐了缺一样都可能让流程跑不起来。第一券商版通达信客户端。注意一定是券商定制版。通达信官方提供的免费行情版不带交易功能只有通过券商官网下载的通达信版本才有交易登录界面。如果你不知道自己券商官网有没有直接打客服电话问“你们有没有通达信版本客户端”基本都有。第二TDX交易DLL文件。正常情况下券商的安装目录里会自带交易DLL。部分券商提供单独的下载入口但很多需要你安装好通达信之后去安装目录里找。以我本地一个安装目录为例DLL文件通常放在安装根目录或 T0002 之类的子目录下文件名大致包含 Tdx 和 Trade 两个标识。你在程序里加载时直接指到这个文件路径即可。找不到的时候别慌看看券商的通达信安装目录下有没有类似 stock.dll、trade.dll 之类的文件实在拿不准就把目录截个图问客服一般都能确认。第三Python环境。推荐用 Python 3.8 以上版本我用的是 3.9。安装一下 pywin32Windows下调用DLL很常用以及其他常规依赖pandas、numpy这些做数据分析的朋友应该都有。TDX DLL 本质上还是一个Windows环境下的COM风格或导出函数风格组件所以目前最适合的平台还是Windows。第四一个模拟盘或小额实盘账号。强烈建议你从头到尾先用模拟环境练习实在没有模拟盘就用一手、两手的小额实盘去测。任何自动化下单脚本刚写好时都有可能是“定时炸弹”别拿大资金试火。我用模拟盘跑了整整两周确认没大问题了才切到实盘。3.2 连接TDX交易DLL的代码骨架这一节我照着常用方案写了一个最小可运行的例子它打通了“调用DLL→登录→查资金”的流程你先跑通这个再做下单。在写代码之前有必要先说清楚底层原理TDX DLL的工作方式类似于“你给它一个接口约定按约定传参数、拿返回值”它不是一个标准的本地HTTP服务也不是一套RESTful API而是一堆可以被动态调用的函数。绝大多数操作流程是加载DLL到进程内存。调用初始化或登录函数传入账号、密码、通信方式等参数。拿到一个会话句柄或者连接状态标记。之后所有查询、下单操作都基于这个会话去做。代码骨架我简化了许多边角逻辑重在演示流程import ctypes import os import time dll_path rC:\券商通达信路径\TdxTrade.dll tdx ctypes.WinDLL(dll_path) # 先声明你要调用的函数原型这一步非常关键 # 不同DLL版本参数可能有差异请以你所下载DLL的说明文档为准 tdx.TdxL_InitLogin.argtypes [ ctypes.c_int, # 1行情独立2登录独立 ctypes.c_int, # 行情账号类型 ctypes.c_char_p, # 行情账号 ctypes.c_char_p, # 行情密码 ctypes.c_int, # 交易账号类型 ctypes.c_char_p, # 资金账号 ctypes.c_char_p, # 交易密码 ctypes.c_char_p, # 通讯密码一般为空或固定 ctypes.c_char_p, # 券商代码或客户区号 ctypes.c_int, # 版本号 ] tdx.TdxL_InitLogin.restype ctypes.c_int # 实际调用 ret tdx.TdxL_InitLogin( 2, 1, b行情账号, b行情密码, 1, b资金账号, b交易密码, b, b券商参数, 1, ) if ret 0: print(TDX登录成功连接已建立) else: print(登录失败错误码, ret)这里有几个细节很值得新手注意必须正确声明argtypes和restype。ctypes在默认情况下会把参数视作 int如果你不显式声明用 c_char_p 传入字符串数据到DLL那边就是乱码。这个问题我当年排查了一个小时最后发现就是漏了声明。你直接照上面这种写法来能避开。编码问题。账号、密码必须转成bytes或者你干脆在字符串后面加.encode(utf-8)。有些券商的老DLL只吃GBK编码碰上这种情况你在传参前要用.encode(gbk)具体看你DLL的说明。不确定的话两个都试一次能登录成功就是对的。登录不是即时完成的。刚调用完登录函数不要立刻去查账户那个瞬间连接可能还在建立中。我习惯的做法是登录成功后主动sleep一秒再去做查询避免拿到一个还没就绪的连接状态。登录成功后第二步是查询资金目的是验证连接是否可用同时拿到账户余额用于仓位计算。不同DLL版本的函数名差异很大但思路一致传入一个结构体指针DLL往里填数据然后程序去读结构体。class AccountInfo(ctypes.Structure): _fields_ [ (fund_account, ctypes.c_char * 32), (available_money, ctypes.c_double), (frozen_money, ctypes.c_double), (total_asset, ctypes.c_double), ] account AccountInfo() ret tdx.TdxL_QueryAccountInfo(ctypes.byref(account)) if ret 0: print(可用资金, account.available_money) print(总资产, account.total_asset) else: print(资金查询失败错误码, ret)注意结构体每个字段的顺序、大小都必须跟DLL实际输出一致。数据对不上时读出来的钱数会是天方夜谭比如可用资金显示成负数几千亿这时候别慌不是你没钱了是结构体拼错了。3.3 下单流程的关键细节连接和检查都通过了接下来进入重头戏下单。下单接口的核心参数跟我前面架构里说的一致证券代码、买卖方向、价格、数量。但有几个细节是文档不会强调的这里多写几句。证券代码不要带前缀。不少人习惯把代码写成“sh600000”“sz000001”这种带交易所前缀的格式但在TDX接口的下单函数里通常只要纯数字代码“600000”“000001”就对了。DLL会根据你所在的交易市场自动判断传了前缀反而可能报错。报价精度不能错。A股股价最小变动单位是0.01元你计算下单价格时建议在传给接口之前先做一次四舍五入保留两位小数。别小看这个问题一只股票现价9.998元你因为浮点误差传了9.99成交价就跟你算的不一致策略回测和实盘就会产生偏差。数量必须按合规要求走。买入时A股主板要求100股起买之后以100股为单位递增卖出可以卖出所有零股。科创板要求至少200股超过200股之后可以按1股递增。这些约束必须在你的仓位计算模块里处理好不要指望DLL帮你拦。这里给出一个简化版的下单函数调用示例def send_order(symbol, side, price, volume): # side: 0买入, 1卖出这个映射因DLL版本而异要确认 # 准备下单参数 order_param ( symbol.encode(utf-8), # 证券代码 side, # 买卖方向 price, # 价格已做两位小数精度处理 volume, # 数量已做整手处理 ) # 调用下单函数返回一个委托编号 order_id tdx.TdxL_SendOrder(*order_param) if order_id 0: raise RuntimeError(f下单失败错误码{order_id}) print(f委托已提交编号{order_id}) return order_id下单成功后根据委托编号去轮询成交状态。这里要格外强调一个教训永远不要把“已提交委托”和“已成交”混为一谈。你的单子提交成功可能只是进入了证券商的委托队列是否成交取决于对手盘、取决于价格是否合适、取决于市场流动性。一定要单独做状态轮询把成交回报拿回来再做持仓更新和订单关闭的收尾。轮询的方式不复杂就是每隔一小段时间比如0.5秒或1秒去调用一次查询委托或查询成交的函数看返回的状态是不是“已成”直到成交或到达超时时间为止。超时之后你可以选择撤单重挂也可以保留挂单继续等。这个策略选择你自己根据策略逻辑来定。3.4 资金与持仓管理资金和持仓管理是整个系统里最无趣但最要命的部分。太多人只关注信号准不准、下单快不快结果栽在资金管理上。为什么这么重要因为你的策略代码运行在本地你在本地维护的”缓存持仓“和券商账户里的“真实持仓”必须保持严格一致。哪怕一笔单子漏了更新状态后面的开仓、平仓计算就全乱了可能出现“你以为你满仓实际账户还有钱”或者反过来“你以为你很轻仓实际上仓位已经很重”的灾难性状况。我的做法是建立一个sync模块每次下单前和每次成交回报后都强制从TDX接口拉取一次真实的可用资金和持仓数据跟本地缓存做比对。一旦发现本地缓存和真实账户对不上立刻停止当前策略的所有后续操作打印告警等人工介入。宁可不交易也不能带病运行。具体实现上你可以在内存里维护一个字典key是证券代码value是持仓数量。每次收到成交回报先更新这个字典然后调一次接口校验。偶尔接口会有延迟校验不通过时可以加入“再读两次、间隔一秒”的容错逻辑三次都不一致才告警。另外还有一个每家券商都会有的限制——T1制度。今天买入的股票当天不能卖只有持仓里显示为“可用数量”的部分才能卖出。所以你的卖出数量约束不应该用“持仓总量”而应该用“可用数量”去计算。曾经有人写程序时忘了这一层程序把今天刚买到的股票算进了可卖持仓结果下单时报了个“可用数量不足”的错误还好报错拦住了没造成更大问题。4. 踩坑实录与排查思路这个章节是我最想分享的内容。下面这些坑我几乎是一个一个踩过来的。列成速查表你再遇到类似问题时不至于抓瞎。4.1 连接失败到底是怎么回事TDX登录失败是最常见的问题错误码千奇百怪网上答案又杂。根据我的经验先按下面的顺序排查第一步确认DLL加载成功。代码里加载DLL之后加一行打印确认返回的句柄不是空。出现空句柄大概率是DLL文件路径不对或者缺少VC运行库装上对应版本的运行库就好。第二步确认账号类型参数。TDX登录通常要把“行情账号类型”和“交易账号类型”区分开二者可能用不同的取值。有的DLL里资金账号类型是0有的是1你要去查自己这份DLL的说明文档。一个简单的验证办法是用通达信客户端手动登录成功一次把登录日志里记录的参数值记录下来然后用同样的值去调DLL。第三步确认地区号和通信密码。有些券商的通达信版本需要额外传入客户区号比如你的营业部在某个城市DLL要根据区号找到对应的营业部服务器。通信密码大多数情况下为空但个别老账户开通时设置过。这两项很容易被忽略也是登录失败的常见元凶。第四步检查交易时间。这个看起来像废话但真的会有人犯。非交易时段比如凌晨三点TDX接口登录成功率是很低的因为券商服务器凌晨多半在做清算和维护。你要是周末跑通测试登录失败是正常现象别怀疑代码。4.2 订单状态查询遇上的坑下单功能跑通之后紧接着遇到的是订单状态查询和成交回报的问题。我有一个阶段明明看到客户端里面单子已经成交了程序里却一直查不到。排查之后发现原因是查询接口用错了。TDX接口里“查询当日委托”和“查询当日成交”是两类函数。委托查询只能看到“已报、部成、已成、已撤”这些委托维度状态成交查询才返回每笔成交的价格和数量。有些封装库把这两个函数混在一起封装你在使用时要注意区分。另外一个坑是DLL的缓存机制。部分TDX DLL在会话内会对查询结果做缓存你频繁调用时拿到的是旧数据并不是最新的。解决办法是每次查询返回后稍作等待0.2到0.5秒或者显式调用一个清理缓存的函数。时间节奏需要根据你自己的DLL版本去试。4.3 重复下单的隐患重复下单这个问题每闲聊到一个做量化的朋友几乎都能讲出一次惨痛经历。信号触发一次结果下了两笔单等发现时仓位已经翻倍。这其实是并发控制不到位导致的。TDX查询/下单函数不是线程安全的。如果你用多线程去跑策略比如五个信号同时触发五个线程同时调用下单函数DLL内部可能会出问题导致函数返回异常或者更糟——同一笔信号逻辑被重复执行。我的建议是给所有交易操作加一把全局线程锁保证同一时间只有一个线程在执行“查询-计算-下单”这整个事务。虽然会牺牲一定并发度但换来的是确定性。对于TDX这种秒级延迟的通道来说牺牲这一点并发完全值得。还可以在自己的代码里做一层“订单去重”。每一个策略信号带上唯一ID在下单前检查一下这个信号的ID是否已经执行过下单动作如果执行过直接忽略。这既是并发安全的底线也防止策略逻辑偶发重复触发。加上这层之后我几乎没有再遇到重复下单的问题。4.4 编码问题的诡异表现编码问题是最隐蔽的一类坑。它的表现很诡异登录正常查询也正常但下单时报成交数量错误或者股票代码乱码。原因很简单有些老版本的TDX DLL内部使用的是GBK编码而你现在Python字符串默认是UTF-8直接传过去就乱套了。这个乱码往往不是“完全乱”只是字节错位导致DLL解析到错误的数字。怎么排查你在下单前把要传给DLL的每一字节都打印出来看一遍。如果发现某些中文字段比如证券名称变成乱码就基本可以确定是编码问题。解决办法是在传参前统一用.encode(gbk)并且确保你的代码文件保存编码、终端输出的编码不会二次干扰到实际传输的字节流。更稳妥的做法是尽量避免传中文参数。能用代码、能用数字表达的就不要传名称。代码传真值价格传数值方向传数值这样能大幅降低编码问题的发生概率。4.5 实盘前一定要做的几件事最后这个部分当作送给你的一份清单。程序写完模拟盘跑完准备上实盘之前请逐条确认小额先行。第一次上实盘只用最小手数先跑三天确认没有技术问题再逐步放量。确认交易时间段。你的程序必须知道A股的连续竞价时间段9:30-11:30和13:00-15:00避免在集合竞价阶段盲目下单。加好总开关。程序里必须有一个“一键熔断”开关可以是命令行输入、也可以是监控一个本地文件文件存在就停止交易确保盘中随时能手动终止自动化操作。做好断线恢复。TDX接口不稳定是常态你要处理断线后自动重连并且断线期间暂时停止下单等连接恢复后再继续。记录完整日志。每次下单、每次回报、每次错误都要记录到本地文件。真出了问题日志是你唯一能复盘的东西。我个人在踩过几次坑之后现在做每套新的交易脚本时都会把这套清单当成默认强制流程。很多人说量化的最终瓶颈在策略但以我自己的体验来看在个人量化这条路上活下来比赚得多更重要。这个内容的下一步还可以往两个方向扩展一是把TDX通道封成自己的一套底层库以后所有策略复用二是给这套系统加上更完善的风控面板做成一个真正的轻量级交易工作站。都是后话了先把这一套跑稳你就已经超过了绝大多数停留在手工下单阶段的个人玩家。