From 50491f9aae4953eeb7baedc8d707a1ae33da833d Mon Sep 17 00:00:00 2001 From: GitHub Date: Thu, 21 May 2026 23:21:21 +0800 Subject: [PATCH] feat!: rename project from xmtdx to easy-tdx - Package directory: src/xmtdx/ -> src/easy_tdx/ - Import path: from easy_tdx import ... - pip install easy-tdx - Add LICENSE (MIT) with upstream attribution (pytdx, xmtdx) - Add NOTICE with detailed attribution - Update all examples, tests, scripts, docs - Bump version to 1.0.0 BREAKING CHANGE: import path changed from `xmtdx` to `easy_tdx` Co-Authored-By: Claude Opus 4.7 --- .claude/settings.local.json | 6 +- CLAUDE.md | 2 +- LICENSE | 34 + NOTICE | 24 + README.md | 52 +- docs/api_reference.md | 623 ++++++++++++++++++ docs/field_mapping.md | 315 +++++++++ examples/01_connection/async_connect.py | 2 +- examples/01_connection/connect_best_host.py | 2 +- examples/01_connection/ping_servers.py | 2 +- examples/02_market_info/market_stat.py | 2 +- examples/02_market_info/security_count.py | 2 +- examples/02_market_info/security_list.py | 2 +- examples/02_market_info/security_list_all.py | 2 +- examples/02_market_info/security_quotes.py | 2 +- examples/03_kline/index_bars.py | 2 +- examples/03_kline/security_bars.py | 2 +- examples/04_minute/history_minute_data.py | 2 +- examples/04_minute/minute_time_data.py | 2 +- .../05_transaction/history_transaction.py | 2 +- examples/05_transaction/transaction_data.py | 2 +- examples/06_finance/company_info.py | 2 +- examples/06_finance/finance_info.py | 2 +- examples/06_finance/price_limits.py | 2 +- examples/06_finance/xdxr_info.py | 2 +- examples/07_block/block_info.py | 2 +- examples/08_fund_flow/fund_flow.py | 2 +- examples/08_fund_flow/history_fund_flow.py | 2 +- examples/09_file_download/report_file.py | 2 +- examples/10_offline/block_data.py | 6 +- examples/10_offline/daily_bars.py | 4 +- examples/10_offline/detect_home.py | 6 +- examples/10_offline/ex_daily_bars.py | 2 +- examples/10_offline/gbbq.py | 2 +- examples/10_offline/history_financial.py | 2 +- examples/10_offline/min_bars.py | 4 +- pyproject.toml | 8 +- scripts/probe_unknowns.py | 2 +- scripts/verify_fixes_20260415.py | 4 +- src/{xmtdx => easy_tdx}/__init__.py | 8 +- src/{xmtdx => easy_tdx}/_binary.py | 0 src/{xmtdx => easy_tdx}/client.py | 2 +- src/{xmtdx => easy_tdx}/codec/__init__.py | 0 src/{xmtdx => easy_tdx}/codec/block.py | 0 src/{xmtdx => easy_tdx}/codec/datetime_.py | 0 src/{xmtdx => easy_tdx}/codec/financial.py | 0 src/{xmtdx => easy_tdx}/codec/frame.py | 0 src/{xmtdx => easy_tdx}/codec/industry.py | 0 src/{xmtdx => easy_tdx}/codec/price.py | 0 src/{xmtdx => easy_tdx}/codec/price_rules.py | 0 src/{xmtdx => easy_tdx}/codec/volume.py | 0 src/{xmtdx => easy_tdx}/commands/__init__.py | 0 src/{xmtdx => easy_tdx}/commands/base.py | 0 .../commands/block_info.py | 0 .../commands/company_info.py | 0 .../commands/finance_info.py | 0 src/{xmtdx => easy_tdx}/commands/fund_flow.py | 0 .../commands/minute_time.py | 0 .../commands/report_file.py | 0 .../commands/security_bars.py | 0 .../commands/security_count.py | 0 .../commands/security_list.py | 0 .../commands/security_quotes.py | 0 src/{xmtdx => easy_tdx}/commands/setup.py | 0 .../commands/transaction.py | 0 src/{xmtdx => easy_tdx}/commands/xdxr_info.py | 0 src/{xmtdx => easy_tdx}/ex/__init__.py | 2 +- src/{xmtdx => easy_tdx}/ex/client.py | 0 .../ex/commands/__init__.py | 0 .../ex/commands/get_history_bars_range.py | 0 .../ex/commands/get_instrument_bars.py | 0 .../ex/commands/get_instrument_count.py | 0 .../ex/commands/get_instrument_info.py | 0 .../ex/commands/get_instrument_quote.py | 0 .../ex/commands/get_instrument_quote_list.py | 0 .../ex/commands/get_markets.py | 0 .../ex/commands/get_minute_time.py | 0 .../ex/commands/get_transaction.py | 0 src/{xmtdx => easy_tdx}/ex/commands/setup.py | 0 src/{xmtdx => easy_tdx}/ex/models.py | 0 .../ex/transport/__init__.py | 0 .../ex/transport/async_.py | 0 src/{xmtdx => easy_tdx}/ex/transport/sync.py | 0 src/{xmtdx => easy_tdx}/exceptions.py | 4 +- src/{xmtdx => easy_tdx}/models/__init__.py | 0 src/{xmtdx => easy_tdx}/models/bar.py | 0 src/{xmtdx => easy_tdx}/models/enums.py | 0 src/{xmtdx => easy_tdx}/models/finance.py | 0 src/{xmtdx => easy_tdx}/models/quote.py | 0 src/{xmtdx => easy_tdx}/models/security.py | 0 src/{xmtdx => easy_tdx}/models/stats.py | 0 src/{xmtdx => easy_tdx}/models/timeseries.py | 0 src/{xmtdx => easy_tdx}/offline/__init__.py | 0 src/{xmtdx => easy_tdx}/offline/block.py | 0 src/{xmtdx => easy_tdx}/offline/daily_bar.py | 0 .../offline/ex_daily_bar.py | 0 src/{xmtdx => easy_tdx}/offline/finders.py | 0 src/{xmtdx => easy_tdx}/offline/gbbq.py | 0 .../offline/history_financial.py | 0 src/{xmtdx => easy_tdx}/offline/min_bar.py | 0 src/{xmtdx => easy_tdx}/offline/paths.py | 0 src/{xmtdx => easy_tdx}/py.typed | 0 src/{xmtdx => easy_tdx}/transport/__init__.py | 0 src/{xmtdx => easy_tdx}/transport/async_.py | 0 src/{xmtdx => easy_tdx}/transport/sync.py | 0 tests/integration/test_live_client.py | 2 +- tests/unit/test_a_share_extensions.py | 40 +- tests/unit/test_async_transport.py | 8 +- tests/unit/test_block_info.py | 18 +- tests/unit/test_codec_datetime.py | 2 +- tests/unit/test_codec_frame.py | 2 +- tests/unit/test_codec_price.py | 2 +- tests/unit/test_codec_volume.py | 4 +- tests/unit/test_commands_offline.py | 60 +- tests/unit/test_decode_errors.py | 12 +- tests/unit/test_financial_data.py | 6 +- tests/unit/test_heartbeat.py | 6 +- tests/unit/test_protocol_fixes.py | 18 +- tests/unit/test_sync_transport.py | 6 +- 119 files changed, 1175 insertions(+), 167 deletions(-) create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 docs/api_reference.md create mode 100644 docs/field_mapping.md rename src/{xmtdx => easy_tdx}/__init__.py (88%) rename src/{xmtdx => easy_tdx}/_binary.py (100%) rename src/{xmtdx => easy_tdx}/client.py (99%) rename src/{xmtdx => easy_tdx}/codec/__init__.py (100%) rename src/{xmtdx => easy_tdx}/codec/block.py (100%) rename src/{xmtdx => easy_tdx}/codec/datetime_.py (100%) rename src/{xmtdx => easy_tdx}/codec/financial.py (100%) rename src/{xmtdx => easy_tdx}/codec/frame.py (100%) rename src/{xmtdx => easy_tdx}/codec/industry.py (100%) rename src/{xmtdx => easy_tdx}/codec/price.py (100%) rename src/{xmtdx => easy_tdx}/codec/price_rules.py (100%) rename src/{xmtdx => easy_tdx}/codec/volume.py (100%) rename src/{xmtdx => easy_tdx}/commands/__init__.py (100%) rename src/{xmtdx => easy_tdx}/commands/base.py (100%) rename src/{xmtdx => easy_tdx}/commands/block_info.py (100%) rename src/{xmtdx => easy_tdx}/commands/company_info.py (100%) rename src/{xmtdx => easy_tdx}/commands/finance_info.py (100%) rename src/{xmtdx => easy_tdx}/commands/fund_flow.py (100%) rename src/{xmtdx => easy_tdx}/commands/minute_time.py (100%) rename src/{xmtdx => easy_tdx}/commands/report_file.py (100%) rename src/{xmtdx => easy_tdx}/commands/security_bars.py (100%) rename src/{xmtdx => easy_tdx}/commands/security_count.py (100%) rename src/{xmtdx => easy_tdx}/commands/security_list.py (100%) rename src/{xmtdx => easy_tdx}/commands/security_quotes.py (100%) rename src/{xmtdx => easy_tdx}/commands/setup.py (100%) rename src/{xmtdx => easy_tdx}/commands/transaction.py (100%) rename src/{xmtdx => easy_tdx}/commands/xdxr_info.py (100%) rename src/{xmtdx => easy_tdx}/ex/__init__.py (68%) rename src/{xmtdx => easy_tdx}/ex/client.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/__init__.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_history_bars_range.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_instrument_bars.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_instrument_count.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_instrument_info.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_instrument_quote.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_instrument_quote_list.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_markets.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_minute_time.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/get_transaction.py (100%) rename src/{xmtdx => easy_tdx}/ex/commands/setup.py (100%) rename src/{xmtdx => easy_tdx}/ex/models.py (100%) rename src/{xmtdx => easy_tdx}/ex/transport/__init__.py (100%) rename src/{xmtdx => easy_tdx}/ex/transport/async_.py (100%) rename src/{xmtdx => easy_tdx}/ex/transport/sync.py (100%) rename src/{xmtdx => easy_tdx}/exceptions.py (86%) rename src/{xmtdx => easy_tdx}/models/__init__.py (100%) rename src/{xmtdx => easy_tdx}/models/bar.py (100%) rename src/{xmtdx => easy_tdx}/models/enums.py (100%) rename src/{xmtdx => easy_tdx}/models/finance.py (100%) rename src/{xmtdx => easy_tdx}/models/quote.py (100%) rename src/{xmtdx => easy_tdx}/models/security.py (100%) rename src/{xmtdx => easy_tdx}/models/stats.py (100%) rename src/{xmtdx => easy_tdx}/models/timeseries.py (100%) rename src/{xmtdx => easy_tdx}/offline/__init__.py (100%) rename src/{xmtdx => easy_tdx}/offline/block.py (100%) rename src/{xmtdx => easy_tdx}/offline/daily_bar.py (100%) rename src/{xmtdx => easy_tdx}/offline/ex_daily_bar.py (100%) rename src/{xmtdx => easy_tdx}/offline/finders.py (100%) rename src/{xmtdx => easy_tdx}/offline/gbbq.py (100%) rename src/{xmtdx => easy_tdx}/offline/history_financial.py (100%) rename src/{xmtdx => easy_tdx}/offline/min_bar.py (100%) rename src/{xmtdx => easy_tdx}/offline/paths.py (100%) rename src/{xmtdx => easy_tdx}/py.typed (100%) rename src/{xmtdx => easy_tdx}/transport/__init__.py (100%) rename src/{xmtdx => easy_tdx}/transport/async_.py (100%) rename src/{xmtdx => easy_tdx}/transport/sync.py (100%) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 1e6655c..bbe7f0b 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -11,7 +11,11 @@ "Bash(git push *)", "mcp__zread__get_repo_structure", "Bash(pip install *)", - "mcp__web-reader__webReader" + "mcp__web-reader__webReader", + "Bash(python3 *)", + "Bash(where mypy *)", + "Bash(pip list *)", + "Bash(uv run *)" ] } } diff --git a/CLAUDE.md b/CLAUDE.md index 0b1c1cd..2818a58 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,7 +22,7 @@ ruff format --check src/ tests/ ## 架构 ``` -src/xmtdx/ +src/easy_tdx/ ├── client.py # TdxClient / AsyncTdxClient(高层 API) ├── transport/ │ ├── sync.py # TdxConnection(socket)+ ping_host / ping_all diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f06ca2e --- /dev/null +++ b/LICENSE @@ -0,0 +1,34 @@ +MIT License + +Copyright (c) 2025-present easy-tdx contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +This project contains code inspired by or derived from the following projects, +each originally released under the MIT License: + +1. pytdx — https://github.com/rainx/pytdx + Copyright (c) rainx and contributors + Licensed under the MIT License. + +2. xmtdx — https://github.com/minionszyw/xmtdx + Copyright (c) minionszyw and contributors + Licensed under the MIT License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..f7b6288 --- /dev/null +++ b/NOTICE @@ -0,0 +1,24 @@ +easy-tdx +======== + +This project is an independent implementation of the TongDaXin (TDX) +TCP protocol client for Chinese A-share market data. + +Attribution +----------- + +This project was inspired by and incorporates ideas from: + +- pytdx (https://github.com/rainx/pytdx) + The offline data reading module (daily bars, minute bars, block data, + gbbq share structure changes, historical financial data) borrows file + format parsing methods from pytdx. The gbbq XOR decryption key is + directly ported from pytdx. + +- xmtdx (https://github.com/minionszyw/xmtdx) + The initial project structure and online protocol implementation were + based on xmtdx, which has since been substantially rewritten with bug + fixes, new features, and a different architecture. + +Both upstream projects are licensed under the MIT License. +Their original copyright notices are preserved in the LICENSE file. diff --git a/README.md b/README.md index fae2676..ea6466e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ -# xmtdx +# easy-tdx + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) 通达信 TCP 协议行情数据客户端,零运行时依赖。支持在线行情获取和离线本地数据读取。 -pytdx 年久失修:多处已知解析 bug、Python 2 包袱、无类型注解、大量未知字段被静默丢弃。xmtdx 重新实现协议,修复已知 bug,保留全部原始字节与未知字段供后续逆向分析。 +easy-tdx 是从 [xmtdx](https://github.com/minionszyw/xmtdx) 项目独立出来的全新版本,在线协议实现经过大幅重写并修复了大量解析 bug,同时新增了扩展行情、离线数据读取、专业财务数据等完整功能模块。 -离线数据读取模块借鉴了 [pytdx](https://github.com/rainx/pytdx) 的数据格式解析方法,感谢 pytdx 项目的贡献。 +本项目深受以下开源项目启发,感谢它们的贡献: + +- [pytdx](https://github.com/rainx/pytdx) — 离线数据读取模块(日线、分钟线、板块、股本变迁、历史财务的文件格式解析方法)借鉴自 pytdx +- [xmtdx](https://github.com/minionszyw/xmtdx) — 初始项目结构和在线协议实现的原型 + +详见 [NOTICE](NOTICE) 和 [LICENSE](LICENSE) 文件。 ## 特性 @@ -32,7 +39,7 @@ pip install -e ".[pandas]" # 含 pandas(可选) ### 连接与基本查询 ```python -from xmtdx import TdxClient, Market, KlineCategory +from easy_tdx import TdxClient, Market, KlineCategory # 手动指定服务器 with TdxClient("180.153.18.170") as c: @@ -52,7 +59,7 @@ with TdxClient.from_best_host() as c: ```python import asyncio -from xmtdx import AsyncTdxClient, Market, KlineCategory +from easy_tdx import AsyncTdxClient, Market, KlineCategory async def main(): async with AsyncTdxClient.from_best_host() as c: @@ -70,7 +77,7 @@ asyncio.run(main()) ### 服务器测速 ```python -from xmtdx import TdxClient +from easy_tdx import TdxClient # 测速并排序 results = TdxClient.ping_all() @@ -122,7 +129,7 @@ with TdxClient.from_best_host() as c: ### K 线数据 ```python -from xmtdx import Market, KlineCategory +from easy_tdx import Market, KlineCategory with TdxClient.from_best_host() as c: # 个股 K 线 @@ -170,7 +177,7 @@ with TdxClient.from_best_host() as c: ### 财务与公司信息 ```python -from xmtdx import XDXR_CATEGORY_NAMES +from easy_tdx import XDXR_CATEGORY_NAMES with TdxClient.from_best_host() as c: # 除权除息历史 @@ -227,7 +234,7 @@ with TdxClient.from_best_host() as c: ### 文件下载 ```python -from xmtdx import CALC_HOSTS +from easy_tdx import CALC_HOSTS with TdxClient.from_best_host() as c: # 行情服务器可用的文件 @@ -247,7 +254,7 @@ with TdxClient.from_best_host() as c: ### 扩展行情(期货、港股、外盘) ```python -from xmtdx import ExTdxClient +from easy_tdx import ExTdxClient # 扩展行情服务器端口 7727 with ExTdxClient() as c: @@ -285,8 +292,8 @@ export TDX_HOME=/opt/new_tdx ### 日线 K 线 ```python -from xmtdx.offline import detect_tdx_home, read_daily_bars, find_daily_bar_file -from xmtdx import Market +from easy_tdx.offline import detect_tdx_home, read_daily_bars, find_daily_bar_file +from easy_tdx import Market home = detect_tdx_home() @@ -304,11 +311,11 @@ for bar in bars[-10:]: ### 分钟 K 线 ```python -from xmtdx.offline import ( +from easy_tdx.offline import ( read_5min_bars, read_lc_min_bars, find_5min_bar_file, find_lc1_bar_file, find_lc5_bar_file, ) -from xmtdx import Market +from easy_tdx import Market # .5 文件(OHLC 为整数 / 100) filepath = find_5min_bar_file(Market.SH, "600000") @@ -328,7 +335,7 @@ bars = read_lc_min_bars(filepath) ### 扩展市场日线 ```python -from xmtdx.offline import read_ex_daily_bars +from easy_tdx.offline import read_ex_daily_bars # 期货、港股、外盘等扩展市场数据 # 文件位于 vipdoc/ds/lday/,如 29#A1801.day @@ -339,7 +346,7 @@ bars = read_ex_daily_bars(r"C:\new_jyplug\vipdoc\ds\lday\38#2_CPI.day") ### 板块数据 ```python -from xmtdx.offline import read_block_dat, read_customer_blocks +from easy_tdx.offline import read_block_dat, read_customer_blocks # 系统板块(本地 .dat 文件) blocks = read_block_dat(r"C:\new_jyplug\vipdoc\block_zs.dat") @@ -355,7 +362,7 @@ blocks = read_customer_blocks(r"C:\new_jyplug\T0002\blocknew") ### 股本变迁 ```python -from xmtdx.offline import read_gbbq +from easy_tdx.offline import read_gbbq records = read_gbbq(r"C:\new_jyplug\T0002\hq_cache\gbbq") # records[0].market / .code / .datetime / .category / .hongli_panqianliutong / ... @@ -366,7 +373,7 @@ gbbq 文件使用 XOR 加密存储,读取时自动解密。 ### 历史财务数据 ```python -from xmtdx.offline import read_history_financial +from easy_tdx.offline import read_history_financial # 支持 .dat 和 .zip 文件(.zip 自动解压) records = read_history_financial(r"C:\new_jyplug\vipdoc\fin\gpcw20260331.zip") @@ -378,7 +385,7 @@ records = read_history_financial(r"C:\new_jyplug\vipdoc\fin\gpcw20260331.zip") ### 路径检测 ```python -from xmtdx.offline import detect_tdx_home, resolve_vipdoc +from easy_tdx.offline import detect_tdx_home, resolve_vipdoc # 自动检测通达信安装目录 home = detect_tdx_home() @@ -445,7 +452,7 @@ vipdoc/ | `get_transaction_data(market, code, start, count)` | 逐笔成交 | | `get_history_transaction_data(market, code, date, start, count)` | 历史逐笔 | -### xmtdx.offline +### easy_tdx.offline | 函数 | 说明 | |------|------| @@ -577,7 +584,7 @@ name category count codes ## 架构 ``` -src/xmtdx/ +src/easy_tdx/ ├── client.py # TdxClient / AsyncTdxClient(高层 API) ├── ex/ │ ├── client.py # ExTdxClient / AsyncExTdxClient(扩展行情) @@ -630,5 +637,6 @@ ruff format --check src/ tests/ ## 致谢 -- [pytdx](https://github.com/rainx/pytdx) — 离线数据读取模块(日线、分钟线、板块、股本变迁、历史财务的文件格式解析方法)借鉴自 pytdx 项目,感谢 rainx 及所有贡献者的工作 +- [pytdx](https://github.com/rainx/pytdx) — 离线数据读取模块(日线、分钟线、板块、股本变迁、历史财务的文件格式解析方法)借鉴自 pytdx 项目,感谢 rainx 及所有贡献者 +- [xmtdx](https://github.com/minionszyw/xmtdx) — 本项目的初始原型,感谢 minionszyw 的工作 - 通达信协议分析离不开开源社区的逆向工程成果 diff --git a/docs/api_reference.md b/docs/api_reference.md new file mode 100644 index 0000000..ac662d3 --- /dev/null +++ b/docs/api_reference.md @@ -0,0 +1,623 @@ +# easy_tdx API 参考文档 + +> 版本: 0.1.1 | 纯标准库,零运行时依赖 | 需要网络连接通达信行情服务器 + +## 目录 + +- [快速开始](#快速开始) +- [客户端](#客户端) + - [TdxClient(同步)](#tdxclient同步) + - [AsyncTdxClient(异步)](#asynctdxclient异步) +- [连接与服务器选择](#连接与服务器选择) +- [市场信息](#市场信息) +- [K 线数据](#k-线数据) +- [分时数据](#分时数据) +- [逐笔成交](#逐笔成交) +- [财务与公司信息](#财务与公司信息) +- [板块信息](#板块信息) +- [资金流向](#资金流向) +- [文件下载](#文件下载) +- [市场统计](#市场统计) +- [数据模型](#数据模型) +- [枚举](#枚举) +- [异常](#异常) +- [涨跌停价计算](#涨跌停价计算) + +--- + +## 快速开始 + +```python +from easy_tdx import TdxClient, Market, KlineCategory + +# 自动选择最优服务器 +with TdxClient.from_best_host() as c: + # 沪市证券总数 + count = c.get_security_count(Market.SH) + + # 浦发银行日K线 + bars = c.get_security_bars(Market.SH, "600000", KlineCategory.DAY, 0, 10) + + # 实时行情 + quotes = c.get_security_quotes([(Market.SH, "600000"), (Market.SZ, "000001")]) +``` + +--- + +## 客户端 + +### TdxClient(同步) + +```python +TdxClient(host, port=7709, timeout=15.0, auto_reconnect=True) +``` + +| 参数 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| host | `str` | `KNOWN_HOSTS[0]` | 服务器 IP 地址 | +| port | `int` | `7709` | 服务器端口 | +| timeout | `float` | `15.0` | 连接/读写超时(秒) | +| auto_reconnect | `bool` | `True` | 断线自动重连 | + +支持上下文管理器:`with TdxClient(...) as c:` + +#### 工厂方法 + +```python +TdxClient.from_best_host(hosts=KNOWN_HOSTS, port=7709, timeout=15.0, + ping_timeout=5.0, auto_reconnect=True) +``` + +测量 `hosts` 中所有服务器延迟,选择最低延迟的建立连接。若全部不可达,回退到 `hosts[0]`。 + +### AsyncTdxClient(异步) + +```python +AsyncTdxClient(host, port=7709, timeout=15.0, auto_reconnect=True, heartbeat_interval=60.0) +``` + +| 参数 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| heartbeat_interval | `float` | `60.0` | 心跳间隔(秒),≤0 禁用 | + +所有方法均为 `async def`,使用 `await` 调用。支持异步上下文管理器:`async with AsyncTdxClient(...) as c:` + +> **注意**:单个 AsyncTdxClient 仅维护一条 TCP 连接,并发调用在连接内串行执行。 + +--- + +## 连接与服务器选择 + +### ping_all + +```python +TdxClient.ping_all(hosts=KNOWN_HOSTS, port=7709, timeout=5.0) -> list[tuple[str, float]] +``` + +测量多台服务器延迟,返回按延迟升序排列的 `(host, seconds)` 列表。 + +**示例**: +```python +results = TdxClient.ping_all() +for host, latency in results: + print(f"{host}: {latency * 1000:.1f} ms") +``` + +### connect / close + +```python +c.connect() # 建立连接 +c.close() # 关闭连接 +``` + +建议使用上下文管理器自动管理。 + +--- + +## 市场信息 + +### get_security_count + +```python +c.get_security_count(market: Market) -> int +``` + +获取指定市场的证券总数。 + +| 参数 | 类型 | 说明 | +|------|------|------| +| market | `Market` | 市场代码(SZ/SH/BJ) | + +### get_security_list + +```python +c.get_security_list(market: Market, start: int) -> list[SecurityInfo] +``` + +获取证券列表(每页约 1000 条)。 + +| 参数 | 类型 | 说明 | +|------|------|------| +| market | `Market` | 市场代码 | +| start | `int` | 分页偏移量(0, 1000, 2000, ...) | + +### get_security_list_all + +```python +c.get_security_list_all() -> list[SecurityInfo] +``` + +获取沪深 A 股完整列表,自动挂载行业信息(通达信行业 + 申万行业)。 + +**注意**: +- 内部会拉取 `tdxhy.cfg` 并遍历全部证券,耗时较长 +- `Market.BJ` 因服务器端问题暂不纳入 + +**A股过滤规则**: +- 沪市:60xxxx(主板)、68xxxx(科创板) +- 深市:00xxxx(主板)、30xxxx(创业板) + +### get_security_quotes + +```python +c.get_security_quotes(stocks: list[tuple[Market, str]]) -> list[SecurityQuote] +``` + +批量获取实时五档行情,**最多 80 只/次**。 + +| 参数 | 类型 | 说明 | +|------|------|------| +| stocks | `list[tuple[Market, str]]` | (市场, 代码) 列表 | + +--- + +## K 线数据 + +### get_security_bars + +```python +c.get_security_bars(market: Market, code: str, category: KlineCategory, + start: int, count: int = 800) -> list[SecurityBar] +``` + +获取个股 K 线数据。 + +| 参数 | 类型 | 说明 | +|------|------|------| +| market | `Market` | 市场代码 | +| code | `str` | 证券代码(如 "600000") | +| category | `KlineCategory` | K 线周期 | +| start | `int` | 分页偏移(0 为最新) | +| count | `int` | 请求数量(最多 800) | + +### get_index_bars + +```python +c.get_index_bars(market: Market, code: str, category: KlineCategory, + start: int, count: int = 800) -> list[SecurityBar] +``` + +获取指数 K 线数据。参数同 `get_security_bars`。 + +**常用指数**: +| 指数 | market | code | +|------|--------|------| +| 上证指数 | SH | 000001 | +| 深证成指 | SZ | 399001 | +| 创业板指 | SZ | 399006 | +| 沪深300 | SH | 000300 | + +--- + +## 分时数据 + +### get_minute_time_data + +```python +c.get_minute_time_data(market: Market, code: str) -> list[MinuteBar] +``` + +获取今日分时数据(240 条)。内部优先尝试历史接口,失败后回退到实时接口。 + +### get_history_minute_time_data + +```python +c.get_history_minute_time_data(market: Market, code: str, date: int) -> list[MinuteBar] +``` + +获取历史某日分时数据。 + +| 参数 | 类型 | 说明 | +|------|------|------| +| date | `int` | YYYYMMDD 格式(如 20250110) | + +--- + +## 逐笔成交 + +### get_transaction_data + +```python +c.get_transaction_data(market: Market, code: str, + start: int, count: int = 800) -> list[TransactionRecord] +``` + +获取当日逐笔成交。 + +### get_history_transaction_data + +```python +c.get_history_transaction_data(market: Market, code: str, date: int, + start: int, count: int = 800) -> list[TransactionRecord] +``` + +获取历史逐笔成交。 + +| 参数 | 类型 | 说明 | +|------|------|------| +| date | `int` | YYYYMMDD 格式 | +| start | `int` | 分页偏移 | +| count | `int` | 请求数量(最多 800) | + +--- + +## 财务与公司信息 + +### get_xdxr_info + +```python +c.get_xdxr_info(market: Market, code: str) -> list[XdxrRecord] +``` + +获取除权除息历史记录。返回值按时间排序,包含分红、送股、配股、股本变动等。 + +### get_finance_info + +```python +c.get_finance_info(market: Market, code: str) -> FinanceInfo +``` + +获取最新财务数据,包含股本结构、资产负债、利润指标等。 + +### get_company_info_category + +```python +c.get_company_info_category(market: Market, code: str) -> list[CompanyInfoCategory] +``` + +获取公司信息文件目录,返回可用的文件名、起始偏移和长度。 + +### get_company_info_content + +```python +c.get_company_info_content(market: Market, code: str, filename: str, + offset: int, length: int) -> str +``` + +读取公司信息文本内容。需先通过 `get_company_info_category` 获取文件名和长度。 + +--- + +## 板块信息 + +### get_block_info + +```python +c.get_block_info(filename: str) -> list[TdxBlock] +``` + +获取并解析板块文件。 + +**常用文件名**: +| 文件名 | 说明 | +|--------|------| +| `block_zs.dat` | 行业/指数板块 | +| `block_gn.dat` | 概念板块 | +| `block_fg.dat` | 风格板块 | + +--- + +## 资金流向 + +### get_fund_flow + +```python +c.get_fund_flow(market: Market, code: str) -> FundFlow +``` + +获取个股当日资金流向(基于 L1 逐笔数据统计)。 + +**资金分级**: +| 级别 | 单笔成交额 | +|------|-----------| +| 超大单 | > 100 万 | +| 大单 | 20 ~ 100 万 | +| 中单 | 4 ~ 20 万 | +| 小单 | ≤ 4 万 | + +### get_history_fund_flow + +```python +c.get_history_fund_flow(market: Market, code: str, + start: int, count: int) -> list[HistoricalFundFlow] +``` + +获取历史日线资金流向序列。优先走直连接口,若服务器不支持则自动回退为逐笔成交重算。 + +--- + +## 文件下载 + +### get_report_file + +```python +c.get_report_file(filename: str) -> bytes +``` + +从服务器拉取大文件(分块传输)。 + +**常用文件**: +| 文件名 | 说明 | +|--------|------| +| `base_info.zip` | 基础信息包 | +| `tdxhy.cfg` | 行业映射配置 | + +--- + +## 市场统计 + +### get_market_stat + +```python +c.get_market_stat() -> MarketStat +``` + +获取 A 股全市场涨跌统计(基于 880005 行情统计代码)。 + +**注意**:`suspended_count` 是 `total - up - down - neutral` 的残差估算值。 + +--- + +## 数据模型 + +### SecurityInfo + +证券列表条目。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| market | `Market` | 市场代码 | +| code | `str` | 证券代码 | +| name | `str` | 证券名称 | +| volunit | `int` | 成交量单位(手 = volunit 股) | +| decimal_point | `int` | 价格小数位数 | +| pre_close | `float` | 昨收价 | +| industry_tdx | `str` | 通达信行业代码(扩展字段) | +| industry_sw | `str` | 申万行业代码(扩展字段) | + +### SecurityQuote + +实时五档行情。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| market | `Market` | 市场代码 | +| code | `str` | 证券代码 | +| price | `float` | 现价 | +| pre_close | `float` | 昨收 | +| open | `float` | 今开 | +| high | `float` | 最高 | +| low | `float` | 最低 | +| vol | `float` | 总成交量(手) | +| amount | `float` | 成交额(元) | +| bid1~bid5 | `float` | 买一到买五价 | +| bid_vol1~bid_vol5 | `float` | 买一到买五量 | +| ask1~ask5 | `float` | 卖一到卖五价 | +| ask_vol1~ask_vol5 | `float` | 卖一到卖五量 | +| s_vol | `float` | 内盘(主动卖) | +| b_vol | `float` | 外盘(主动买) | +| rise_speed | `float` | 涨速 | +| server_time | `str` | 服务器时间 | + +### SecurityBar + +K 线数据。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| open | `float` | 开盘价 | +| close | `float` | 收盘价 | +| high | `float` | 最高价 | +| low | `float` | 最低价 | +| vol | `float` | 成交量(股) | +| amount | `float` | 成交额(元) | +| year | `int` | 年 | +| month | `int` | 月 | +| day | `int` | 日 | +| hour | `int` | 时 | +| minute | `int` | 分 | +| datetime_str | `str` | 属性,格式化时间字符串 | + +### MinuteBar + +分时数据。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| price | `float` | 价格 | +| vol | `int` | 成交量 | + +### TransactionRecord + +逐笔成交。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| hour | `int` | 时 | +| minute | `int` | 分 | +| price | `float` | 成交价 | +| vol | `int` | 成交量 | +| buyorsell | `int` | 方向(0=买, 1=卖, 2=中性, 8=集合竞价) | + +### XdxrRecord + +除权除息记录。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| market | `Market` | 市场 | +| code | `str` | 代码 | +| year/month/day | `int` | 日期 | +| category | `int` | 事件类型(见 XDXR_CATEGORY_NAMES) | +| fenhong | `float \| None` | 每股分红(元) | +| peigujia | `float \| None` | 配股价 | +| songzhuangu | `float \| None` | 每股送转股比例 | +| peigu | `float \| None` | 每股配股比例 | + +### FinanceInfo + +最新财务数据。包含股本结构(流通股本、总股本、国家股等)、资产负债(总资产、净资产等)、利润指标(主营收入、净利润等)和每股指标。字段名使用拼音,完整列表见源码 `models/finance.py`。 + +### CompanyInfoCategory + +公司信息文件目录。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| name | `str` | 目录名 | +| filename | `str` | 文件名 | +| start | `int` | 起始偏移 | +| length | `int` | 内容长度 | + +### TdxBlock + +板块信息。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| name | `str` | 板块名称 | +| category | `int` | 分类(0=行业, 1=地域, 2=概念, 3=风格) | +| count | `int` | 成分股数量 | +| codes | `list[str]` | 成分股代码列表 | + +### MarketStat + +市场统计。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| up_count | `int` | 上涨家数 | +| down_count | `int` | 下跌家数 | +| neutral_count | `int` | 平盘家数 | +| suspended_count | `int` | 停牌估算 | +| total_count | `int` | 总计 | +| total_amount | `float` | 总成交额 | +| total_volume | `float` | 总成交量 | +| total_market_cap | `float` | 总市值(元),来自 880001 收盘价 | +| limit_up_count | `int` | 涨停家数,来自 880006 close | +| limit_down_count | `int` | 跌停家数,来自 880006 open | + +### FundFlow + +资金流向。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| super_in / super_out | `float` | 超大单流入/流出 | +| large_in / large_out | `float` | 大单流入/流出 | +| medium_in / medium_out | `float` | 中单流入/流出 | +| small_in / small_out | `float` | 小单流入/流出 | +| main_net_inflow | `float` | 属性:主力净流入(超大+大) | +| total_net_inflow | `float` | 属性:全单净流入 | + +### HistoricalFundFlow + +历史日线资金流向。字段同 FundFlow,额外包含 `year`/`month`/`day` 日期字段。 + +--- + +## 枚举 + +### Market + +| 值 | 名称 | 说明 | +|----|------|------| +| 0 | SZ | 深圳 | +| 1 | SH | 上海 | +| 2 | BJ | 北京 | + +### KlineCategory + +| 值 | 名称 | 说明 | +|----|------|------| +| 0 | MIN_5 | 5 分钟 | +| 1 | MIN_15 | 15 分钟 | +| 2 | MIN_30 | 30 分钟 | +| 3 | MIN_60 | 60 分钟 | +| 4 | DAY | 日线 | +| 5 | WEEK | 周线 | +| 6 | MONTH | 月线 | +| 7 | MIN_1 | 1 分钟 | +| 8 | MIN_3 | 3 分钟(内部用) | +| 9 | YEAR | 年线 | +| 10 | SEASON | 季线 | +| 11 | YEAR_ALT | 年线(备用) | + +--- + +## 异常 + +所有异常继承自 `TdxError`。 + +| 异常 | 说明 | +|------|------| +| `TdxError` | 基础异常 | +| `TdxConnectionError` | 连接错误(断线、超时等) | +| `TdxDecodeError` | 数据解析错误 | +| `TdxCommandError` | 命令执行错误 | + +--- + +## 涨跌停价计算 + +### get_price_limits + +```python +c.get_price_limits(market: Market, code: str, name: str, + pre_close: float) -> tuple[float | None, float | None] +``` + +按交易规则计算涨跌停价。返回 `(涨停价, 跌停价)`,不适用时对应位置为 `None`。 + +内部逻辑: +- 自动检测上市初期不设涨跌幅限制的窗口期 +- 通过日 K 线条数估算已上市交易天数 +- 调用 `compute_price_limits()` 执行规则计算 + +### compute_price_limits(独立函数) + +```python +from easy_tdx.codec.price_rules import compute_price_limits + +compute_price_limits(market, code, name, pre_close, listed_days=None) + -> tuple[float | None, float | None] +``` + +涨跌幅规则: +| 类型 | 涨跌幅 | +|------|--------| +| 主板(60/00) | ±10% | +| 科创板(68) | ±20% | +| 创业板(30) | ±20% | +| ST 股 | ±5% | +| 上市首 N 日 | 不设限制 | + +--- + +## 全局常量 + +| 常量 | 类型 | 说明 | +|------|------|------| +| `KNOWN_HOSTS` | `list[str]` | A 股行情服务器列表 | +| `KNOWN_EX_HOSTS` | `list[str]` | 扩展行情服务器列表 | +| `XDXR_CATEGORY_NAMES` | `dict[int, str]` | 除权除息事件类型映射 | diff --git a/docs/field_mapping.md b/docs/field_mapping.md new file mode 100644 index 0000000..31ebc99 --- /dev/null +++ b/docs/field_mapping.md @@ -0,0 +1,315 @@ +# easy_tdx 字段映射表 + +> 模型字段名 ↔ 中文含义 ↔ 数据类型对照 + +--- + +## SecurityInfo(证券列表条目) + +来源:`get_security_list()` / `get_security_list_all()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `market` | 市场 | `Market` | SZ=深圳, SH=上海, BJ=北京 | +| `code` | 证券代码 | `str` | 6 位代码,如 "600000" | +| `name` | 证券名称 | `str` | GBK 解码 | +| `volunit` | 成交量单位 | `int` | 1 手 = volunit 股 | +| `decimal_point` | 价格小数位 | `int` | 通常为 2 | +| `pre_close` | 昨收价 | `float` | 通达信自定义浮点 | +| `industry_tdx` | 通达信行业 | `str` | 如 "T1001",需 `get_security_list_all()` | +| `industry_sw` | 申万行业 | `str` | 如 "X500102",需 `get_security_list_all()` | + +--- + +## SecurityQuote(实时五档行情) + +来源:`get_security_quotes()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `market` | 市场 | `Market` | | +| `code` | 证券代码 | `str` | | +| `price` | 现价 | `float` | 当前最新成交价 | +| `pre_close` | 昨收价 | `float` | 昨日收盘价 | +| `open` | 今开 | `float` | 今日开盘价 | +| `high` | 最高 | `float` | 今日最高价 | +| `low` | 最低 | `float` | 今日最低价 | +| `vol` | 总成交量 | `float` | 单位:手 | +| `cur_vol` | 当前成交量 | `float` | | +| `amount` | 成交额 | `float` | 单位:元 | +| `s_vol` | 内盘 | `float` | 主动卖出成交量 | +| `b_vol` | 外盘 | `float` | 主动买入成交量 | +| `active1` | 活跃度1 | `int` | 含义待确认 | +| `active2` | 活跃度2 | `int` | 含义待确认 | +| `bid1` ~ `bid5` | 买一~买五价 | `float` | | +| `bid_vol1` ~ `bid_vol5` | 买一~买五量 | `float` | | +| `ask1` ~ `ask5` | 卖一~卖五价 | `float` | | +| `ask_vol1` ~ `ask_vol5` | 卖一~卖五量 | `float` | | +| `rise_speed` | 涨速 | `float` | | +| `limit_up` | 涨停价 | `float \| None` | 需 `get_price_limits()` 计算 | +| `limit_down` | 跌停价 | `float \| None` | 需 `get_price_limits()` 计算 | +| `server_time` | 服务器时间 | `str` | 格式 HH:MM:SS.mmm | + +--- + +## SecurityBar(K 线数据) + +来源:`get_security_bars()` / `get_index_bars()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `open` | 开盘价 | `float` | | +| `close` | 收盘价 | `float` | | +| `high` | 最高价 | `float` | | +| `low` | 最低价 | `float` | | +| `vol` | 成交量 | `float` | 单位:股 | +| `amount` | 成交额 | `float` | 单位:元 | +| `year` | 年 | `int` | | +| `month` | 月 | `int` | | +| `day` | 日 | `int` | | +| `hour` | 时 | `int` | 日线为 0 | +| `minute` | 分 | `int` | 日线为 0 | +| `datetime_str` | 时间字符串 | `str` | 属性,格式 YYYY-MM-DD HH:MM | + +--- + +## MinuteBar(分时数据) + +来源:`get_minute_time_data()` / `get_history_minute_time_data()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `price` | 价格 | `float` | | +| `vol` | 成交量 | `int` | | + +--- + +## TransactionRecord(逐笔成交) + +来源:`get_transaction_data()` / `get_history_transaction_data()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `hour` | 时 | `int` | | +| `minute` | 分 | `int` | | +| `price` | 成交价 | `float` | | +| `vol` | 成交量 | `int` | | +| `buyorsell` | 买卖方向 | `int` | 0=买, 1=卖, 2=中性, 8=集合竞价 | + +--- + +## XdxrRecord(除权除息记录) + +来源:`get_xdxr_info()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `market` | 市场 | `Market` | | +| `code` | 证券代码 | `str` | | +| `year` | 年 | `int` | | +| `month` | 月 | `int` | | +| `day` | 日 | `int` | | +| `category` | 事件类型 | `int` | 见 XDXR_CATEGORY_NAMES | +| `name` | 事件名称 | `str` | | +| `fenhong` | 每股分红 | `float \| None` | 元,category=1 | +| `peigujia` | 配股价 | `float \| None` | 元/股,category=1 | +| `songzhuangu` | 每股送转股比例 | `float \| None` | category=1 | +| `peigu` | 每股配股比例 | `float \| None` | category=1 | +| `suogu` | 缩股比例 | `float \| None` | category=11,12 | +| `xingquanjia` | 行权价 | `float \| None` | category=13,14 | +| `fenshu` | 分数 | `float \| None` | category=13,14 | +| `panqian_liutong` | 盘前流通股本 | `float \| None` | 万股,category=2~10 | +| `panhou_liutong` | 盘后流通股本 | `float \| None` | 万股,category=2~10 | +| `qian_zongguben` | 前总股本 | `float \| None` | 万股,category=2~10 | +| `hou_zongguben` | 后总股本 | `float \| None` | 万股,category=2~10 | + +### XDXR_CATEGORY_NAMES(事件类型映射) + +| category | 名称 | +|----------|------| +| 1 | 除权除息 | +| 2 | 送配股上市 | +| 3 | 非流通股上市 | +| 4 | 未知股本变动 | +| 5 | 股本变化 | +| 6 | 增发新股 | +| 7 | 股份回购 | +| 8 | 增发新股上市 | +| 9 | 转配股上市 | +| 10 | 可转债上市 | +| 11 | 扩缩股 | +| 12 | 非流通股缩股 | +| 13 | 送认购权证 | +| 14 | 送认沽权证 | + +--- + +## FinanceInfo(最新财务数据) + +来源:`get_finance_info()` + +### 股本结构(单位:万股) + +| 字段名 | 中文 | +|--------|------| +| `liutong_guben` | 流通股本 | +| `zong_guben` | 总股本 | +| `guojia_gu` | 国家股 | +| `faqiren_faren_gu` | 发起人法人股 | +| `faren_gu` | 法人股 | +| `b_gu` | B股 | +| `h_gu` | H股 | +| `zhigong_gu` | 职工股 | + +### 基本信息 + +| 字段名 | 中文 | 类型 | +|--------|------|------| +| `province` | 省份代码 | `int` | +| `industry` | 行业代码 | `int` | +| `updated_date` | 财务更新日期 | `int` | YYYYMMDD | +| `ipo_date` | 上市日期 | `int` | YYYYMMDD | +| `gudong_renshu` | 股东人数 | `float` | + +### 资产负债(单位:元) + +| 字段名 | 中文 | +|--------|------| +| `zong_zichan` | 总资产 | +| `liudong_zichan` | 流动资产 | +| `guding_zichan` | 固定资产 | +| `wuxing_zichan` | 无形资产 | +| `liudong_fuzhai` | 流动负债 | +| `changqi_fuzhai` | 长期负债 | +| `ziben_gongjijin` | 资本公积金 | +| `jing_zichan` | 净资产 | + +### 利润指标(单位:元) + +| 字段名 | 中文 | +|--------|------| +| `zhuying_shouru` | 主营收入 | +| `zhuying_lirun` | 主营利润 | +| `yingshou_zhangkuan` | 应收账款 | +| `yingye_lirun` | 营业利润 | +| `touzi_shouyu` | 投资收益 | +| `jingying_xianjinliu` | 经营现金流 | +| `zong_xianjinliu` | 总现金流 | +| `cunhuo` | 存货 | +| `lirun_zonghe` | 利润总额 | +| `shuihou_lirun` | 税后利润 | +| `jing_lirun` | 净利润 | +| `weifen_lirun` | 未分配利润 | + +### 每股指标 + +| 字段名 | 中文 | 说明 | +|--------|------|------| +| `meigujing_zichan` | 每股净资产 | 原协议字段 baoliu1 | + +--- + +## CompanyInfoCategory(公司信息目录) + +来源:`get_company_info_category()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `name` | 目录名 | `str` | 如 "最新提示" | +| `filename` | 文件名 | `str` | 如 "600000.txt" | +| `start` | 起始偏移 | `int` | 字节偏移 | +| `length` | 内容长度 | `int` | 字节数 | + +--- + +## TdxBlock(板块信息) + +来源:`get_block_info()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `name` | 板块名称 | `str` | 如 "房地产" | +| `category` | 板块分类 | `int` | 0=行业, 1=地域, 2=概念, 3=风格 | +| `count` | 成分股数 | `int` | | +| `codes` | 成分股代码 | `list[str]` | 6 位代码列表 | + +--- + +## MarketStat(市场统计) + +来源:`get_market_stat()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `up_count` | 上涨家数 | `int` | | +| `down_count` | 下跌家数 | `int` | | +| `neutral_count` | 平盘家数 | `int` | | +| `suspended_count` | 停牌(估算) | `int` | 残差:total - up - down - neutral | +| `total_count` | 总计 | `int` | | +| `total_amount` | 总成交额 | `float` | 元 | +| `total_volume` | 总成交量 | `float` | | +| `total_market_cap` | 总市值 | `float` | 元,880001 price × 1e10 | +| `limit_up_count` | 涨停家数 | `int` | 880006 price | +| `limit_down_count` | 跌停家数 | `int` | 880006 open | + +--- + +## FundFlow(资金流向) + +来源:`get_fund_flow()` + +| 字段名 | 中文 | 类型 | 说明 | +|--------|------|------|------| +| `super_in` | 超大单流入 | `float` | 单笔 >100 万 | +| `super_out` | 超大单流出 | `float` | | +| `large_in` | 大单流入 | `float` | 20~100 万 | +| `large_out` | 大单流出 | `float` | | +| `medium_in` | 中单流入 | `float` | 4~20 万 | +| `medium_out` | 中单流出 | `float` | | +| `small_in` | 小单流入 | `float` | ≤4 万 | +| `small_out` | 小单流出 | `float` | | +| `main_net_inflow` | 主力净流入 | `float` | 属性:超大+大 | +| `total_net_inflow` | 全单净流入 | `float` | 属性:全部 | + +--- + +## HistoricalFundFlow(历史资金流向) + +来源:`get_history_fund_flow()` + +字段同 FundFlow,额外包含: + +| 字段名 | 中文 | 类型 | +|--------|------|------| +| `year` | 年 | `int` | +| `month` | 月 | `int` | +| `day` | 日 | `int` | + +--- + +## 枚举 + +### Market(市场) + +| 值 | 名称 | 说明 | +|----|------|------| +| 0 | SZ | 深圳 | +| 1 | SH | 上海 | +| 2 | BJ | 北京 | + +### KlineCategory(K 线周期) + +| 值 | 名称 | 说明 | +|----|------|------| +| 0 | MIN_5 | 5 分钟 | +| 1 | MIN_15 | 15 分钟 | +| 2 | MIN_30 | 30 分钟 | +| 3 | MIN_60 | 60 分钟 | +| 4 | DAY | 日线 | +| 5 | WEEK | 周线 | +| 6 | MONTH | 月线 | +| 7 | MIN_1 | 1 分钟 | +| 8 | MIN_3 | 3 分钟(内部用) | +| 9 | YEAR | 年线 | +| 10 | SEASON | 季线 | +| 11 | YEAR_ALT | 年线(备用) | diff --git a/examples/01_connection/async_connect.py b/examples/01_connection/async_connect.py index 0c089f7..a69f4c8 100644 --- a/examples/01_connection/async_connect.py +++ b/examples/01_connection/async_connect.py @@ -1,7 +1,7 @@ """演示:异步客户端连接与基本用法。""" import asyncio -from xmtdx import AsyncTdxClient, Market, KlineCategory +from easy_tdx import AsyncTdxClient, Market, KlineCategory async def main(): diff --git a/examples/01_connection/connect_best_host.py b/examples/01_connection/connect_best_host.py index f152594..c78e6d2 100644 --- a/examples/01_connection/connect_best_host.py +++ b/examples/01_connection/connect_best_host.py @@ -1,6 +1,6 @@ """演示:自动从候选服务器中选延迟最低的建立连接。""" -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market # 方式一:手动指定服务器 with TdxClient("180.153.18.170") as c: diff --git a/examples/01_connection/ping_servers.py b/examples/01_connection/ping_servers.py index 3bedebb..57e0d18 100644 --- a/examples/01_connection/ping_servers.py +++ b/examples/01_connection/ping_servers.py @@ -1,7 +1,7 @@ """演示:测量多台通达信服务器延迟并排序。""" import pandas as pd -from xmtdx import TdxClient +from easy_tdx import TdxClient results = TdxClient.ping_all() df = pd.DataFrame(results, columns=["服务器", "延迟(s)"]) diff --git a/examples/02_market_info/market_stat.py b/examples/02_market_info/market_stat.py index 486a240..0f805df 100644 --- a/examples/02_market_info/market_stat.py +++ b/examples/02_market_info/market_stat.py @@ -1,7 +1,7 @@ """演示:获取全市场涨跌统计概况。""" import pandas as pd -from xmtdx import TdxClient +from easy_tdx import TdxClient with TdxClient.from_best_host() as c: stat = c.get_market_stat() diff --git a/examples/02_market_info/security_count.py b/examples/02_market_info/security_count.py index 442611f..63446be 100644 --- a/examples/02_market_info/security_count.py +++ b/examples/02_market_info/security_count.py @@ -1,6 +1,6 @@ """演示:获取市场证券总数。""" -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: sh_count = c.get_security_count(Market.SH) diff --git a/examples/02_market_info/security_list.py b/examples/02_market_info/security_list.py index a812387..7ea135f 100644 --- a/examples/02_market_info/security_list.py +++ b/examples/02_market_info/security_list.py @@ -4,7 +4,7 @@ """ import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: stocks = c.get_security_list(Market.SH, start=0) diff --git a/examples/02_market_info/security_list_all.py b/examples/02_market_info/security_list_all.py index fff0548..5a35120 100644 --- a/examples/02_market_info/security_list_all.py +++ b/examples/02_market_info/security_list_all.py @@ -6,7 +6,7 @@ import logging import pandas as pd -from xmtdx import TdxClient +from easy_tdx import TdxClient # 启用日志,查看分页进度 logging.basicConfig(level=logging.INFO, format="%(message)s") diff --git a/examples/02_market_info/security_quotes.py b/examples/02_market_info/security_quotes.py index 8b9aa1c..763be38 100644 --- a/examples/02_market_info/security_quotes.py +++ b/examples/02_market_info/security_quotes.py @@ -1,7 +1,7 @@ """演示:批量获取实时五档行情。最多支持 80 只/次。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: stocks = [ diff --git a/examples/03_kline/index_bars.py b/examples/03_kline/index_bars.py index 1e98831..1ee5b03 100644 --- a/examples/03_kline/index_bars.py +++ b/examples/03_kline/index_bars.py @@ -7,7 +7,7 @@ """ import pandas as pd -from xmtdx import TdxClient, Market, KlineCategory +from easy_tdx import TdxClient, Market, KlineCategory with TdxClient.from_best_host() as c: bars = c.get_index_bars(Market.SH, "999999", KlineCategory.DAY, 0, 10) diff --git a/examples/03_kline/security_bars.py b/examples/03_kline/security_bars.py index e850c17..fa4d6e3 100644 --- a/examples/03_kline/security_bars.py +++ b/examples/03_kline/security_bars.py @@ -6,7 +6,7 @@ K 线类别: """ import pandas as pd -from xmtdx import TdxClient, Market, KlineCategory +from easy_tdx import TdxClient, Market, KlineCategory with TdxClient.from_best_host() as c: bars = c.get_security_bars(Market.SZ, "002176", KlineCategory.DAY, 0, 100) diff --git a/examples/04_minute/history_minute_data.py b/examples/04_minute/history_minute_data.py index 1ca777a..0612f3f 100644 --- a/examples/04_minute/history_minute_data.py +++ b/examples/04_minute/history_minute_data.py @@ -1,7 +1,7 @@ """演示:获取历史某日分时数据。date 参数为 YYYYMMDD 格式的整数。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: date = 20250110 diff --git a/examples/04_minute/minute_time_data.py b/examples/04_minute/minute_time_data.py index 55178f1..61498f1 100644 --- a/examples/04_minute/minute_time_data.py +++ b/examples/04_minute/minute_time_data.py @@ -1,7 +1,7 @@ """演示:获取今日分时数据(240 条)。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: bars = c.get_minute_time_data(Market.SH, "600000") diff --git a/examples/05_transaction/history_transaction.py b/examples/05_transaction/history_transaction.py index 344409b..e90a10f 100644 --- a/examples/05_transaction/history_transaction.py +++ b/examples/05_transaction/history_transaction.py @@ -1,7 +1,7 @@ """演示:获取历史逐笔成交数据。date 参数为 YYYYMMDD 格式的整数。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: date = 20250110 diff --git a/examples/05_transaction/transaction_data.py b/examples/05_transaction/transaction_data.py index 6eb3d5a..45e3ce6 100644 --- a/examples/05_transaction/transaction_data.py +++ b/examples/05_transaction/transaction_data.py @@ -1,7 +1,7 @@ """演示:获取当日逐笔成交数据。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: records = c.get_transaction_data(Market.SH, "600000", 0, 20) diff --git a/examples/06_finance/company_info.py b/examples/06_finance/company_info.py index 1550c19..c72d280 100644 --- a/examples/06_finance/company_info.py +++ b/examples/06_finance/company_info.py @@ -1,7 +1,7 @@ """演示:获取公司信息目录与各个分类的详细内容。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market CODE = "600519" NAME = "贵州茅台" diff --git a/examples/06_finance/finance_info.py b/examples/06_finance/finance_info.py index 0956349..2ce90cc 100644 --- a/examples/06_finance/finance_info.py +++ b/examples/06_finance/finance_info.py @@ -1,7 +1,7 @@ """演示:获取最新财务数据。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: info = c.get_finance_info(Market.SH, "600519") diff --git a/examples/06_finance/price_limits.py b/examples/06_finance/price_limits.py index 9cf3b4c..b77d75d 100644 --- a/examples/06_finance/price_limits.py +++ b/examples/06_finance/price_limits.py @@ -1,7 +1,7 @@ """演示:计算个股涨跌停价格。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market CODE = "600519" NAME = "贵州茅台" diff --git a/examples/06_finance/xdxr_info.py b/examples/06_finance/xdxr_info.py index 6d5a701..5f0431f 100644 --- a/examples/06_finance/xdxr_info.py +++ b/examples/06_finance/xdxr_info.py @@ -1,7 +1,7 @@ """演示:获取除权除息历史记录。""" import pandas as pd -from xmtdx import TdxClient, Market, XDXR_CATEGORY_NAMES +from easy_tdx import TdxClient, Market, XDXR_CATEGORY_NAMES with TdxClient.from_best_host() as c: records = c.get_xdxr_info(Market.SH, "600519") diff --git a/examples/07_block/block_info.py b/examples/07_block/block_info.py index 2defc10..8cee2ad 100644 --- a/examples/07_block/block_info.py +++ b/examples/07_block/block_info.py @@ -7,7 +7,7 @@ """ import pandas as pd -from xmtdx import TdxClient +from easy_tdx import TdxClient with TdxClient.from_best_host() as c: blocks = c.get_block_info("block_gn.dat") diff --git a/examples/08_fund_flow/fund_flow.py b/examples/08_fund_flow/fund_flow.py index 7298218..a41b7a8 100644 --- a/examples/08_fund_flow/fund_flow.py +++ b/examples/08_fund_flow/fund_flow.py @@ -4,7 +4,7 @@ """ import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: flow = c.get_fund_flow(Market.SH, "600519") diff --git a/examples/08_fund_flow/history_fund_flow.py b/examples/08_fund_flow/history_fund_flow.py index 45a3d54..2eae460 100644 --- a/examples/08_fund_flow/history_fund_flow.py +++ b/examples/08_fund_flow/history_fund_flow.py @@ -1,7 +1,7 @@ """演示:获取个股历史日线资金流向序列。""" import pandas as pd -from xmtdx import TdxClient, Market +from easy_tdx import TdxClient, Market with TdxClient.from_best_host() as c: flows = c.get_history_fund_flow(Market.SH, "600519", 0, 10) diff --git a/examples/09_file_download/report_file.py b/examples/09_file_download/report_file.py index e2a9a70..2d7cbe2 100644 --- a/examples/09_file_download/report_file.py +++ b/examples/09_file_download/report_file.py @@ -16,7 +16,7 @@ from pathlib import Path -from xmtdx import CALC_HOSTS, TdxClient +from easy_tdx import CALC_HOSTS, TdxClient OUTPUT_DIR = Path(__file__).parent / "downloads" diff --git a/examples/10_offline/block_data.py b/examples/10_offline/block_data.py index a26d490..2f8f52b 100644 --- a/examples/10_offline/block_data.py +++ b/examples/10_offline/block_data.py @@ -9,9 +9,9 @@ from pathlib import Path -from xmtdx import TdxClient -from xmtdx.models.finance import TdxBlock -from xmtdx.offline import detect_tdx_home, read_block_dat, read_customer_blocks +from easy_tdx import TdxClient +from easy_tdx.models.finance import TdxBlock +from easy_tdx.offline import detect_tdx_home, read_block_dat, read_customer_blocks def _print_blocks(blocks: list[TdxBlock], title: str) -> None: diff --git a/examples/10_offline/daily_bars.py b/examples/10_offline/daily_bars.py index 02cbd2d..b2fabea 100644 --- a/examples/10_offline/daily_bars.py +++ b/examples/10_offline/daily_bars.py @@ -7,8 +7,8 @@ 需要本地已安装通达信并下载过日线数据。 """ -from xmtdx.offline import detect_tdx_home, read_daily_bars, find_daily_bar_file -from xmtdx import Market +from easy_tdx.offline import detect_tdx_home, read_daily_bars, find_daily_bar_file +from easy_tdx import Market home = detect_tdx_home() if home is None: diff --git a/examples/10_offline/detect_home.py b/examples/10_offline/detect_home.py index 60c5461..4125bd7 100644 --- a/examples/10_offline/detect_home.py +++ b/examples/10_offline/detect_home.py @@ -18,9 +18,9 @@ vipdoc 目录结构: import os from pathlib import Path -from xmtdx.offline import detect_tdx_home, resolve_vipdoc -from xmtdx.offline import find_daily_bar_file, find_5min_bar_file, find_lc1_bar_file -from xmtdx import Market +from easy_tdx.offline import detect_tdx_home, resolve_vipdoc +from easy_tdx.offline import find_daily_bar_file, find_5min_bar_file, find_lc1_bar_file +from easy_tdx import Market # --- 检测安装目录 --- print("=" * 60) diff --git a/examples/10_offline/ex_daily_bars.py b/examples/10_offline/ex_daily_bars.py index e3a01e0..99c237f 100644 --- a/examples/10_offline/ex_daily_bars.py +++ b/examples/10_offline/ex_daily_bars.py @@ -8,7 +8,7 @@ from pathlib import Path -from xmtdx.offline import detect_tdx_home, read_ex_daily_bars +from easy_tdx.offline import detect_tdx_home, read_ex_daily_bars home = detect_tdx_home() if home is None: diff --git a/examples/10_offline/gbbq.py b/examples/10_offline/gbbq.py index e1c76ac..d791701 100644 --- a/examples/10_offline/gbbq.py +++ b/examples/10_offline/gbbq.py @@ -8,7 +8,7 @@ from pathlib import Path -from xmtdx.offline import detect_tdx_home, read_gbbq +from easy_tdx.offline import detect_tdx_home, read_gbbq home = detect_tdx_home() if home is None: diff --git a/examples/10_offline/history_financial.py b/examples/10_offline/history_financial.py index a4de6e9..ddb69a4 100644 --- a/examples/10_offline/history_financial.py +++ b/examples/10_offline/history_financial.py @@ -12,7 +12,7 @@ from pathlib import Path -from xmtdx.offline import detect_tdx_home, read_history_financial +from easy_tdx.offline import detect_tdx_home, read_history_financial home = detect_tdx_home() if home is None: diff --git a/examples/10_offline/min_bars.py b/examples/10_offline/min_bars.py index dea388b..32cf06c 100644 --- a/examples/10_offline/min_bars.py +++ b/examples/10_offline/min_bars.py @@ -8,7 +8,7 @@ 需要本地已安装通达信并下载过分钟数据。 """ -from xmtdx.offline import ( +from easy_tdx.offline import ( detect_tdx_home, read_5min_bars, read_lc_min_bars, @@ -16,7 +16,7 @@ from xmtdx.offline import ( find_lc1_bar_file, find_lc5_bar_file, ) -from xmtdx import Market +from easy_tdx import Market home = detect_tdx_home() if home is None: diff --git a/pyproject.toml b/pyproject.toml index fd081c2..b5ef404 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,9 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "xmtdx" -version = "0.1.1" -description = "通达信 TCP 协议 A 股行情数据客户端" +name = "easy-tdx" +version = "1.0.0" +description = "通达信 TCP 协议行情数据客户端,支持在线行情与离线本地数据读取" readme = "README.md" requires-python = ">=3.10" dependencies = ["tzdata>=2024.1"] @@ -15,7 +15,7 @@ dev = ["pytest>=8.0", "pytest-cov", "mypy>=1.9", "ruff>=0.4"] pandas = ["pandas>=2.0"] [tool.hatch.build.targets.wheel] -packages = ["src/xmtdx"] +packages = ["src/easy_tdx"] [tool.mypy] strict = true diff --git a/scripts/probe_unknowns.py b/scripts/probe_unknowns.py index bee355b..0bdae22 100644 --- a/scripts/probe_unknowns.py +++ b/scripts/probe_unknowns.py @@ -2,7 +2,7 @@ import sys -from xmtdx import Market, TdxClient +from easy_tdx import Market, TdxClient def probe_minute_averages(client, market, code): diff --git a/scripts/verify_fixes_20260415.py b/scripts/verify_fixes_20260415.py index 3f172a6..8795613 100644 --- a/scripts/verify_fixes_20260415.py +++ b/scripts/verify_fixes_20260415.py @@ -2,8 +2,8 @@ import sys -from xmtdx import Market, TdxClient -from xmtdx.models.enums import KlineCategory +from easy_tdx import Market, TdxClient +from easy_tdx.models.enums import KlineCategory def main(): diff --git a/src/xmtdx/__init__.py b/src/easy_tdx/__init__.py similarity index 88% rename from src/xmtdx/__init__.py rename to src/easy_tdx/__init__.py index 3588ea2..1d5c198 100644 --- a/src/xmtdx/__init__.py +++ b/src/easy_tdx/__init__.py @@ -1,8 +1,8 @@ -"""xmtdx — 通达信 TCP 协议 A 股行情数据客户端。 +"""easy_tdx — 通达信 TCP 协议 A 股行情数据客户端。 快速开始:: - from xmtdx import TdxClient, Market, KlineCategory + from easy_tdx import TdxClient, Market, KlineCategory with TdxClient("180.153.18.170") as c: count = c.get_security_count(Market.SH) @@ -11,7 +11,7 @@ asyncio 版本:: import asyncio - from xmtdx import AsyncTdxClient, Market, KlineCategory + from easy_tdx import AsyncTdxClient, Market, KlineCategory async def main(): async with AsyncTdxClient("180.153.18.170") as c: @@ -75,4 +75,4 @@ __all__ = [ "CALC_HOSTS", ] -__version__ = "0.1.1" +__version__ = "1.0.0" diff --git a/src/xmtdx/_binary.py b/src/easy_tdx/_binary.py similarity index 100% rename from src/xmtdx/_binary.py rename to src/easy_tdx/_binary.py diff --git a/src/xmtdx/client.py b/src/easy_tdx/client.py similarity index 99% rename from src/xmtdx/client.py rename to src/easy_tdx/client.py index 0d28993..6f9d1f0 100644 --- a/src/xmtdx/client.py +++ b/src/easy_tdx/client.py @@ -136,7 +136,7 @@ def _historical_fund_flow_from_records( # 同步客户端 # ============================================================ -_CACHE_DIR = Path.home() / ".xmtdx" / "cache" +_CACHE_DIR = Path.home() / ".easy_tdx" / "cache" _CACHE_MAX_AGE = 86400 # 1 天 diff --git a/src/xmtdx/codec/__init__.py b/src/easy_tdx/codec/__init__.py similarity index 100% rename from src/xmtdx/codec/__init__.py rename to src/easy_tdx/codec/__init__.py diff --git a/src/xmtdx/codec/block.py b/src/easy_tdx/codec/block.py similarity index 100% rename from src/xmtdx/codec/block.py rename to src/easy_tdx/codec/block.py diff --git a/src/xmtdx/codec/datetime_.py b/src/easy_tdx/codec/datetime_.py similarity index 100% rename from src/xmtdx/codec/datetime_.py rename to src/easy_tdx/codec/datetime_.py diff --git a/src/xmtdx/codec/financial.py b/src/easy_tdx/codec/financial.py similarity index 100% rename from src/xmtdx/codec/financial.py rename to src/easy_tdx/codec/financial.py diff --git a/src/xmtdx/codec/frame.py b/src/easy_tdx/codec/frame.py similarity index 100% rename from src/xmtdx/codec/frame.py rename to src/easy_tdx/codec/frame.py diff --git a/src/xmtdx/codec/industry.py b/src/easy_tdx/codec/industry.py similarity index 100% rename from src/xmtdx/codec/industry.py rename to src/easy_tdx/codec/industry.py diff --git a/src/xmtdx/codec/price.py b/src/easy_tdx/codec/price.py similarity index 100% rename from src/xmtdx/codec/price.py rename to src/easy_tdx/codec/price.py diff --git a/src/xmtdx/codec/price_rules.py b/src/easy_tdx/codec/price_rules.py similarity index 100% rename from src/xmtdx/codec/price_rules.py rename to src/easy_tdx/codec/price_rules.py diff --git a/src/xmtdx/codec/volume.py b/src/easy_tdx/codec/volume.py similarity index 100% rename from src/xmtdx/codec/volume.py rename to src/easy_tdx/codec/volume.py diff --git a/src/xmtdx/commands/__init__.py b/src/easy_tdx/commands/__init__.py similarity index 100% rename from src/xmtdx/commands/__init__.py rename to src/easy_tdx/commands/__init__.py diff --git a/src/xmtdx/commands/base.py b/src/easy_tdx/commands/base.py similarity index 100% rename from src/xmtdx/commands/base.py rename to src/easy_tdx/commands/base.py diff --git a/src/xmtdx/commands/block_info.py b/src/easy_tdx/commands/block_info.py similarity index 100% rename from src/xmtdx/commands/block_info.py rename to src/easy_tdx/commands/block_info.py diff --git a/src/xmtdx/commands/company_info.py b/src/easy_tdx/commands/company_info.py similarity index 100% rename from src/xmtdx/commands/company_info.py rename to src/easy_tdx/commands/company_info.py diff --git a/src/xmtdx/commands/finance_info.py b/src/easy_tdx/commands/finance_info.py similarity index 100% rename from src/xmtdx/commands/finance_info.py rename to src/easy_tdx/commands/finance_info.py diff --git a/src/xmtdx/commands/fund_flow.py b/src/easy_tdx/commands/fund_flow.py similarity index 100% rename from src/xmtdx/commands/fund_flow.py rename to src/easy_tdx/commands/fund_flow.py diff --git a/src/xmtdx/commands/minute_time.py b/src/easy_tdx/commands/minute_time.py similarity index 100% rename from src/xmtdx/commands/minute_time.py rename to src/easy_tdx/commands/minute_time.py diff --git a/src/xmtdx/commands/report_file.py b/src/easy_tdx/commands/report_file.py similarity index 100% rename from src/xmtdx/commands/report_file.py rename to src/easy_tdx/commands/report_file.py diff --git a/src/xmtdx/commands/security_bars.py b/src/easy_tdx/commands/security_bars.py similarity index 100% rename from src/xmtdx/commands/security_bars.py rename to src/easy_tdx/commands/security_bars.py diff --git a/src/xmtdx/commands/security_count.py b/src/easy_tdx/commands/security_count.py similarity index 100% rename from src/xmtdx/commands/security_count.py rename to src/easy_tdx/commands/security_count.py diff --git a/src/xmtdx/commands/security_list.py b/src/easy_tdx/commands/security_list.py similarity index 100% rename from src/xmtdx/commands/security_list.py rename to src/easy_tdx/commands/security_list.py diff --git a/src/xmtdx/commands/security_quotes.py b/src/easy_tdx/commands/security_quotes.py similarity index 100% rename from src/xmtdx/commands/security_quotes.py rename to src/easy_tdx/commands/security_quotes.py diff --git a/src/xmtdx/commands/setup.py b/src/easy_tdx/commands/setup.py similarity index 100% rename from src/xmtdx/commands/setup.py rename to src/easy_tdx/commands/setup.py diff --git a/src/xmtdx/commands/transaction.py b/src/easy_tdx/commands/transaction.py similarity index 100% rename from src/xmtdx/commands/transaction.py rename to src/easy_tdx/commands/transaction.py diff --git a/src/xmtdx/commands/xdxr_info.py b/src/easy_tdx/commands/xdxr_info.py similarity index 100% rename from src/xmtdx/commands/xdxr_info.py rename to src/easy_tdx/commands/xdxr_info.py diff --git a/src/xmtdx/ex/__init__.py b/src/easy_tdx/ex/__init__.py similarity index 68% rename from src/xmtdx/ex/__init__.py rename to src/easy_tdx/ex/__init__.py index dd8be76..aa1440d 100644 --- a/src/xmtdx/ex/__init__.py +++ b/src/easy_tdx/ex/__init__.py @@ -1,4 +1,4 @@ -"""xmtdx.ex — 通达信扩展行情(期货、港股、外股等,端口 7727)。""" +"""easy_tdx.ex — 通达信扩展行情(期货、港股、外股等,端口 7727)。""" from .client import AsyncExTdxClient, ExTdxClient from .models import KNOWN_EX_HOSTS, KNOWN_EX_MARKETS diff --git a/src/xmtdx/ex/client.py b/src/easy_tdx/ex/client.py similarity index 100% rename from src/xmtdx/ex/client.py rename to src/easy_tdx/ex/client.py diff --git a/src/xmtdx/ex/commands/__init__.py b/src/easy_tdx/ex/commands/__init__.py similarity index 100% rename from src/xmtdx/ex/commands/__init__.py rename to src/easy_tdx/ex/commands/__init__.py diff --git a/src/xmtdx/ex/commands/get_history_bars_range.py b/src/easy_tdx/ex/commands/get_history_bars_range.py similarity index 100% rename from src/xmtdx/ex/commands/get_history_bars_range.py rename to src/easy_tdx/ex/commands/get_history_bars_range.py diff --git a/src/xmtdx/ex/commands/get_instrument_bars.py b/src/easy_tdx/ex/commands/get_instrument_bars.py similarity index 100% rename from src/xmtdx/ex/commands/get_instrument_bars.py rename to src/easy_tdx/ex/commands/get_instrument_bars.py diff --git a/src/xmtdx/ex/commands/get_instrument_count.py b/src/easy_tdx/ex/commands/get_instrument_count.py similarity index 100% rename from src/xmtdx/ex/commands/get_instrument_count.py rename to src/easy_tdx/ex/commands/get_instrument_count.py diff --git a/src/xmtdx/ex/commands/get_instrument_info.py b/src/easy_tdx/ex/commands/get_instrument_info.py similarity index 100% rename from src/xmtdx/ex/commands/get_instrument_info.py rename to src/easy_tdx/ex/commands/get_instrument_info.py diff --git a/src/xmtdx/ex/commands/get_instrument_quote.py b/src/easy_tdx/ex/commands/get_instrument_quote.py similarity index 100% rename from src/xmtdx/ex/commands/get_instrument_quote.py rename to src/easy_tdx/ex/commands/get_instrument_quote.py diff --git a/src/xmtdx/ex/commands/get_instrument_quote_list.py b/src/easy_tdx/ex/commands/get_instrument_quote_list.py similarity index 100% rename from src/xmtdx/ex/commands/get_instrument_quote_list.py rename to src/easy_tdx/ex/commands/get_instrument_quote_list.py diff --git a/src/xmtdx/ex/commands/get_markets.py b/src/easy_tdx/ex/commands/get_markets.py similarity index 100% rename from src/xmtdx/ex/commands/get_markets.py rename to src/easy_tdx/ex/commands/get_markets.py diff --git a/src/xmtdx/ex/commands/get_minute_time.py b/src/easy_tdx/ex/commands/get_minute_time.py similarity index 100% rename from src/xmtdx/ex/commands/get_minute_time.py rename to src/easy_tdx/ex/commands/get_minute_time.py diff --git a/src/xmtdx/ex/commands/get_transaction.py b/src/easy_tdx/ex/commands/get_transaction.py similarity index 100% rename from src/xmtdx/ex/commands/get_transaction.py rename to src/easy_tdx/ex/commands/get_transaction.py diff --git a/src/xmtdx/ex/commands/setup.py b/src/easy_tdx/ex/commands/setup.py similarity index 100% rename from src/xmtdx/ex/commands/setup.py rename to src/easy_tdx/ex/commands/setup.py diff --git a/src/xmtdx/ex/models.py b/src/easy_tdx/ex/models.py similarity index 100% rename from src/xmtdx/ex/models.py rename to src/easy_tdx/ex/models.py diff --git a/src/xmtdx/ex/transport/__init__.py b/src/easy_tdx/ex/transport/__init__.py similarity index 100% rename from src/xmtdx/ex/transport/__init__.py rename to src/easy_tdx/ex/transport/__init__.py diff --git a/src/xmtdx/ex/transport/async_.py b/src/easy_tdx/ex/transport/async_.py similarity index 100% rename from src/xmtdx/ex/transport/async_.py rename to src/easy_tdx/ex/transport/async_.py diff --git a/src/xmtdx/ex/transport/sync.py b/src/easy_tdx/ex/transport/sync.py similarity index 100% rename from src/xmtdx/ex/transport/sync.py rename to src/easy_tdx/ex/transport/sync.py diff --git a/src/xmtdx/exceptions.py b/src/easy_tdx/exceptions.py similarity index 86% rename from src/xmtdx/exceptions.py rename to src/easy_tdx/exceptions.py index b80bb5d..88c6d0d 100644 --- a/src/xmtdx/exceptions.py +++ b/src/easy_tdx/exceptions.py @@ -1,8 +1,8 @@ -"""xmtdx 异常层次""" +"""easy-tdx 异常层次""" class TdxError(Exception): - """所有 xmtdx 异常的基类""" + """所有 easy-tdx 异常的基类""" class TdxConnectionError(TdxError): diff --git a/src/xmtdx/models/__init__.py b/src/easy_tdx/models/__init__.py similarity index 100% rename from src/xmtdx/models/__init__.py rename to src/easy_tdx/models/__init__.py diff --git a/src/xmtdx/models/bar.py b/src/easy_tdx/models/bar.py similarity index 100% rename from src/xmtdx/models/bar.py rename to src/easy_tdx/models/bar.py diff --git a/src/xmtdx/models/enums.py b/src/easy_tdx/models/enums.py similarity index 100% rename from src/xmtdx/models/enums.py rename to src/easy_tdx/models/enums.py diff --git a/src/xmtdx/models/finance.py b/src/easy_tdx/models/finance.py similarity index 100% rename from src/xmtdx/models/finance.py rename to src/easy_tdx/models/finance.py diff --git a/src/xmtdx/models/quote.py b/src/easy_tdx/models/quote.py similarity index 100% rename from src/xmtdx/models/quote.py rename to src/easy_tdx/models/quote.py diff --git a/src/xmtdx/models/security.py b/src/easy_tdx/models/security.py similarity index 100% rename from src/xmtdx/models/security.py rename to src/easy_tdx/models/security.py diff --git a/src/xmtdx/models/stats.py b/src/easy_tdx/models/stats.py similarity index 100% rename from src/xmtdx/models/stats.py rename to src/easy_tdx/models/stats.py diff --git a/src/xmtdx/models/timeseries.py b/src/easy_tdx/models/timeseries.py similarity index 100% rename from src/xmtdx/models/timeseries.py rename to src/easy_tdx/models/timeseries.py diff --git a/src/xmtdx/offline/__init__.py b/src/easy_tdx/offline/__init__.py similarity index 100% rename from src/xmtdx/offline/__init__.py rename to src/easy_tdx/offline/__init__.py diff --git a/src/xmtdx/offline/block.py b/src/easy_tdx/offline/block.py similarity index 100% rename from src/xmtdx/offline/block.py rename to src/easy_tdx/offline/block.py diff --git a/src/xmtdx/offline/daily_bar.py b/src/easy_tdx/offline/daily_bar.py similarity index 100% rename from src/xmtdx/offline/daily_bar.py rename to src/easy_tdx/offline/daily_bar.py diff --git a/src/xmtdx/offline/ex_daily_bar.py b/src/easy_tdx/offline/ex_daily_bar.py similarity index 100% rename from src/xmtdx/offline/ex_daily_bar.py rename to src/easy_tdx/offline/ex_daily_bar.py diff --git a/src/xmtdx/offline/finders.py b/src/easy_tdx/offline/finders.py similarity index 100% rename from src/xmtdx/offline/finders.py rename to src/easy_tdx/offline/finders.py diff --git a/src/xmtdx/offline/gbbq.py b/src/easy_tdx/offline/gbbq.py similarity index 100% rename from src/xmtdx/offline/gbbq.py rename to src/easy_tdx/offline/gbbq.py diff --git a/src/xmtdx/offline/history_financial.py b/src/easy_tdx/offline/history_financial.py similarity index 100% rename from src/xmtdx/offline/history_financial.py rename to src/easy_tdx/offline/history_financial.py diff --git a/src/xmtdx/offline/min_bar.py b/src/easy_tdx/offline/min_bar.py similarity index 100% rename from src/xmtdx/offline/min_bar.py rename to src/easy_tdx/offline/min_bar.py diff --git a/src/xmtdx/offline/paths.py b/src/easy_tdx/offline/paths.py similarity index 100% rename from src/xmtdx/offline/paths.py rename to src/easy_tdx/offline/paths.py diff --git a/src/xmtdx/py.typed b/src/easy_tdx/py.typed similarity index 100% rename from src/xmtdx/py.typed rename to src/easy_tdx/py.typed diff --git a/src/xmtdx/transport/__init__.py b/src/easy_tdx/transport/__init__.py similarity index 100% rename from src/xmtdx/transport/__init__.py rename to src/easy_tdx/transport/__init__.py diff --git a/src/xmtdx/transport/async_.py b/src/easy_tdx/transport/async_.py similarity index 100% rename from src/xmtdx/transport/async_.py rename to src/easy_tdx/transport/async_.py diff --git a/src/xmtdx/transport/sync.py b/src/easy_tdx/transport/sync.py similarity index 100% rename from src/xmtdx/transport/sync.py rename to src/easy_tdx/transport/sync.py diff --git a/tests/integration/test_live_client.py b/tests/integration/test_live_client.py index a0a9a46..49b21a0 100644 --- a/tests/integration/test_live_client.py +++ b/tests/integration/test_live_client.py @@ -10,7 +10,7 @@ import os import pytest -from xmtdx import AsyncTdxClient, Market, TdxClient +from easy_tdx import AsyncTdxClient, Market, TdxClient _LIVE_ENABLED = os.getenv("XMTDX_LIVE") == "1" _LIVE_HOST = os.getenv("XMTDX_HOST", "180.153.18.170") diff --git a/tests/unit/test_a_share_extensions.py b/tests/unit/test_a_share_extensions.py index 481ecbe..d4540ce 100644 --- a/tests/unit/test_a_share_extensions.py +++ b/tests/unit/test_a_share_extensions.py @@ -4,17 +4,17 @@ import asyncio import struct from unittest.mock import patch -from xmtdx import AsyncTdxClient, Market, TdxClient -from xmtdx.client import _classify_fund_flow -from xmtdx.models.bar import SecurityBar -from xmtdx.models.quote import SecurityQuote -from xmtdx.models.security import SecurityInfo -from xmtdx.models.stats import HistoricalFundFlow -from xmtdx.models.timeseries import MinuteBar -from xmtdx.models.timeseries import TransactionRecord +from easy_tdx import AsyncTdxClient, Market, TdxClient +from easy_tdx.client import _classify_fund_flow +from easy_tdx.models.bar import SecurityBar +from easy_tdx.models.quote import SecurityQuote +from easy_tdx.models.security import SecurityInfo +from easy_tdx.models.stats import HistoricalFundFlow +from easy_tdx.models.timeseries import MinuteBar +from easy_tdx.models.timeseries import TransactionRecord -@patch("xmtdx.client.TdxConnection") +@patch("easy_tdx.client.TdxConnection") def test_get_fund_flow_logic(_mock_conn_cls): """测试资金流分类计算逻辑。""" client = TdxClient("127.0.0.1") @@ -48,7 +48,7 @@ def test_classify_fund_flow_exact_thresholds_use_lower_bucket(): assert flow.medium_in == 200000.0 assert flow.small_in == 40000.0 -@patch("xmtdx.client.TdxConnection") +@patch("easy_tdx.client.TdxConnection") def test_get_security_list_all_filtering(_mock_conn_cls): """测试三市 A 股过滤与行业挂载逻辑。""" client = TdxClient("127.0.0.1") @@ -84,7 +84,7 @@ def test_get_security_list_all_filtering(_mock_conn_cls): s0 = next(s for s in all_stocks if s.code == "600000") assert s0.industry_tdx == "T01" -@patch("xmtdx.client.TdxConnection") +@patch("easy_tdx.client.TdxConnection") def test_get_market_stat_mapping(_mock_conn_cls): """测试市场统计字段映射。""" client = TdxClient("127.0.0.1") @@ -114,7 +114,7 @@ def test_get_market_stat_mapping(_mock_conn_cls): def test_get_history_fund_flow_parsing(): """测试历史资金流序列解析逻辑。""" - from xmtdx.commands.fund_flow import GetHistoryFundFlowCmd + from easy_tdx.commands.fund_flow import GetHistoryFundFlowCmd # 模拟 Category 22 响应 (Header 9 + Count 2 + Body 36) body = bytearray(9) @@ -136,7 +136,7 @@ def test_get_history_fund_flow_parsing(): assert res[0].day == 8 -@patch("xmtdx.client.TdxConnection") +@patch("easy_tdx.client.TdxConnection") def test_get_history_fund_flow_fallback(_mock_conn_cls): """Category 22 空回包时,自动回退到历史逐笔重算。""" client = TdxClient("127.0.0.1") @@ -197,7 +197,7 @@ def test_get_history_fund_flow_fallback(_mock_conn_cls): ] -@patch("xmtdx.client.TdxConnection") +@patch("easy_tdx.client.TdxConnection") def test_get_price_limits_uses_listing_window(_mock_conn_cls): """client.get_price_limits 应结合日 K 条数判断上市初期限价窗口。""" client = TdxClient("127.0.0.1") @@ -223,13 +223,13 @@ def test_get_price_limits_uses_listing_window(_mock_conn_cls): ) -@patch("xmtdx.client.TdxConnection") +@patch("easy_tdx.client.TdxConnection") def test_get_minute_time_data_prefers_history_endpoint(_mock_conn_cls): """今日分时优先走历史分时接口,规避当前分时协议歧义。""" client = TdxClient("127.0.0.1") expected = [MinuteBar(price=9.7, vol=13694)] - with patch("xmtdx.client._today_in_shanghai", return_value=20260422), patch.object( + with patch("easy_tdx.client._today_in_shanghai", return_value=20260422), patch.object( TdxClient, "get_history_minute_time_data", return_value=expected, @@ -244,13 +244,13 @@ def test_get_minute_time_data_prefers_history_endpoint(_mock_conn_cls): assert result == expected -@patch("xmtdx.client.TdxConnection") +@patch("easy_tdx.client.TdxConnection") def test_get_minute_time_data_falls_back_to_current_endpoint(_mock_conn_cls): """历史分时失败时,仍回退到原今日分时命令。""" client = TdxClient("127.0.0.1") fallback = [MinuteBar(price=9.61, vol=10698)] - with patch("xmtdx.client._today_in_shanghai", return_value=20260422), patch.object( + with patch("easy_tdx.client._today_in_shanghai", return_value=20260422), patch.object( TdxClient, "get_history_minute_time_data", side_effect=RuntimeError("history unavailable"), @@ -271,9 +271,9 @@ def test_async_get_minute_time_data_prefers_history_endpoint(): expected = [MinuteBar(price=9.7, vol=13694)] async def run_test() -> None: - with patch("xmtdx.client.AsyncTdxConnection"): + with patch("easy_tdx.client.AsyncTdxConnection"): client = AsyncTdxClient("127.0.0.1") - with patch("xmtdx.client._today_in_shanghai", return_value=20260422), patch.object( + with patch("easy_tdx.client._today_in_shanghai", return_value=20260422), patch.object( AsyncTdxClient, "get_history_minute_time_data", return_value=expected, diff --git a/tests/unit/test_async_transport.py b/tests/unit/test_async_transport.py index e53994e..31eca18 100644 --- a/tests/unit/test_async_transport.py +++ b/tests/unit/test_async_transport.py @@ -6,10 +6,10 @@ import asyncio import struct import time -from xmtdx import AsyncTdxClient, Market -from xmtdx.commands.security_count import GetSecurityCountCmd -from xmtdx.commands.setup import SETUP_COMMANDS -from xmtdx.exceptions import TdxConnectionError +from easy_tdx import AsyncTdxClient, Market +from easy_tdx.commands.security_count import GetSecurityCountCmd +from easy_tdx.commands.setup import SETUP_COMMANDS +from easy_tdx.exceptions import TdxConnectionError def _pack_frame(body: bytes) -> bytes: diff --git a/tests/unit/test_block_info.py b/tests/unit/test_block_info.py index adca2e6..23d7c26 100644 --- a/tests/unit/test_block_info.py +++ b/tests/unit/test_block_info.py @@ -4,19 +4,19 @@ import asyncio import struct from unittest.mock import patch -from xmtdx.client import AsyncTdxClient, TdxClient -from xmtdx.codec.block import parse_block_dat -from xmtdx.models.finance import TdxBlock +from easy_tdx.client import AsyncTdxClient, TdxClient +from easy_tdx.codec.block import parse_block_dat +from easy_tdx.models.finance import TdxBlock -@patch("xmtdx.client.AsyncTdxConnection") +@patch("easy_tdx.client.AsyncTdxConnection") def test_async_get_block_info_logic(mock_conn_cls): """测试 AsyncTdxClient.get_block_info 的异步拉取逻辑。""" mock_conn = mock_conn_cls.return_value # 模拟异步 execute async def mock_execute(cmd): - from xmtdx.commands.block_info import GetBlockInfoCmd, GetBlockInfoMetaCmd + from easy_tdx.commands.block_info import GetBlockInfoCmd, GetBlockInfoMetaCmd if isinstance(cmd, GetBlockInfoMetaCmd): return 100, "hash" if isinstance(cmd, GetBlockInfoCmd): @@ -29,7 +29,7 @@ def test_async_get_block_info_logic(mock_conn_cls): async def main(): client = AsyncTdxClient("127.0.0.1") - with patch("xmtdx.client.parse_block_dat") as mock_parse: + with patch("easy_tdx.client.parse_block_dat") as mock_parse: mock_parse.return_value = [] res = await client.get_block_info("test.dat") @@ -71,7 +71,7 @@ def test_parse_block_dat_basic(): assert b.codes == ["600000", "000001"] -@patch("xmtdx.client.TdxConnection") +@patch("easy_tdx.client.TdxConnection") def test_get_block_info_logic(mock_conn_cls): """测试 TdxClient.get_block_info 的分片拉取逻辑。""" mock_conn = mock_conn_cls.return_value @@ -80,7 +80,7 @@ def test_get_block_info_logic(mock_conn_cls): # 模拟 GetBlockInfoMeta 响应:size=35000 (需要2次拉取) def mock_execute(cmd): - from xmtdx.commands.block_info import GetBlockInfoCmd, GetBlockInfoMetaCmd + from easy_tdx.commands.block_info import GetBlockInfoCmd, GetBlockInfoMetaCmd if isinstance(cmd, GetBlockInfoMetaCmd): return 35000, "dummy_hash" if isinstance(cmd, GetBlockInfoCmd): @@ -91,7 +91,7 @@ def test_get_block_info_logic(mock_conn_cls): mock_conn.execute.side_effect = mock_execute # 我们主要测试循环是否正确 - with patch("xmtdx.client.parse_block_dat") as mock_parse: + with patch("easy_tdx.client.parse_block_dat") as mock_parse: mock_parse.return_value = [TdxBlock("Test", 1, 0, [])] res = client.get_block_info("test.dat") diff --git a/tests/unit/test_codec_datetime.py b/tests/unit/test_codec_datetime.py index 08fe660..69301a9 100644 --- a/tests/unit/test_codec_datetime.py +++ b/tests/unit/test_codec_datetime.py @@ -2,7 +2,7 @@ import struct -from xmtdx.codec.datetime_ import get_datetime, get_datetime_day, get_datetime_minute, get_time +from easy_tdx.codec.datetime_ import get_datetime, get_datetime_day, get_datetime_minute, get_time def _pack_minute(year: int, month: int, day: int, hour: int, minute: int) -> bytes: diff --git a/tests/unit/test_codec_frame.py b/tests/unit/test_codec_frame.py index 6aa4609..c5506c5 100644 --- a/tests/unit/test_codec_frame.py +++ b/tests/unit/test_codec_frame.py @@ -3,7 +3,7 @@ import struct import zlib -from xmtdx.codec.frame import HEADER_SIZE, decompress_body, parse_header +from easy_tdx.codec.frame import HEADER_SIZE, decompress_body, parse_header def _make_header(zipsize: int, unzipsize: int) -> bytes: diff --git a/tests/unit/test_codec_price.py b/tests/unit/test_codec_price.py index 176abf4..5f89376 100644 --- a/tests/unit/test_codec_price.py +++ b/tests/unit/test_codec_price.py @@ -1,6 +1,6 @@ """get_price / put_price 单元测试,测试向量来自 pytdx 实际报文。""" -from xmtdx.codec.price import get_price, put_price +from easy_tdx.codec.price import get_price, put_price class TestGetPrice: diff --git a/tests/unit/test_codec_volume.py b/tests/unit/test_codec_volume.py index 6b33fca..d71db04 100644 --- a/tests/unit/test_codec_volume.py +++ b/tests/unit/test_codec_volume.py @@ -2,7 +2,7 @@ import struct -from xmtdx.codec.volume import get_volume +from easy_tdx.codec.volume import get_volume def _pack(ivol: int) -> bytes: @@ -18,7 +18,7 @@ class TestGetVolume: def test_known_value_4098(self): # pytdx 注释 "4098 ---> 3.0" 含义:raw 4098 对应真实股数 3.0亿, # 但 get_volume(4098) ≈ 5.88e-39(接近零),说明 xdxr_info 里对股本字段 - # 调用 get_volume 是错误用法。xmtdx 在 xdxr_info 命令中会用正确的解码方式。 + # 调用 get_volume 是错误用法。easy-tdx 在 xdxr_info 命令中会用正确的解码方式。 val, pos = get_volume(_pack(4098), 0) assert abs(val) < 1e-30 # 接近零,与 pytdx 行为一致 diff --git a/tests/unit/test_commands_offline.py b/tests/unit/test_commands_offline.py index 5e6877d..794ef37 100644 --- a/tests/unit/test_commands_offline.py +++ b/tests/unit/test_commands_offline.py @@ -21,8 +21,8 @@ def load_hex(name: str) -> bytes: # --------------------------------------------------------------------------- def test_security_count_parse(): - from xmtdx.commands.security_count import GetSecurityCountCmd - from xmtdx.models.enums import Market + from easy_tdx.commands.security_count import GetSecurityCountCmd + from easy_tdx.models.enums import Market body = load_hex("security_count") cmd = GetSecurityCountCmd(Market.SH) @@ -39,8 +39,8 @@ def test_security_count_parse(): # --------------------------------------------------------------------------- def test_security_list_parse(): - from xmtdx.commands.security_list import GetSecurityListCmd - from xmtdx.models.enums import Market + from easy_tdx.commands.security_list import GetSecurityListCmd + from easy_tdx.models.enums import Market body = load_hex("security_list") cmd = GetSecurityListCmd(Market.SH, 0) @@ -58,8 +58,8 @@ def test_security_list_parse(): def test_security_list_pre_close_uses_tdx_float_for_a_share(): - from xmtdx.commands.security_list import GetSecurityListCmd - from xmtdx.models.enums import Market + from easy_tdx.commands.security_list import GetSecurityListCmd + from easy_tdx.models.enums import Market body = ( struct.pack(" None: - from xmtdx.models.enums import Market + from easy_tdx.models.enums import Market r = FinancialRecord( code="600519", market=Market.SH, report_date=20260331, fields=[1.0, 2.0] diff --git a/tests/unit/test_heartbeat.py b/tests/unit/test_heartbeat.py index 20b88e8..3029ee5 100644 --- a/tests/unit/test_heartbeat.py +++ b/tests/unit/test_heartbeat.py @@ -3,13 +3,13 @@ import asyncio from unittest.mock import AsyncMock, patch -from xmtdx import AsyncTdxClient +from easy_tdx import AsyncTdxClient def test_heartbeat_sends_periodically(): async def run_test(): # 模拟连接和执行 - with patch("xmtdx.client.AsyncTdxConnection") as mock_conn_cls: + with patch("easy_tdx.client.AsyncTdxConnection") as mock_conn_cls: mock_conn = mock_conn_cls.return_value mock_conn.connect = AsyncMock() mock_conn.close = AsyncMock() @@ -40,7 +40,7 @@ def test_heartbeat_sends_periodically(): def test_heartbeat_stops_on_close(): async def run_test(): - with patch("xmtdx.client.AsyncTdxConnection") as mock_conn_cls: + with patch("easy_tdx.client.AsyncTdxConnection") as mock_conn_cls: mock_conn = mock_conn_cls.return_value mock_conn.connect = AsyncMock() mock_conn.close = AsyncMock() diff --git a/tests/unit/test_protocol_fixes.py b/tests/unit/test_protocol_fixes.py index 377ff92..7f14b06 100644 --- a/tests/unit/test_protocol_fixes.py +++ b/tests/unit/test_protocol_fixes.py @@ -3,12 +3,12 @@ import struct from unittest.mock import patch -from xmtdx.codec.price_rules import compute_price_limits -from xmtdx.commands.fund_flow import GetHistoryFundFlowCmd -from xmtdx.commands.security_bars import GetSecurityBarsCmd -from xmtdx.commands.security_list import GetSecurityListCmd -from xmtdx.commands.security_quotes import GetSecurityQuotesCmd -from xmtdx.models.enums import KlineCategory, Market +from easy_tdx.codec.price_rules import compute_price_limits +from easy_tdx.commands.fund_flow import GetHistoryFundFlowCmd +from easy_tdx.commands.security_bars import GetSecurityBarsCmd +from easy_tdx.commands.security_list import GetSecurityListCmd +from easy_tdx.commands.security_quotes import GetSecurityQuotesCmd +from easy_tdx.models.enums import KlineCategory, Market def test_security_bars_exact_layout(): @@ -53,7 +53,7 @@ def test_security_list_request_length(): def test_security_quotes_limit_mapping(): """验证涨跌停价现在返回 None,且 pre_close 正确。""" - from xmtdx.codec.price import put_price + from easy_tdx.codec.price import put_price cmd = GetSecurityQuotesCmd([(Market.SH, "600000")]) @@ -96,7 +96,7 @@ def test_security_quotes_limit_mapping(): def test_security_quotes_server_time_format(): """服务器时间应按“小时 + 百万分之一小时”统一解码。""" - from xmtdx.commands.security_quotes import _format_server_time + from easy_tdx.commands.security_quotes import _format_server_time assert _format_server_time(9500000) == "09:30:00.000" assert _format_server_time(14999212) == "14:59:57.163" @@ -156,7 +156,7 @@ def test_history_fund_flow_uses_uint32_volume_words(): seen.append(raw) return float(raw) - with patch("xmtdx.commands.fund_flow._decode_volume", side_effect=fake_decode): + with patch("easy_tdx.commands.fund_flow._decode_volume", side_effect=fake_decode): records = GetHistoryFundFlowCmd(Market.SH, "600000", 0, 1).parse_response(bytes(body)) assert seen == raw_words diff --git a/tests/unit/test_sync_transport.py b/tests/unit/test_sync_transport.py index 6f2daa7..40a1cb2 100644 --- a/tests/unit/test_sync_transport.py +++ b/tests/unit/test_sync_transport.py @@ -2,8 +2,8 @@ from unittest.mock import patch -from xmtdx.exceptions import TdxConnectionError -from xmtdx.transport.sync import TdxConnection +from easy_tdx.exceptions import TdxConnectionError +from easy_tdx.transport.sync import TdxConnection class _FakeSocket: @@ -26,7 +26,7 @@ def test_sync_connection_closes_socket_when_setup_fails() -> None: sock = _FakeSocket() conn = TdxConnection("127.0.0.1", port=7709, timeout=0.2) - with patch("xmtdx.transport.sync.socket.socket", return_value=sock), patch.object( + with patch("easy_tdx.transport.sync.socket.socket", return_value=sock), patch.object( TdxConnection, "_send_setup", side_effect=TdxConnectionError("setup failed"),