From ad2fc9c84c89326fb3be25bfd9cb9eb72dae25f0 Mon Sep 17 00:00:00 2001 From: M Date: Tue, 14 Apr 2026 22:57:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=85=A8=E9=87=8F=20?= =?UTF-8?q?A=20=E8=82=A1=E5=88=97=E8=A1=A8=E8=8E=B7=E5=8F=96=E4=B8=8E?= =?UTF-8?q?=E8=A1=8C=E4=B8=9A=E4=BF=A1=E6=81=AF=E8=87=AA=E5=8A=A8=E6=8C=82?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 及数据模型变更。 --- README.md | 2 ++ src/xmtdx/client.py | 66 ++++++++++++++++++++++++++++++++++++ src/xmtdx/codec/industry.py | 23 +++++++++++++ src/xmtdx/models/security.py | 4 +++ src/xmtdx/transport/sync.py | 2 ++ 5 files changed, 97 insertions(+) create mode 100644 src/xmtdx/codec/industry.py diff --git a/README.md b/README.md index 6d853aa..88cd712 100644 --- a/README.md +++ b/README.md @@ -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(除权除息) diff --git a/src/xmtdx/client.py b/src/xmtdx/client.py index b7c56b0..f46fcca 100644 --- a/src/xmtdx/client.py +++ b/src/xmtdx/client.py @@ -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]: diff --git a/src/xmtdx/codec/industry.py b/src/xmtdx/codec/industry.py new file mode 100644 index 0000000..7641523 --- /dev/null +++ b/src/xmtdx/codec/industry.py @@ -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 diff --git a/src/xmtdx/models/security.py b/src/xmtdx/models/security.py index b6990d8..de8f8c7 100644 --- a/src/xmtdx/models/security.py +++ b/src/xmtdx/models/security.py @@ -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) diff --git a/src/xmtdx/transport/sync.py b/src/xmtdx/transport/sync.py index f4ba19e..f86dd52 100644 --- a/src/xmtdx/transport/sync.py +++ b/src/xmtdx/transport/sync.py @@ -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",