通道与数据源¶
通达信的不同数据散落在不同的协议和服务器上。选路是 router.py 的职责,
调用方不需要关心——但知道「为什么慢」「为什么某个字段没有」时,这张表是排错起点。
路由表¶
| 请求 | 通道 | 为什么 |
|---|---|---|
| K 线 — 复权(QFQ / HFQ) | MAC | 复权数据只有 MAC 通道提供 |
K 线 — MIN_1 不复权 |
MAC | 标准通道 1 分钟深度不足;且必须串行分页 |
| K 线 — 其他周期不复权 | 标准 | 价格小数位规则已验证 |
| 指数 K 线 | 标准 | 含涨跌家数等指数专有字段 |
| 五档报价 | 标准 | 标准通道字段更全(含 decimal_point) |
| 逐笔成交 | MAC | 含 trade_count(成交笔数),且支持历史回溯 |
| 分时 | MAC | 标准通道不完整;MAC 0x122D 稳定返回 240 条 |
| 证券列表 / 数量 | 标准 | 仅标准通道提供 |
| 除权除息 / 财务 | 标准 | 仅标准通道提供 |
| 板块 / 竞价 / 异动 / 快照 / 资金流 / 服务器时段 / 分类报价 | MAC | 这些是 MAC 专有命令 |
| 港股 / 美股 / 期货 / 期权 | EX(7727) | 完全不同的协议栈 |
只请求,不改语义
路由是内部实现细节。同一个 get_bars() 调用,传不传 adjust 会走不同通道,
但返回类型和字段完全一致——你不需要写 if adjust: 分支。
三组服务器¶
src/pytdxdata/servers.json 里维护三份清单,分别对应三个连接池:
| 池 | 数量级 | 用途 |
|---|---|---|
| 标准池 | 9 台 | 报价 / K线 / 列表 / 财务 |
| MAC 池 | 22 台 | 逐笔 / 分时 / 复权 / 板块 / 资金流 |
| EX 池 | 4 台 | 港股 / 美股 / 期货 / 期权 |
上表为 2026-09-21 离线探针(
python -m pytdxdata.server_probe --discover)实测可达台数。 服务器会下线、换 IP 或变更协议,实际台数以当日测速缓存为准(见下方「运行时:当日测速缓存」)。
EX 池内部还要再分两类,因为握手方式不同:
| 类型 | 数量 | 握手 |
|---|---|---|
| MAC EX 服务器 | 2 | 0x2454 login |
| pytdx EX 服务器 | 2(通达信扩展市场 / 国泰君安) | 0x2454 setup |
这就是 config.get_ex_handshakes() 存在的原因——EX 的 4 台机器不是同一种握手。
注意 MAC 池实测并不比标准池小(22 台 vs 9 台)。早期版本曾按「MAC 可用节点很少」来解释逐笔 / 分时并发弱于日线,实测已推翻该前提——两者的单台连接上限(mac_pool_cap / std_pool_cap)默认相同,都是 4。
为什么这些服务器会被「探测」¶
服务器列表不是静态配置——通达信节点会下线、换 IP、或者握手正常但返回空数据。
server_probe 的探测逻辑不是只看 TCP 连通性,而是真实请求一段 K 线并校验内容:
python -m pytdxdata.server_probe # 按申报协议全量探测
python -m pytdxdata.server_probe --discover # 三种协议全测,并据实修正 protocols
python -m pytdxdata.server_probe --group ex # 只测 EX
python -m pytdxdata.server_probe --refresh # 只重测当前 status=ok 的
普通「只 ping 握手」的探测会把这类坏节点放进来,症状是偶发的空返回—— 比直接连不上更难查。所以探测必须校验数据。
写回字段:status / latency / checked_at / checked_date / fast_pool,
以及顶层 version / checked_date / note。其中 fast_pool 与运行时缓存里的 fast
用同一条规则(整体延迟前 10 名,或延迟 < 200ms),用于还没有当日缓存时的冷启动排序。
为什么需要 --discover¶
「申报协议」会过期。实测(2026-09-21)有一批服务器的 0x010C 标准 K 线变成空响应
(返回 2 字节空壳),但同端口同主机的 0x122E MAC 帧正常——它们从 standard 退化成了
mac-only。只测申报协议的话,这类退化永远发现不了,标准池会一直含着一批死节点。
--discover 对每台主机把三种协议都测一遍,并据实测结果重写 protocols;
配套两个必须处理的细节:
- 失败必须重试一次再判定不支持。单次抖动会误删协议,而
protocols被收窄后不会自动恢复。 - EX 握手模式申报可能错,探测失败时换另一种(
login⇄setup)再试。 - 无任何协议可达时不动
protocols,只把status置fail——保留"它申报过什么"的信息。 - 顺带剔除
ip非法的脏记录(历史遗留如"s1")。
运行时:当日测速缓存¶
python -m pytdxdata.server_probe 是离线重生成随包清单的工具;运行时还有一条在线的
轻量路径——TdxData.start() 会异步触发一次全量测速,把结果缓存到用户级目录
(…/pytdxdata/hosts-today.json),当天后续启动直接复用,不再重测。
设计与几个必须踩对的点:
| 关注点 | 正确做法 | 反例(会静默出错) |
|---|---|---|
| 候选集 | 取全部候选(清单 ∪ 常量 ∪ env) | 只把「当前池成员」丢给探针 → 上次慢的节点永远捞不回来 |
| 可达判定 | 发真实 K 线命令并要求非空 | 只握手 / 只读报文头 → 测不出「空响应」坏节点 |
| 写盘位置 | 用户级缓存目录 | 写回包内 servers.json → 容器只读、多用户互相覆盖 |
| 对调用方 | 后台 task + debug 日志 + 绝不抛异常 | 在 start() 里 await 或打印 → 启动变慢、污染用户输出 |
| 并发 | 进程内单例 + asyncio.Lock |
三个池各测一遍 → 三倍流量 |
| 写入 | 临时文件 + os.replace 原子替换 |
直接覆写 → 并发进程读到半截 JSON |
全部不可达时不写缓存——否则「今天全挂了」会被缓存一整天,明天也不重试。
缓存存在时,config.get_standard_hosts() 等按实测延迟升序返回该协议的可达主机;
缓存缺失 / 跨日 / 版本不符 / 已损坏时,才回落到包内清单。开关见
安装与配置 → 环境变量。
EX 通道细节¶
- 端口固定 7727,与标准 / MAC 的 7709 不同
- 走 pytdx EX 协议(
0x23FFK线 /0x23FA报价 /0x23F4市场列表) - 合约代码规则与 A 股不同:港股 5 位(
00700)、美股字母(AAPL)、 期货带月份(IFL0/rb2610)、期权 8 位(10010971) - 美股期权 / 港股期权协议内无数据
await td.get_bars("hk00700", KlinePeriod.DAY, count=700)
await td.get_bars("usAAPL", KlinePeriod.DAY, count=700)
扩展市场用字符串前缀(hk / us / cffex …)指定;ExMarket 的完整成员见 枚举速查。