通达信Python自动化交易:基于Windows消息机制的实操方案 简介这是一套基于Python开发的华泰证券通达信客户端股票自动化交易工具面向量化初学者、个人投资者及桌面自动化实践者解决手动盯盘下单效率低、响应滞后的问题。工具支持同时监控5只股票依据价格与时间双重条件自动触发买卖指令单次下单耗时低于2秒适配Windows 10 64位环境亦兼容32位Python依赖pywin32与tushare实现界面交互与行情获取。资源包为166KB的ZIP压缩文件共含12个文件包括核心脚本pyautotrade.pyw、封装Windows API的winguiauto.py、说明文档README.md、LICENSE协议文件以及8张界面配置截图setting1.png至setting7.png、trading.png直观呈现操作路径与交易界面状态。目前已有3227人学习下载读者可直接部署运行获得开箱即用的条件单框架、通达信GUI自动化控制逻辑、股票数量整百截断处理机制及稳定低延迟的下单流程实现。1. pyautotrade_tdx不是“全自动印钞机”而是把通达信行情交易指令捏进Python黑匣子的实操工具链你见过凌晨三点还在跑回测、却卡在「下单按钮点不动」的量化新手吗我见过——A同学用三套不同框架试了两周最后发现不是策略不行是根本没打通「Python脚本 → 通达信客户端 → 真实委托单」这一公里。pyautotrade_tdx 就是为这最后一公里生的它不造轮子也不封装API而是用Windows底层消息机制WM_COMMAND PostMessage精准模拟人工点击把通达信桌面版变成可编程的交易终端。它不依赖券商提供的Web API很多小券商压根没开放也不碰敏感的内存注入或DLL劫持所有操作都在用户态完成符合绝大多数券商对客户端安全性的隐性要求。适合三类人想快速验证策略逻辑但被接口卡住的个人开发者需要对接老旧通达信定制版无API权限的某高校金融实验室以及正在做跨平台交易中台、需保留通达信作为后备通道的某公司技术组。注意它解决的是「能下单」不是「必盈利」——策略逻辑、风控模块、资金管理全得你自己填。2. 核心原理与选型依据为什么非得用Windows消息而不是PyWin32或uiautomation2.1 通达信客户端的“反自动化”设计现实通达信桌面版尤其是V6.85及更早版本对常规UI自动化有明确防御行为主窗口句柄HWND每次启动都变化且不响应FindWindow(TdxW_MainFrame_Class, None)这类经典查找所有交易按钮如“买入”“卖出”是自绘控件OwnerDrawn Button没有标准Button类名PyWin32的FindWindowEx无法定位输入框代码、价格、数量使用自定义Edit控件SendKeys常触发焦点丢失或输入错位最关键的是通达信进程会检测GetAsyncKeyState等钩子调用一旦发现非常规键盘监听立即弹窗警告并禁用交易功能。提示这不是bug是通达信主动设计的安全层。很多教程教用pyautogui.click(x,y)硬坐标点击结果在高DPI屏幕或窗口缩放时全部偏移——因为坐标系随分辨率动态变而pyautotrade_tdx绕开了这个死结。2.2 pyautotrade_tdx的破局点只发消息不碰界面项目核心不操作像素而是向通达信窗口发送Windows原生消息用EnumWindows遍历所有窗口通过窗口标题含“通达信”“TdxW”字样和进程名tdxw.exe双重匹配主窗口用GetDlgItem获取子控件ID如买入按钮ID1001卖出ID1002这些ID在通达信各版本中高度稳定调用PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(1001, 0), 0)模拟点击完全等效于鼠标左键按下抬起对输入框先SetForegroundWindow激活窗口再用SendMessage(hwnd_edit, WM_SETTEXT, 0, 000001)直接写入文本绕过焦点争夺。这种做法的优势在于✅ 操作速度极快毫秒级无截图/OCR延迟✅ 100%兼容高DPI、多显示器、窗口缩放✅ 不触发通达信的反钩子检测因未调用SetWindowsHookEx✅ 失败时可精确返回错误码如ERROR_INVALID_WINDOW_HANDLE说明窗口未找到。2.3 为什么不用uiautomation或pywinautouiautomation依赖UI Automation Provider而通达信自绘控件未实现IAccessible接口uiautomation.findFirst返回Nonepywinauto的backenduia同样失效backendwin32则退化为win32gui仍需手动找句柄和ID不如pyautotrade_tdx封装得彻底更重要的是pyautotrade_tdx把ID映射表固化在tdx_ids.py里如BUY_BUTTON_ID 1001你改一行就能适配新版本——而其他库要重写整套查找逻辑。3. 快速上手从安装到第一笔模拟下单的完整闭环3.1 环境准备与依赖安装仅Windowspyautotrade_tdx严格限定Windows平台因依赖Win32 API推荐Python 3.8–3.11。不要用conda环境——部分win32扩展在conda中存在句柄泄漏问题用标准CPython即可# 创建干净虚拟环境 python -m venv tdx_env tdx_env\Scripts\activate.bat # 安装核心依赖注意无需pip install pyautogui/pywin32 pip install pywin32306 # 必须指定306新版307有PostMessage兼容问题 pip install psutil # 用于进程存在性检查注意pywin32306是血泪经验。某次升级到307后PostMessage在通达信最小化状态下失效降级即恢复。原因在于307修改了消息队列处理逻辑与通达信消息泵冲突。3.2 下载源码与配置通达信路径项目无PyPI包必须克隆源码GitHub仓库名通常为pyautotrade-tdxgit clone https://github.com/xxx/pyautotrade-tdx.git cd pyautotrade-tdx关键配置在config.py中需手动填写你的通达信安装路径# config.py TDX_PATH rC:\newtdx # 通达信主程序目录含tdxw.exe TDX_EXE tdxw.exe # 可执行文件名部分定制版可能叫tdxpro.exe TRADING_ACCOUNT A123456789 # 你的资金账号用于自动登录逻辑说明TDX_PATH必须指向通达信根目录不是T0002数据目录。程序启动时会检查os.path.exists(os.path.join(TDX_PATH, TDX_EXE))不存在则抛出TdxNotInstalledError。TRADING_ACCOUNT用于自动填充登录框若为空则需手动输入账号密码。3.3 运行demo下单一只股票并验证成交进入examples/目录运行place_order_demo.py# examples/place_order_demo.py from pyautotrade_tdx.trader import TdxTrader from pyautotrade_tdx.utils import wait_for_window # 初始化交易器自动检测并激活通达信窗口 trader TdxTrader() # 等待通达信主窗口就绪超时30秒 if not wait_for_window(通达信, timeout30): raise RuntimeError(通达信未启动或窗口标题不匹配) # 执行买入深市股票000001价格9.5元数量100股 result trader.buy( stock_code000001, price9.5, quantity100, exchangeSZ # SZ深市SH沪市 ) print(f下单结果: {result}) # 输出示例: {success: True, order_id: 20240520000001, msg: 委托成功}执行前确保通达信已启动并登录账号密码正确交易菜单已展开快捷键Ctrl1打开委托窗口当前处于“买入”标签页非查询或撤单页。参数说明stock_code6位纯数字深市以000/001/002开头沪市以600/601/603开头price必须为float单位为元精度到小数点后2位quantity必须为100的整数倍A股最小单位exchange强制指定避免代码前缀误判如000001可能是深市也可能是沪市B股此处按A股规则。4. 避坑指南五个真实翻车现场与自救方案4.1 现象wait_for_window一直超时控制台打印“找不到窗口”原因通达信窗口标题被修改。默认标题为“通达信金融终端”但某些券商定制版会改为“XX证券通达信”或添加版本号如“通达信V6.85”导致EnumWindows匹配失败。解决手动确认当前窗口标题用win32gui.GetWindowText(hwnd)遍历所有窗口找到含tdxw.exe进程的窗口标题修改utils.py中wait_for_window函数的匹配逻辑将硬编码通达信改为正则匹配import re def wait_for_window(pattern, timeout30): start time.time() while time.time() - start timeout: hwnds [] win32gui.EnumWindows(lambda hwnd, param: param.append(hwnd), hwnds) for hwnd in hwnds: title win32gui.GetWindowText(hwnd) if re.search(pattern, title): # 改为re.search(r通达信|XX证券, title) return hwnd time.sleep(0.5) return None4.2 现象下单后弹出“委托失败未选择市场”或“代码格式错误”原因通达信委托窗口未聚焦在正确的交易所标签页。程序默认假设当前在“买入”页且市场为“深市”但用户可能手动切到了“沪市”或“创业板”。解决在trader.py的buy()方法开头强制切换市场标签# 切换到深市标签ID1005或沪市ID1006 market_id 1005 if exchange SZ else 1006 win32gui.PostMessage(self.hwnd_main, win32con.WM_COMMAND, win32api.MAKELONG(market_id, 0), 0) time.sleep(0.3) # 等待标签页渲染4.3 现象输入价格/数量后点击“确定”无反应或委托单显示“0”股原因通达信对输入框内容校验严格。若输入字符串含空格、中文逗号、或小数位数超2位如9.500会静默清空字段。解决所有数值输入前强制格式化price_str f{price:.2f} # 保证两位小数 quantity_str str(int(quantity)) # 强制转整数去浮点 win32gui.SendMessage(hwnd_price, win32con.WM_SETTEXT, 0, price_str) win32gui.SendMessage(hwnd_qty, win32con.WM_SETTEXT, 0, quantity_str)输入后调用win32gui.SendMessage(hwnd_price, win32con.EM_GETLINECOUNT, 0, 0)验证是否写入成功返回1表示成功。4.4 现象连续下单时第二单报错ERROR_INVALID_WINDOW_HANDLE原因通达信在委托成功后会短暂关闭委托窗口动画效果此时hwnd句柄失效。后续操作若未重新获取句柄就会失败。解决所有交易操作前增加句柄刷新def _ensure_trading_window(self): # 查找委托窗口标题含“委托” hwnd_order win32gui.FindWindow(None, 委托) if not hwnd_order: # 模拟快捷键Ctrl1重新打开 win32api.keybd_event(win32con.VK_CONTROL, 0, 0, 0) win32api.keybd_event(ord(1), 0, 0, 0) win32api.keybd_event(ord(1), 0, win32con.KEYEVENTF_KEYUP, 0) win32api.keybd_event(win32con.VK_CONTROL, 0, win32con.KEYEVENTF_KEYUP, 0) time.sleep(1) return hwnd_order4.5 现象程序运行中通达信突然弹窗“检测到异常操作已禁用交易”原因高频调用PostMessage被判定为宏软件。通达信每分钟检测一次消息频率若10秒内超过15次WM_COMMAND触发保护。解决在trader.py中为所有PostMessage调用添加随机延迟import random def safe_post_message(hwnd, msg, wparam, lparam): time.sleep(random.uniform(0.1, 0.3)) # 100ms~300ms抖动 return win32gui.PostMessage(hwnd, msg, wparam, lparam)关键操作间强制time.sleep(0.5)如输入代码→等待→输入价格→等待→点击买入。5. 进阶实战构建带风控的日内交易循环与成交确认闭环5.1 为什么必须做成交确认——通达信的“伪委托”陷阱通达信委托窗口点击“确定”后只代表“委托已发出”不等于“已报送到交易所”。常见场景网络延迟导致委托单卡在本地缓存券商服务器拒绝如价格超出涨跌幅委托单进入“已报”状态但未成交需人工撤单。若不做成交确认你的策略可能以为“已买入”实际仓位为零后续信号全错乱。pyautotrade_tdx提供check_order_status(order_id)方法但它依赖通达信“成交回报”窗口——而该窗口默认不自动弹出。5.2 启用成交回报窗口的隐藏设置通达信需手动开启回报提示通达信主界面 → 系统 → 投资者系统设置 → 委托设置勾选“成交回报自动弹出”和“成交回报声音提示”设置“成交回报窗口停留时间”为30秒避免一闪而过。提示此设置保存在T0002\profile.cfg中键名为[Trade]AutoPopUpReport1。若程序部署在无GUI服务器可用win32api.WritePrivateProfileString自动写入。5.3 实现成交确认的完整代码段以下代码演示如何等待成交回报窗口出现并提取成交价格与数量# utils.py def wait_for_deal_report(timeout60): 等待成交回报窗口出现返回窗口句柄 start time.time() while time.time() - start timeout: hwnds [] win32gui.EnumWindows(lambda hwnd, param: param.append(hwnd), hwnds) for hwnd in hwnds: title win32gui.GetWindowText(hwnd) if 成交回报 in title and 通达信 in title: return hwnd time.sleep(0.5) return None # trader.py 中新增方法 def confirm_deal(self, order_id, timeout60): 确认订单是否成交返回成交详情 # 步骤1等待成交回报窗口 hwnd_report wait_for_deal_report(timeout) if not hwnd_report: return {success: False, msg: 未收到成交回报} # 步骤2读取成交回报窗口中的文本通达信用Static控件显示 # 成交回报窗口结构ID1001为股票代码ID1002为成交价格ID1003为成交数量 code self._get_static_text(hwnd_report, 1001) # 如000001 price self._get_static_text(hwnd_report, 1002) # 如9.48 qty self._get_static_text(hwnd_report, 1003) # 如100 # 步骤3关闭回报窗口避免堆积 win32gui.PostMessage(hwnd_report, win32con.WM_CLOSE, 0, 0) return { success: True, order_id: order_id, stock_code: code.strip(), deal_price: float(price), deal_quantity: int(qty), timestamp: datetime.now().isoformat() } def _get_static_text(self, hwnd_parent, ctrl_id): 从Static控件读取文本 hwnd_ctrl win32gui.GetDlgItem(hwnd_parent, ctrl_id) length win32gui.SendMessage(hwnd_ctrl, win32con.WM_GETTEXTLENGTH, 0, 0) buffer ctypes.create_unicode_buffer(length 1) win32gui.SendMessage(hwnd_ctrl, win32con.WM_GETTEXT, length 1, ctypes.byref(buffer)) return buffer.value5.4 构建带风控的交易循环模板以下是一个生产级交易循环骨架包含熔断、重试、日志记录# strategies/daily_loop.py import logging from pyautotrade_tdx.trader import TdxTrader from pyautotrade_tdx.utils import wait_for_window # 配置日志 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[logging.FileHandler(trade.log), logging.StreamHandler()] ) def trading_cycle(): trader TdxTrader() max_retries 3 # 熔断检查当日亏损超3%停止交易 if get_daily_loss() 0.03: logging.warning(当日亏损超3%触发熔断退出循环) return for signal in generate_signals(): # 你的策略信号生成器 for attempt in range(max_retries): try: # 1. 下单 result trader.buy( stock_codesignal[code], pricesignal[price], quantitysignal[qty], exchangesignal[exchange] ) if not result[success]: raise RuntimeError(f下单失败: {result[msg]}) # 2. 等待成交确认 deal_result trader.confirm_deal(result[order_id]) if not deal_result[success]: raise RuntimeError(成交确认超时) logging.info(f成交成功: {deal_result}) break # 成功则跳出重试循环 except Exception as e: logging.error(f第{attempt1}次尝试失败: {e}) if attempt max_retries - 1: logging.critical(f信号{signal}最终失败跳过) time.sleep(2 ** attempt) # 指数退避 if __name__ __main__: # 确保通达信已就绪 if not wait_for_window(通达信): raise RuntimeError(通达信未启动) trading_cycle()从那以后我每次部署新策略都强制走一遍这个闭环下单→等待回报窗口→读取成交价→比对策略预期价→记录偏差。曾发现某券商对0.01元价格四舍五入到0.00导致委托失败而日志里清清楚楚写着“成交价0.00”立刻定位到价格精度问题。希望帮到你。本文还有配套的精品资源点击获取