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 及数据模型变更。
This commit is contained in:
M
2026-04-14 22:57:45 +08:00
parent b049511010
commit ad2fc9c84c
5 changed files with 97 additions and 0 deletions
+2
View File
@@ -95,6 +95,7 @@ client = AsyncTdxClient.from_best_host(ping_timeout=5.0)
|------|------|
| `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 线(指数) |
@@ -164,6 +165,7 @@ _raw
```
market code name volunit decimal_point pre_close
industry_tdx industry_sw
```
### XdxrRecord(除权除息)
+66
View File
@@ -17,6 +17,7 @@ from .commands.security_quotes import GetSecurityQuotesCmd
from .commands.transaction import GetHistoryTransactionDataCmd, GetTransactionDataCmd
from .commands.xdxr_info import GetXdxrInfoCmd
from .codec.block import parse_block_dat
from .codec.industry import parse_tdxhy_cfg
from .exceptions import TdxConnectionError
from .models.bar import SecurityBar
from .models.enums import KlineCategory, Market
@@ -145,6 +146,41 @@ class TdxClient:
"""获取证券列表(每页约1000条,按 start 分页)。"""
return self._execute(GetSecurityListCmd(market, start))
def get_security_list_all(self) -> list[SecurityInfo]:
"""获取全市场(沪深 A 股)完整证券列表,并自动挂载行业信息。"""
# 1. 尝试获取行业配置
industry_map = {}
try:
cfg_data = self.get_report_file("tdxhy.cfg")
if cfg_data:
industry_map = parse_tdxhy_cfg(cfg_data)
except Exception:
pass
all_stocks: list[SecurityInfo] = []
for market in [Market.SH, Market.SZ]:
count = self.get_security_count(market)
for start in range(0, count, 1000):
stocks = self.get_security_list(market, start)
for s in stocks:
# 精确 A 股过滤规则
is_a_share = False
if market == Market.SH:
# 沪市 A 股:60xxxx, 68xxxx
if s.code.startswith(("60", "68")):
is_a_share = True
elif market == Market.SZ:
# 深市 A 股:00xxxx (主板), 30xxxx (创业板)
if s.code.startswith(("00", "30")):
is_a_share = True
if is_a_share:
# 挂载行业信息
if s.code in industry_map:
s.industry_tdx, s.industry_sw = industry_map[s.code]
all_stocks.append(s)
return all_stocks
def get_security_quotes(
self, stocks: list[tuple[Market, str]]
) -> list[SecurityQuote]:
@@ -416,6 +452,36 @@ class AsyncTdxClient:
async def get_security_list(self, market: Market, start: int) -> list[SecurityInfo]:
return await self._execute(GetSecurityListCmd(market, start))
async def get_security_list_all(self) -> list[SecurityInfo]:
"""获取全市场完整证券列表,并自动挂载行业信息。"""
industry_map = {}
try:
cfg_data = await self.get_report_file("tdxhy.cfg")
if cfg_data:
industry_map = parse_tdxhy_cfg(cfg_data)
except Exception:
pass
all_stocks: list[SecurityInfo] = []
for market in [Market.SH, Market.SZ]:
count = await self.get_security_count(market)
for start in range(0, count, 1000):
stocks = await self.get_security_list(market, start)
for s in stocks:
is_a_share = False
if market == Market.SH:
if s.code.startswith(("60", "68")):
is_a_share = True
elif market == Market.SZ:
if s.code.startswith(("00", "30")):
is_a_share = True
if is_a_share:
if s.code in industry_map:
s.industry_tdx, s.industry_sw = industry_map[s.code]
all_stocks.append(s)
return all_stocks
async def get_security_quotes(
self, stocks: list[tuple[Market, str]]
) -> list[SecurityQuote]:
+23
View File
@@ -0,0 +1,23 @@
"""通达信行业配置文件 (tdxhy.cfg) 解析器。"""
def parse_tdxhy_cfg(content: bytes) -> dict[str, tuple[str, str]]:
"""解析 tdxhy.cfg 字节内容。
返回字典: { "code": (tdx_industry, sw_industry), ... }
"""
results = {}
try:
text = content.decode("gbk", errors="replace")
for line in text.splitlines():
parts = line.strip().split("|")
if len(parts) >= 3:
# 格式: 市场|代码|行业1|||行业2
# 我们只关心 A 股 6 位代码
code = parts[1]
if len(code) == 6:
tdx_ind = parts[2]
sw_ind = parts[5] if len(parts) >= 6 else ""
results[code] = (tdx_ind, sw_ind)
except Exception:
pass
return results
+4
View File
@@ -16,4 +16,8 @@ class SecurityInfo:
decimal_point: int # 价格小数位数
pre_close: float # 昨收价(已修复 pytdx Bug #3:改用正确价格解码)
# 扩展字段(通过 get_security_list_all 关联 tdxhy.cfg 获得)
industry_tdx: str = "" # 通达信行业代码 (如 T1001)
industry_sw: str = "" # 申万行业代码 (如 X500102)
_raw: bytes = field(default=b"", repr=False, compare=False)
+2
View File
@@ -21,8 +21,10 @@ _DEFAULT_TIMEOUT = 15.0
# 已知可用的通达信行情服务器(按优先级排序)
KNOWN_HOSTS: list[str] = [
"180.153.18.170",
"124.71.187.122",
"180.153.18.171",
"180.153.18.172",
"119.147.212.81",
"115.238.56.198",
"115.238.90.165",
"218.75.126.9",