Files
easy-tdx/README.md
T
M ad2fc9c84c feat: 实现全量 A 股列表获取与行业信息自动挂载
1. 编解码器:新增 codec/industry.py 用于解析 tdxhy.cfg 行业配置。
2. 数据模型:SecurityInfo 增加 industry_tdx 和 industry_sw 字段。
3. 客户端 API:增加 get_security_list_all(),支持分页拉取全市场 A 股并自动匹配行业标签。
4. 通讯优化:更新 KNOWN_HOSTS 包含更稳定的华为云及招商证券节点。
5. 文档更新:README.md 同步 API 及数据模型变更。
2026-04-14 22:57:45 +08:00

254 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# xmtdx
通达信 TCP 协议 A 股行情数据客户端,零运行时依赖。
pytdx 年久失修:多处已知解析 bug、Python 2 包袱、无类型注解、大量未知字段被静默丢弃。xmtdx 重新实现协议,修复已知 bug,保留全部原始字节与未知字段供后续逆向分析。
## 特性
- **零依赖**:纯标准库,Python ≥ 3.10
- **同步 + asyncio 双接口**`TdxClient` / `AsyncTdxClient`commands 层不含任何 IO
- **完整类型注解**strict `mypy` + `ruff` 通过
- **高可用传输**:同步/异步均支持 `ping_all()``from_best_host()`、断线自动重连
- **修复 pytdx 已知 bug**(见下文)
- **保留原始字节**:每条数据记录含 `_raw: bytes`,未知字段以 `unknown_N` 命名而非丢弃
- **保活心跳机制**`AsyncTdxClient` 自动发送心跳包,确保长连接生产环境稳定性
- **全市场涨跌统计**:一键获取全 A 股涨/跌/平家数及总成交额
- **离线 + 本地传输回归测试**:覆盖解析、异步并发串行化、超时、自动重连与坏包处理
## 安装
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e . # 开发模式
pip install -e ".[dev]" # 含测试/类型检查工具
pip install -e ".[pandas]" # 含 pandas(可选)
```
## 快速开始
### 同步
```python
from xmtdx import TdxClient, Market, KlineCategory
# 指定服务器
with TdxClient("180.153.18.170") as c:
count = c.get_security_count(Market.SH)
bars = c.get_security_bars(Market.SH, "600000", KlineCategory.DAY, 0, 5)
for b in bars:
print(b.year, b.month, b.day, b.open, b.close, b.high, b.low, b.vol)
# 自动测速选最低延迟服务器
with TdxClient.from_best_host() as c:
quotes = c.get_security_quotes([(Market.SH, "600000"), (Market.SZ, "000001")])
print(quotes[0].price, quotes[0].bid1, quotes[0].ask1)
```
### asyncio
```python
import asyncio
from xmtdx import AsyncTdxClient, Market, KlineCategory
async def main():
async with AsyncTdxClient("180.153.18.170") as c:
bars = await c.get_security_bars(Market.SH, "600000", KlineCategory.DAY, 0, 5)
print(bars[0])
asyncio.run(main())
```
### 高可用工具
```python
from xmtdx import ping_all, KNOWN_HOSTS
# 并发测速,返回按延迟排序的 [(host, seconds), ...]
results = ping_all(KNOWN_HOSTS, timeout=5.0)
for host, ms in results:
print(f"{host} {ms*1000:.0f} ms")
# 自动选最优服务器
with TdxClient.from_best_host(ping_timeout=5.0) as c:
...
# asyncio 版本同样支持
client = AsyncTdxClient.from_best_host(ping_timeout=5.0)
```
内置服务器列表(`KNOWN_HOSTS`):
```
180.153.18.170 180.153.18.171 180.153.18.172
115.238.56.198 115.238.90.165 218.75.126.9
47.107.75.159 59.175.238.38
```
## API
### TdxClient
| 方法 | 说明 |
|------|------|
| `get_security_count(market)` | 市场证券总数 |
| `get_security_list(market, start)` | 证券列表(每页 ~1000 条) |
| `get_security_list_all()` | 全市场 A 股列表(自动挂载行业信息) |
| `get_security_quotes([(market, code), ...])` | 批量实时五档行情(最多 80 只/次) |
| `get_security_bars(market, code, category, start, count=800)` | K 线(股票) |
| `get_index_bars(market, code, category, start, count=800)` | K 线(指数) |
| `get_minute_time_data(market, code)` | 今日分时(240 条) |
| `get_history_minute_time_data(market, code, date)` | 历史某日分时,`date=YYYYMMDD` |
| `get_transaction_data(market, code, start, count=800)` | 当日逐笔成交(分页) |
| `get_history_transaction_data(market, code, date, start, count=800)` | 历史逐笔成交 |
| `get_xdxr_info(market, code)` | 除权除息历史 |
| `get_finance_info(market, code)` | 最新财务数据 |
| `get_company_info_category(market, code)` | 公司信息文件目录 |
| `get_company_info_content(market, code, filename, offset, length)` | 公司信息文本 |
| `get_block_info(filename)` | 板块信息(行业、概念、风格等) |
| `get_report_file(filename)` | 批量拉取大文件(如 'base_info.zip', 'gpcw.txt' |
`AsyncTdxClient` 提供与同步版对应的查询方法与高可用入口,均为 `async def`
单个 `AsyncTdxClient` 仅维护一条 TCP 连接;并发调用会在连接内串行执行。
### KlineCategory
```
MIN_1 MIN_3 MIN_5 MIN_15 MIN_30 MIN_60
DAY WEEK MONTH SEASON YEAR YEAR_ALT
```
## 数据模型
所有 dataclass 字段均有类型注解。每条记录附带 `_raw: bytes`(原始协议字节)。
### SecurityBarK 线)
```
open close high low vol amount
year month day hour minute
_raw
```
### SecurityQuote(实时行情)
```
market code price pre_close open high low
vol cur_vol amount s_vol b_vol
bid1..bid5 bid_vol1..bid_vol5
ask1..ask5 ask_vol1..ask_vol5
rise_speed limit_up limit_down server_time
unknown_2..unknown_3 unknown_5..unknown_8
_raw
```
### MinuteBar(分时)
```
price vol
unknown_1 # 原 pytdx 丢弃字段,保留供分析(≠ 均价)
_raw
```
### TransactionRecord(逐笔成交)
```
hour minute price vol
buyorsell # 0=买, 1=卖, 2=中性, 8=集合竞价
unknown_last
_raw
```
### SecurityInfo(证券列表)
```
market code name volunit decimal_point pre_close
industry_tdx industry_sw
```
### XdxrRecord(除权除息)
```
market code year month day category name
fenhong peigujia songzhuangu peigu suogu
xingquanjia fenshu
panqian_liutong panhou_liutong # 单位:万股
qian_zongguben hou_zongguben # 单位:万股
_raw
```
### FinanceInfo(财务)
流通股本、总股本、各省份/行业代码、资产负债表及利润表主要科目(30 个 float 字段)。
### CompanyInfoCategory(公司信息目录)
```
name filename start length
```
### TdxBlock(板块信息)
```
name category count codes
```
## 修复的 pytdx Bug
| # | 位置 | 问题 | 修复 |
|---|------|------|------|
| 1 | `xdxr_info` | 循环内始终读 `body[:7]`,所有记录字段相同 | 改为从当前 `pos` 读取,pos 正确推进 |
| 2 | `security_list` | GBK 解码截断时 crash | `decode('gbk', errors='replace')` |
| 3 | `security_list` | `pre_close` 错用成交量解码函数 | 改用价格解码 |
| 4 | `transaction` | 最后一个字段被 `_` 丢弃 | 保留为 `unknown_last` |
| 5 | `minute_time` | `reversed1` 字段被丢弃 | 保留为 `unknown_1` |
| 6 | `xdxr_info` | 股本字段用 `float(uint32)` 直解,差约 374 倍 | 改用 `_decode_volume`(通达信自定义浮点),单位万股,与 `FinanceInfo` 完全吻合 |
| 7 | `security_quotes` | 涨停/跌停价映射错误或缺失 | 解析 `unknown_2/3` 为绝对价格字段 `limit_up/down` |
## 架构
```
src/xmtdx/
├── client.py # TdxClient / AsyncTdxClient(高层 API
├── transport/
│ ├── sync.py # TdxConnectionsocket+ ping_host / ping_all
│ └── async_.py # AsyncTdxConnectionasyncio
├── commands/ # 每条命令:build_request() + parse_response(),无 IO
├── codec/ # price / volume / datetime / frame 编解码
└── models/ # 纯 dataclass,无业务逻辑
```
commands 层不依赖 transport,可独立单测。transport 层负责 TCP、握手、帧解压、分发。
## 开发
```bash
# 单元测试(无需网络)
python -m pytest tests/unit/
# 集成测试(需要网络,默认跳过)
XMTDX_LIVE=1 python -m pytest tests/integration/
# 未知字段探测脚本
python scripts/probe_unknowns.py
# 类型检查
mypy src/
# lint
ruff check src/ tests/
```
## 协议说明
通达信使用私有二进制 TCP 协议:
- **帧格式**16 字节响应头(含 zipsize / unzipsize),body 按需 zlib 解压
- **价格编码**:变长有符号整数(类 LEB128,bit8=继续,bit7=符号,首字节低 6 位 + 后续低 7 位)
- **成交量编码**:4 字节自定义浮点(字节 3 = 指数,字节 0-2 = 精度),**不可用于价格字段**
- **握手**:连接后必须顺序发送 3 条 setup 命令,响应丢弃
- **价格存储**:整数 × 100,差分编码(相邻 tick 存 delta