通达信行情接口DLL数据采集实战:从加载到落库
简介这份资源是借用TdxHqApi dll实现的实时数据采集器StockRealData面向对通达信行情接口、C#与Java混合开发感兴趣的个人学习者可用于搭建本地行情采集与数据落地的实验环境。压缩包共299个文件约110.7MB以84个cs源码、21个java文件、23个dll动态库为主辅以config配置、csproj工程、class字节码、txt说明及少量pdf、xlsx、csv等文档数据并包含通达信数据格式、分析家dad格式等行情文件便于理解接口调用与数据解析流程。目前已有54人学习下载。资源内含完整的工程结构与多语言调用示例读者可据此研究TdxHqApi的封装方式、实时行情拉取逻辑与数据存储思路也可作为二次开发与接口调试的参考适合具备一定编程基础、希望深入理解行情采集链路的学习者使用。1. 拆开一个用 TdxHqApi dll 做实时数据采集的 StockRealData 小工具行情软件里看到的每一笔分时、每一档盘口背后都得有人把数据从接口里捞出来、清洗、再喂给策略或界面。StockRealData 就是干这件事的一个采集器它借通达信行情接口 TdxHqApi 这个 dll把实时行情拉进本地供个人学习和小规模策略验证用。适合谁想自己搭一套行情落库、又不想从零啃协议栈的人也适合拿它当 dll 调用范例理解 C/C# 里怎么加载一个行情 dll、怎么把回调数据转成结构化记录。它不解决选股只解决“数据从哪来、怎么稳定拿”。2. TdxHqApi 与 StockRealData 的对接原理为什么用 dll 而不是重写协议2.1 行情接口的三层结构连接、订阅、回调通达信行情体系大致分三层最底层是 TCP 长连接和私有二进制协议中间是 TdxHqApi 这类封装好的 dll最上层才是 StockRealData 这种业务采集器。dll 的价值在于把协议握手、心跳、解包这些脏活封在二进制里对外只暴露几个函数连接服务器、登录、请求某只股票的实时快照、请求分时或 K 线。StockRealData 要做的不是重写协议而是把 dll 的返回值翻译成自己能落库的结构。常见做法是采集器启动时先调一次初始化拿到一个会话句柄之后所有请求都带这个句柄。句柄失效服务器踢线、网络抖动时dll 一般会返回一个错误码而不是抛异常所以采集器必须自己维护重连逻辑。这一点是后面避坑章节反复要提的dll 不会替你重连它只负责单次调用。2.2 数据流从 dll 回调到本地落库一次完整的采集链路是这样的StockRealData 定时触发 → 调用 dll 的行情请求函数 → dll 内部走 TCP 拿回二进制包 → 解包成结构体数组 → 采集器把结构体映射成自己的字段 → 写入内存队列 → 落库线程批量写盘。中间任何一环阻塞都会让行情延迟累积。我一般会把“请求”和“落库”拆成两个线程中间用有界队列连接。队列满了就丢最旧的快照而不是让请求线程等落库——行情场景里旧数据比丢数据更危险。下面是一个简化的采集循环骨架语言用 C 示意因为 dll 导出通常是 C 风格接口// 采集线程只负责请求不碰磁盘 void CollectLoop(TdxHqApi* api, BlockingQueueSnapshot queue) { while (running) { for (auto code : watchList) { Snapshot snap; // 调用 dll 导出的行情请求返回 0 表示成功 int ret api-GetQuote(code.c_str(), snap); if (ret ! 0) { // 错误码不抛异常交给重连逻辑处理 HandleApiError(ret); continue; } // 队列满时丢弃最旧数据保证采集不被落库拖死 queue.push_drop_oldest(snap); } std::this_thread::sleep_for(std::chrono::milliseconds(200)); } }逻辑说明GetQuote是 dll 暴露的单次请求返回码是判断成功与否的唯一依据push_drop_oldest是自定义队列行为标准库队列没有这个语义需要自己实现。参数上sleep_for的 200ms 是采集频率太快会触发服务器限流太慢分时数据会丢点。这个值要按你订阅的股票数量调整订阅 50 只以内200ms 够用超过 200 只建议拉到 500ms 以上否则单轮请求还没跑完下一轮就开始了。2.3 字段映射dll 结构体到你的表结构dll 返回的结构体字段名往往很简略比如code、price、vol、bid1、ask1。落库前要决定是原样存还是转成带时间戳和复权标记的宽表。个人学习场景我建议原样存一张窄表再加一张字典表记录字段含义避免以后换 dll 版本时字段对不上。dll 字段含义落库类型注意code股票代码varchar(10)带市场前缀还是纯数字看 dll 约定price最新价decimal(10,3)部分接口返回的是整数需除以 100vol成交量bigint单位可能是手落库前确认bid1/ask1买一/卖一decimal(10,3)盘口字段可能为空要允许 nulltime行情时间datetime服务器时间不是你本地时间这张表的关键不是字段多少而是“单位”和“空值”两列。血泪经验很多采集器跑了一周才发现成交量单位是手不是股回测结果全错。落库前先拿一只你熟悉的股票手工核对一次价格和成交量比写十行校验代码都管用。3. 把 StockRealData 跑起来环境、加载与最小验证3.1 dll 加载失败的排查顺序dll 类项目第一步翻车几乎都发生在加载阶段报错常见的是“找不到指定的模块”或“failed to load”。排查顺序固定先看位数是否匹配32 位程序加载不了 64 位 dll反之亦然再看依赖的运行库是否齐全最后看 dll 所在目录是否在搜索路径里。Windows 下可以用dumpbin /dependents看它依赖了哪些库# 查看 dll 依赖确认缺哪个运行库 dumpbin /dependents TdxHqApi.dll # 如果提示找不到 dumpbin用 VS 开发者命令行或改用 Dependency Walker逻辑说明dumpbin输出里如果有你机器上没有的MSVCP140.dll、VCRUNTIME140.dll之类就是运行库缺失装对应版本的微软运行库即可。这一步能解决大部分“dll 加载失败”不用去下那些来路不明的修复工具。参数上/dependents只列直接依赖间接依赖要递归看但通常直接依赖里就能发现缺的那个。3.2 最小验证先拿一只股票打通全链路不要一上来就订阅几百只股票。先写一个最小验证连接、请求一只你熟悉的股票、打印结果、落一条记录。跑通了再扩订阅列表。下面是一个验证脚本的伪代码结构# 用 ctypes 加载 dll 做最小验证确认接口能通 import ctypes api ctypes.CDLL(./TdxHqApi.dll) # 按 dll 实际导出名和参数类型声明这里仅为示意 api.Init.restype ctypes.c_int api.GetQuote.argtypes [ctypes.c_char_p, ctypes.c_void_p] handle api.Init() if handle 0: raise RuntimeError(初始化失败检查服务器地址和端口) buf ctypes.create_string_buffer(256) ret api.GetQuote(b600000, buf) print(返回码:, ret, 数据:, buf.raw[:64])逻辑说明ctypes.CDLL是 Python 加载 C 风格 dll 的标准方式argtypes和restype必须和 dll 导出一致否则会读到垃圾数据甚至崩溃。参数上Init返回 0 通常表示失败但具体约定要看 dll 文档GetQuote的第二个参数是输出缓冲区大小要够放结构体256 字节是保守值。这一步跑通说明 dll 加载、接口调用、数据返回三件事都对了再往上加落库和调度。3.3 采集频率与订阅数量的平衡采集频率和订阅数量是一对矛盾频率越高、订阅越多单轮请求耗时越长越容易触发服务器限流或本地队列积压。我一般按这个经验值起步单只股票请求耗时约 520ms50 只股票一轮约 0.51 秒。所以 200ms 的采集间隔在 50 只时已经偏紧实际会退化成“上一轮没跑完下一轮又来”。调整方法先测单轮耗时再定间隔。间隔至少是单轮耗时的 1.5 倍留出网络抖动余量。如果必须高频就分多个采集线程每个线程负责一部分股票但要注意 dll 是否线程安全——多数行情 dll 不是多线程调用同一个句柄会出玄学问题稳妥做法是每个线程独立初始化一个句柄。4. 避坑与常见问题dll 采集器最容易翻车的五处4.1 现象跑几小时后数据不再更新进程还在原因dll 的会话被服务器踢掉但采集器没有检测到继续用失效句柄请求返回码被忽略。解决每次请求都检查返回码连续 N 次失败就触发重连重连时先释放旧句柄再初始化新句柄不要复用。N 取 35 比较稳太小会因网络抖动误重连太大则数据断档时间长。4.2 现象落库数据里价格出现 0 或异常大值原因dll 返回的价格是整数需要除以 100 或 1000采集器没做单位转换或者盘口字段为空时读到了未初始化内存。解决落库前对每个价格字段做范围校验超出合理区间比如 0.0110000就标记为可疑并记录原始值不要直接写库。空值统一转 null不要用 0 代替。4.3 现象程序启动报“找不到指定的模块”但 dll 明明在目录里原因dll 依赖的运行库缺失或位数不匹配或 dll 放在子目录但没加入搜索路径。解决按 3.1 的顺序排查先用dumpbin看依赖再确认位数最后把 dll 和依赖库放同一目录或加入 PATH。不要用网上那些“dll 修复工具”它们经常替换成不兼容的版本反而把系统搞乱。4.4 现象多线程采集时程序随机崩溃原因行情 dll 大多不是线程安全的多个线程共用一个句柄会踩内存。解决每个采集线程独立初始化自己的句柄或者干脆单线程采集、多线程落库。如果 dll 文档明确说线程安全再考虑共享句柄。这个坑很隐蔽崩溃位置往往不在 dll 调用处排查起来费时间。4.5 现象采集正常但数据时间戳对不上原因dll 返回的是服务器时间和你本地时区、系统时间可能不一致或者采集器落库时用了本地时间。解决统一用 dll 返回的行情时间作为主时间戳本地时间只用于记录采集时刻。落库表里两个时间字段分开存回测时用行情时间排查延迟时用采集时间。5. 进阶把采集器做成可验证、可回放的小系统5.1 用回放验证采集质量采集器跑起来只是第一步能不能信它的数据是另一回事。我的习惯是每天收盘后拿采集器存的分时数据和行情软件里的分时图对一遍重点看开盘、收盘、午间休市三个时间点。如果这三个点对得上中间大概率没问题。更进一步可以把某一天的数据导出成 CSV写个脚本重放验证落库逻辑在重复写入时不会产生重复记录。# 回放验证检查同一天同一只股票是否有重复时间戳 import pandas as pd df pd.read_csv(snapshot_600000.csv, parse_dates[quote_time]) dup df[df.duplicated(subset[code, quote_time], keepFalse)] if not dup.empty: print(发现重复时间戳:, len(dup), 条) print(dup.head()) else: print(时间戳唯一落库逻辑正常)逻辑说明duplicated的subset指定用代码加行情时间做唯一键keepFalse会把所有重复项都标出来而不是只留一条。参数上如果你的采集频率高于行情最小变动周期重复是正常的这时唯一键要加上采集时刻。这个脚本不解决采集问题但能帮你确认落库没有重复写。5.2 采集器的边界与个人学习的定位StockRealData 这类工具定位是个人学习和小规模验证不是生产级行情分发。它的边界很清楚单机、单进程、订阅数量有限、没有分布式容错。拿它做策略原型验证够用拿它做多策略共享行情源就会遇到瓶颈。我一般会把它当“数据入口”后面接一个本地消息队列或数据库让策略从队列读而不是直接调 dll。如果你要扩到更多股票或更高频率优先考虑的是换更底层的接口或专业行情源而不是在这个采集器上堆线程。堆线程只会让 dll 的线程安全问题更早暴露。从那以后我每次接一个新的行情 dll都先写一个最小验证脚本确认加载、调用、返回码三件事再动业务代码。希望帮到你。本文还有配套的精品资源点击获取