TDX 通达信数据源
概述
通达信是实时盘口与资金流的主源(K线/指数为第一备胎),使用 TCP 协议(端口 7709)直连通达信行情服务器,不经过 HTTP 层,因此不存在封IP风险。
自 v2.0 起改为项目内自包含协议实现(data_sources/tdx/ 包),已彻底移除
easy_tdx / mootdx / pytdx 等第三方 TDX 库依赖(python/requirements.txt 中不再出现),
随包分发、零外部依赖。
架构设计
┌─────────────────────────────────────────────────────────┐
│ tdx/ (自包含协议实现) │
│ ├── codec — 协议编解码原语(帧头/价格/成交量/日期)│
│ ├── connection — TCP 连接管理(握手/心跳/测速/健康评分)│
│ ├── commands — 全部协议命令(请求构造+响应解析→dict) │
│ ├── client — 高层客户端(韧性执行/故障转移/业务方法)│
│ ├── adjust — 复权因子(0x000f 批量 GBBQ → qfq/hfq) │
│ └── f10 — F10 公司资料(官方 7615 TQLEX HTTP 网关)│
├─────────────────────────────────────────────────────────┤
│ tdx_client.py (对外门面/兼容适配层,26 个 get_* 函数) │
├─────────────────────────────────────────────────────────┤
│ tdx_client_compat.py(兼容层,re-export tdx_client) │
├─────────────────────────────────────────────────────────┤
│ tdx_client_backup_pool.py(降级路径:独立服务器池的第二条连接)│
└─────────────────────────────────────────────────────────┘
主备切换策略
| 层级 | 文件 | 说明 |
|---|---|---|
| 协议实现 | tdx/ 包 | codec/connection/commands/client/adjust/f10,纯标准库 |
| 对外门面 | tdx_client.py | 26 个 get_* 函数(25 个数据接口 + 1 个内部辅助) |
| 兼容层 | tdx_client_compat.py | re-export tdx_client,旧代码无需修改 |
| 降级路径 | tdx_client_backup_pool.py | 自带 _TDX_SERVERS 独立服务器池,主客户端所选主机异常时备用 |
实现文件
- 协议实现:
backend/app/services/data_sources/tdx/(codec / connection / commands / client / adjust / f10) - 对外门面:
backend/app/services/data_sources/tdx_client.py - 兼容层:
backend/app/services/data_sources/tdx_client_compat.py - 降级路径:
backend/app/services/data_sources/tdx_client_backup_pool.py
连接管理
两阶段韧性连接
自实现客户端内置两阶段故障转移机制:
- 同主机重试:指数退避 4 次,延迟序列
_RETRY_DELAYS = (0.5, 1.0, 2.0, 3.0)秒 - 跨主机切换:当前主机连续失败后自动切换到下一台(
find_verified_hosts()并发测速优选)
# 自动选择延迟最低的服务器 + 自动重连 + 心跳保活(15s)
# timeout/budget 由生产调用方给保守值:超时 5s、韧性恢复总预算 5s,
# TDX 不可达时快速抛错降级(实测最坏曾达 ~75s)
client = TdxDataClient.from_best_host(
timeout=5.0,
auto_reconnect=True,
heartbeat_interval=15.0, # 15 秒心跳,连续 20 次失败判定连接失效
budget=5.0,
)
client.connect()
共享故障冷却(轻量熔断)
tdx/client.py 在韧性执行层之上再做一层进程级冷却:连续失败达 _TDX_FAIL_THRESHOLD=2
次即进入冷却,期间所有 TDX 操作快速失败,由上层降级链(腾讯/东财)立即兜底;
冷却时长指数退避 30 → 60 → 120 → 240 → 300 秒封顶,任一成功即复位。
(避免 TDX 主机不可达时每个请求各自重走 5s 韧性预算,把 stocks/{code}
拖到 10s+、行业对比拖到 17~27s。)
健康评分系统
- 每次成功请求:
record_success()加分 - 每次失败:
record_failure()降权 - 服务器选择时优先使用健康分高的节点
空数据故障转移
当某台服务器返回空数据时,自动逐台实测其他服务器,直到找到有效数据源。
连接复用
全局单例 + 冷却机制,避免频繁 TCP 握手:
# tdx/client.py 单例管理(模块级)
_client: TdxDataClient | None = None
_client_lock = threading.Lock()
_last_fail: float = 0.0
_RETRY_INTERVAL = 30.0 # 初始化失败后的冷却期(秒)
def get_client() -> TdxDataClient:
"""获取全局 TdxDataClient 单例(失败后 30s 冷却)。"""
...
提供的数据(26 个 get_* 函数)
基础行情接口
| 功能 | 函数 | 说明 |
|---|---|---|
| K线数据 | get_kline(code, category, offset) | 日/周/月/分钟K线 |
| 日K线 | get_daily_kline(code, count) | 日K线快捷方法 |
| 五档盘口 | get_quotes(codes) | 实时买卖五档 + 涨跌停价 |
| 全市场快照 | get_all_a_share_quotes() | 全A股实时快照(涨跌分布分桶用,覆盖不足 3000 只判不可用) |
| 单股行情 | get_single_quote(code) | 单只股票完整行情 |
| 指数行情 | get_index_quotes() | 上证/深证/创业板/科创50(腾讯主源后的补齐源) |
| 分时数据 | get_minute_data(code) | 当日240点分时走势 |
| 逐笔成交 | get_transactions(code, date) | 当日/历史逐笔 |
| 涨跌停价快照 | get_limits(code="") | 协议 0x0452,全市场或单股 |
| 集合竞价 | get_auction(code, count) | 协议 0x056A,竞价撮合序列(⚠️ 返回不含日期,见下方注意事项) |
财务与基本面
| 功能 | 函数 | 说明 |
|---|---|---|
| 财务快照 | get_finance_snapshot(code) | 30字段完整财务数据 |
| 财务信息 | get_finance_info(code) | 同 get_finance_snapshot |
| 除权除息 | get_xdxr_info(code) | 分红送转、股本变动历史 |
| 复权因子 | get_adjustment_factors(codes) | 0x000f 批量 GBBQ → 本地 qfq/hfq 系数 |
| F10资料 | get_f10(code, category) | 公司概况/财务分析等文本 |
| F10结构化 | get_f10_company_profile_structured(code) | 公司概况键值对(写入 fox_company_profile) |
市场与板块
| 功能 | 函数 | 说明 |
|---|---|---|
| 全A股列表 | get_security_list_all() | 5000+股票,含行业映射 |
| 板块数据 | get_block_info(block_type) | 行业/概念/风格板块+成分股 |
| 市场统计 | get_market_stat() | 涨跌家数、涨停跌停、总市值 |
| 板块排行 | get_board_ranking(board_type) | 行业/概念涨跌排行 |
| 个股板块 | get_belong_board(code) | 个股所属板块列表 |
资金流向
| 功能 | 函数 | 说明 |
|---|---|---|
| 当日资金流 | get_fund_flow(code) | 四级资金流(超大/大/中/小) |
| 历史资金流 | get_history_fund_flow(code, days) | 日线资金流序列(0x0ffc 覆盖时用真实主力口径) |
| 主力资金流 | get_money_flow_daily(code) | 0x0ffc 主站原生日线资金流(约最近 5 日) |
| MAC资金流 | get_capital_flow(code) | 多日主力/小单/中单/大单 |
与历史第三方库实现的差异
早期版本用 mootdx / easy_tdx 等第三方库;现已全部由 tdx/ 包自实现,
能力覆盖已超过原库(数据源丰富化参考 eltdx 3.1.3 / 1.2.0):
| 特性 | 自实现 tdx/(当前) | 早期第三方库 |
|---|---|---|
| 依赖 | 纯标准库,零第三方依赖 | 需额外安装,版本兼容受限 |
| 连接管理 | 两阶段韧性+心跳+健康评分+共享冷却熔断 | 单例缓存,无重连 |
| 故障转移 | 协议级验证优选 + 跨主机自动切换 | 无(或仅 TCP 连通性) |
| 财务数据 | 30 字段 | 10 字段 |
| 行业映射 | 通达信+申万行业代码 | 无 |
| 板块数据 | 行业/概念/风格 | 无 |
| 市场统计 | 涨跌/涨停/市值 | 无 |
| 资金流向 | 四级+历史+0x0ffc 原生主力 | 无 |
| 复权K线 | 前复权/后复权(GBBQ 因子本地化) | 无 |
| 除权除息 | 完整历史 | 无 |
| F10 | 官方 7615 TQLEX HTTP 网关(结构化) | 无 |
在降级链中的位置
指数行情: 腾讯指数(主) → TDX 补齐缺失指数(双源互补,非故障切换)
K线数据: 腾讯K线(主) → TDX K线(备胎) → 东财 push2his(兜底)
北交所特例:新浪 → 东财(腾讯仅返最新 1 根、TDX 不覆盖北交所)
财务数据: TDX 财务快照 + 新浪利润表 → 快照构造单条兜底
涨跌分布: TDX 全市场快照统计(880005/880006,覆盖 ≥3000 只) → 东财区间统计(filter 已失效,恒判不可用)
→ fox_stock_wide 宽表(仅非交易时段) → 放弃分段保留实时家数
资金概况: TDX 实时成交额(880005) → fox_market_daily 上一日成交额兜底
首页涨跌分布和资金概况优先使用 TDX TCP 实时统计(基于 880005/880001/880006 统计指数),
而非已移除的 stock_base_info 表聚合。DB 聚合路径自 2026-08-22 起已废弃
(db_market_breadth 等恒返回 None),仅剩「上一日总成交额」走 fox_market_daily。
这确保了首页始终展示当日实时市场广度。
两条「静默错值」防线(详见 数据源总览):
- TDX 证券列表分页中断会只返回部分市场(实测沪市 2000 只后失败、深市整体跳过),
tdx_market_breadth_full以_MIN_FULL_MARKET_QUOTES=3000为覆盖下限,低于即判不可用; - 东财 push2
filter参数自 2026-09-22 起被服务端忽略(15 路不同 filter 全部返回全量总数),em_market_breadth用「2 路探针 + 家数/分段自洽校验」直接判不可用,不再把全等分段交给下游。
注意事项
- TDX 客户端为同步阻塞实现,需在
ThreadPoolExecutor中执行,避免阻塞 asyncio 事件循环 - 连接可能因网络波动断开,客户端内置自动重连(同主机指数退避 4 次
0.5/1/2/3s+ 跨主机切换) - 主机持续不可达时进入共享冷却(30→300s 退避),期间 TDX 操作快速失败并由上层降级链兜底
- 非交易时段数据为上一交易日收盘数据
- 批量命令逐只回退带连续失败熔断(
_FINANCE_FALLBACK_MAX_STREAK=5), 避免 TDX 故障时全市场同步白耗数分钟并刷数千条 warning
get_auction(0x056A)返回的撮合序列不带日期 —— 必须自己设闸门返回的每行只有 index / time(HH:MM:SS)/ hour / minute / second / price /
matched_vol / unmatched_vol / unmatched_direction 等时段内字段,没有交易日字段。于是:
- 非交易日、以及交易日 09:15(集合竞价开始)之前请求时, 它给出的是上一交易日的撮合序列;
- 数值看着完全正常(如昨涨停股 +10% 开盘),调用方无从分辨;
- 直接展示就等于把昨日行情标成「今日竞价」—— 静默口径错误,比报错危险得多。
正确用法:调用方按交易日历 + 窗口起点判定,无今日数据时不要取数。
参考实现 services/abnormal_moves._auction_session_state():
| 状态 | 条件 | 处理 |
|---|---|---|
ready | 交易日且 ≥ 09:15 | 正常取数 |
pre_auction | 交易日但 < 09:15 | 返回空,不触 TDX |
non_trading_day | 周末 / 法定休市 | 返回空,不触 TDX |
另注:09:25 最终撮合价落定后,该序列当日冻结(不再变化), 故 09:30 之后可以给很长的缓存 TTL。
自实现主客户端若出现不可预期的兼容性问题,可手动切到独立服务器池的备用路径:
# tdx_client_backup_pool.py 自带 _TDX_SERVERS 池,与主客户端互不影响
from .tdx_client_backup_pool import get_kline, get_quotes, ...