probar 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. probar-0.1.0/.gitignore +28 -0
  2. probar-0.1.0/CHANGELOG.md +25 -0
  3. probar-0.1.0/LICENSE +21 -0
  4. probar-0.1.0/PKG-INFO +137 -0
  5. probar-0.1.0/README.md +101 -0
  6. probar-0.1.0/probar/__init__.py +54 -0
  7. probar-0.1.0/probar/_version.py +1 -0
  8. probar-0.1.0/probar/core/__init__.py +4 -0
  9. probar-0.1.0/probar/core/cache.py +36 -0
  10. probar-0.1.0/probar/core/calendar.py +17 -0
  11. probar-0.1.0/probar/core/capabilities.py +63 -0
  12. probar-0.1.0/probar/core/errors.py +32 -0
  13. probar-0.1.0/probar/core/http.py +69 -0
  14. probar-0.1.0/probar/core/models.py +79 -0
  15. probar-0.1.0/probar/core/rate_limit.py +35 -0
  16. probar-0.1.0/probar/core/symbols.py +89 -0
  17. probar-0.1.0/probar/playground/__init__.py +1 -0
  18. probar-0.1.0/probar/playground/__main__.py +24 -0
  19. probar-0.1.0/probar/playground/app.py +292 -0
  20. probar-0.1.0/probar/playground/index.html +1965 -0
  21. probar-0.1.0/probar/providers/__init__.py +1 -0
  22. probar-0.1.0/probar/providers/base.py +35 -0
  23. probar-0.1.0/probar/providers/eastmoney/__init__.py +3 -0
  24. probar-0.1.0/probar/providers/eastmoney/client.py +355 -0
  25. probar-0.1.0/probar/providers/eastmoney/endpoints.py +125 -0
  26. probar-0.1.0/probar/providers/eastmoney/parsers.py +249 -0
  27. probar-0.1.0/probar/providers/tdx/PROTOCOL.md +48 -0
  28. probar-0.1.0/probar/providers/tdx/__init__.py +3 -0
  29. probar-0.1.0/probar/providers/tdx/_codec.py +166 -0
  30. probar-0.1.0/probar/providers/tdx/_protocol.py +129 -0
  31. probar-0.1.0/probar/providers/tdx/client.py +154 -0
  32. probar-0.1.0/probar/providers/tdx/parsers.py +86 -0
  33. probar-0.1.0/probar/providers/tdx/servers.py +24 -0
  34. probar-0.1.0/probar/providers/tdx/transport.py +131 -0
  35. probar-0.1.0/probar/providers/ths/__init__.py +3 -0
  36. probar-0.1.0/probar/providers/ths/client.py +65 -0
  37. probar-0.1.0/pyproject.toml +80 -0
  38. probar-0.1.0/tests/__init__.py +0 -0
  39. probar-0.1.0/tests/conftest.py +41 -0
  40. probar-0.1.0/tests/fixtures/eastmoney_fflow.json +12 -0
  41. probar-0.1.0/tests/fixtures/eastmoney_financials.json +35 -0
  42. probar-0.1.0/tests/fixtures/eastmoney_kline.json +15 -0
  43. probar-0.1.0/tests/fixtures/eastmoney_lhb.json +35 -0
  44. probar-0.1.0/tests/fixtures/eastmoney_quotes_batch.json +34 -0
  45. probar-0.1.0/tests/fixtures/eastmoney_securities.json +13 -0
  46. probar-0.1.0/tests/fixtures/eastmoney_trends.json +15 -0
  47. probar-0.1.0/tests/fixtures/tdx_quotes.json +100 -0
  48. probar-0.1.0/tests/fixtures/tdx_quotes_raw.json +87 -0
  49. probar-0.1.0/tests/test_capabilities.py +19 -0
  50. probar-0.1.0/tests/test_eastmoney_more.py +101 -0
  51. probar-0.1.0/tests/test_eastmoney_optimize.py +181 -0
  52. probar-0.1.0/tests/test_eastmoney_parsers.py +91 -0
  53. probar-0.1.0/tests/test_namespaces.py +40 -0
  54. probar-0.1.0/tests/test_playground.py +64 -0
  55. probar-0.1.0/tests/test_rate_limit.py +23 -0
  56. probar-0.1.0/tests/test_securities.py +115 -0
  57. probar-0.1.0/tests/test_symbols.py +55 -0
  58. probar-0.1.0/tests/test_tdx_codec.py +150 -0
  59. probar-0.1.0/tests/test_tdx_quote.py +199 -0
@@ -0,0 +1,28 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+ env/
11
+
12
+ # Test / type / lint caches
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # probar 本地缓存(后续版本落盘缓存目录)
20
+ .probar_cache/
21
+
22
+ # mkdocs 文档站构建输出
23
+ site/
24
+
25
+ # IDE / OS
26
+ .idea/
27
+ .vscode/
28
+ .DS_Store
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ 本项目遵循[语义化版本](https://semver.org/lang/zh-CN/)。
4
+
5
+ ## [0.1.0] - 2026-06-21
6
+
7
+ 首个公开发布。**稳定公共 API 仅含已实现接口**(下列);命名空间里的占位接口与路线图项**不属于** v0.1.0 稳定承诺。
8
+
9
+ ### Added
10
+
11
+ - 按数据源拆分的独立命名空间:`pb.dc`(东方财富)/ `pb.tdx`(通达信)/ `pb.ths`(同花顺)。各源数据独立、口径可能不一致,按需选源;**不做"主源",不做跨源故障转移**。
12
+ - 东方财富(`pb.dc`)已实现:`quote` / `quotes` / `kline` / `intraday` / `fund_flow` / `lhb` / `financials` / `securities`(全市场代码表)。
13
+ - 共享基础设施:结构化异常模型、证券代码归一化、令牌桶限流、TTL 缓存、HTTP 传输(重试+限流)、能力矩阵 `pb.capabilities()`。
14
+ - 离线解析测试(冻结真实响应 fixture)、GitHub CI(ruff / mypy / pytest)与每日 canary smoke。
15
+ - 通达信(`pb.tdx`)已实现:`quotes` / `quote` 批量实时五档,**clean-room 自写二进制 TCP 协议(纯标准库 `socket`/`struct`/`zlib`,零第三方依赖)** + 服务器池业务探针;其余接口已声明、分批落地。同花顺(`pb.ths`)命名空间已声明接口面(stub),计划 v0.3 落地。
16
+ - 东财抗限频优化:`dc.quotes` 改 push2 `ulist` 批量端点(一次多只、自动分批 ≤100),`dc.securities` 接 TTL 缓存(默认 1h、未拉满抛 `SchemaChanged` 不缓存残表),`dc.kline` 修复 `limit` 兜底(东财 beg=0 忽略 lmt 时只取最近 limit 根)。
17
+ - 本地接口可视化测试台(`probar[playground]`,`python -m probar.playground`):网页列出全部接口,可选接口、填参数、运行,查看输入/输出 + 来源 + 耗时。
18
+ - 工程规范 `docs/ENGINEERING_GUIDE.md` + 《项目总体方案》`docs/PROJECT_PLAN.md`(运维/发布/演进 + 决策记录)+ 贡献指南 + `.github` issue/PR 模板 + `SECURITY.md`。
19
+
20
+ ### 说明
21
+
22
+ - 本库封装非官方/逆向接口,详见 README 免责声明。
23
+ - 发布采用 PyPI Trusted Publishing(OIDC),见 `.github/workflows/release.yml`。
24
+
25
+ [0.1.0]: https://github.com/anbeaman/probar/releases/tag/v0.1.0
probar-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 probar contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
probar-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.4
2
+ Name: probar
3
+ Version: 0.1.0
4
+ Summary: 稳定、可观测的 A 股数据接入层 —— 覆盖东方财富、通达信、同花顺
5
+ Project-URL: Homepage, https://github.com/anbeaman/probar
6
+ Project-URL: Issues, https://github.com/anbeaman/probar/issues
7
+ Author: anbeaman
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: a-share,eastmoney,quant,stock,tdx,tonghuashun,行情,量化
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Financial and Insurance Industry
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Office/Business :: Financial :: Investment
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: httpx>=0.27
20
+ Requires-Dist: pandas>=2.0
21
+ Provides-Extra: async
22
+ Provides-Extra: dev
23
+ Requires-Dist: fastapi>=0.110; extra == 'dev'
24
+ Requires-Dist: mypy>=1.10; extra == 'dev'
25
+ Requires-Dist: pytest>=8; extra == 'dev'
26
+ Requires-Dist: ruff>=0.6; extra == 'dev'
27
+ Requires-Dist: uvicorn>=0.29; extra == 'dev'
28
+ Provides-Extra: docs
29
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
30
+ Provides-Extra: playground
31
+ Requires-Dist: fastapi>=0.110; extra == 'playground'
32
+ Requires-Dist: uvicorn>=0.29; extra == 'playground'
33
+ Provides-Extra: tdx
34
+ Provides-Extra: ths
35
+ Description-Content-Type: text/markdown
36
+
37
+ # probar
38
+
39
+ 稳定、可观测的 **A 股数据接入层** —— 覆盖东方财富、通达信、同花顺,既能做量化,也能取盘中实时行情。
40
+
41
+ > 设计目标不是"接口最多",而是 **接口坏了能最快发现、最快修、用户最少踩坑**。
42
+ > 数据层优先:回测交给 backtrader / vnpy 等框架,probar 专注把三源数据统一、可靠地喂出来。
43
+
44
+ [![ci](https://github.com/anbeaman/probar/actions/workflows/ci.yml/badge.svg)](https://github.com/anbeaman/probar/actions/workflows/ci.yml)
45
+
46
+ ## 安装
47
+
48
+ ```bash
49
+ pip install probar # 核心:东方财富(pb.dc) + 通达信(pb.tdx)
50
+ pip install "probar[ths]" # 实验性:同花顺 / 问财(pb.ths,反爬 best-effort)
51
+ pip install "probar[async]" # 异步全市场扫描(v0.2)
52
+ pip install "probar[playground]" # 本地接口可视化测试台
53
+ ```
54
+
55
+ ## 接口测试台(本地)
56
+
57
+ 一个可视化网页,列出全部接口,可选接口、填参数、运行,查看输入/输出(表格 + 来源 + 耗时):
58
+
59
+ ```bash
60
+ pip install "probar[playground]"
61
+ python -m probar.playground # 打开 http://127.0.0.1:8787
62
+ ```
63
+
64
+ 左侧选数据源/接口(●已实现 / ○未实现)→ 填参数 → 运行。仅本地开发测试用,会真实联网取数。
65
+
66
+ ## 按数据源拆分的命名空间
67
+
68
+ 每个命名空间**只暴露该源真实支持的接口**。`pb.capabilities()` 记录三源能力(参考),
69
+ 各命名空间暴露其中已实现或计划实现的方法子集。
70
+
71
+ > **已实现**:`pb.dc` 的 `quote / quotes / kline / intraday / fund_flow / lhb / financials / securities`(东财全链路);
72
+ > `pb.tdx` 的 `quotes / quote`(批量实时五档,clean-room 自写二进制协议、纯标准库零依赖)。
73
+ > 其余接口已在命名空间中声明,调用抛 `NotImplementedError` 并注明计划版本;`pb.ths`(同花顺,实验性)计划 v0.3。
74
+
75
+ ```python
76
+ import probar as pb
77
+
78
+ # 东方财富(HTTP/JSON,数据最全)
79
+ pb.dc.quote("600519.SH")
80
+ pb.dc.kline("600519.SH", freq="1d", adjust="qfq")
81
+ pb.dc.fund_flow("000001.SZ")
82
+
83
+ # 通达信(自写二进制协议:批量实时五档;历史分钟/逐笔规划中)
84
+ pb.tdx.quotes(["000001.SZ", "600519.SH"]) # 一次多只,含 L1 五档盘口
85
+
86
+ # 同花顺(题材增强:问财 / 概念;实验性,v0.3 规划)
87
+ # pb.ths.wencai("近5日主力净流入为正且市值<100亿") # 规划中(stub,当前抛 NotImplementedError)
88
+
89
+ # 能力矩阵(各源能力参考;各源数据独立,不互相替换)
90
+ pb.capabilities()
91
+ ```
92
+
93
+ 高级用户可自建实例传入配置:
94
+
95
+ ```python
96
+ from probar import EastMoney
97
+ dc = EastMoney(timeout=5, proxy="http://127.0.0.1:7890")
98
+ dc.kline("000001.SZ")
99
+ ```
100
+
101
+ ## 三源分工
102
+
103
+ | 源 | 命名空间 | 定位 | 强项 | 短板 |
104
+ |---|---|---|---|---|
105
+ | 东方财富 | `pb.dc` | 综合最全(HTTP) | 实时/复权K/资金流/龙虎榜/财报/多市场最全 | 历史逐笔无;北向实时已停披露 |
106
+ | 通达信 | `pb.tdx` | 行情底座 | 批量实时、历史分钟、**历史逐笔** | 无资金流/龙虎榜/北向;复权需自算 |
107
+ | 同花顺 | `pb.ths` | 题材增强(实验) | **问财 NL 选股**、最细概念题材 | 全程反爬,best-effort |
108
+
109
+ 完整能力矩阵见 `pb.capabilities()` 或 [`probar/core/capabilities.py`](probar/core/capabilities.py)。
110
+
111
+ ## 路线图
112
+
113
+ - **v0.1**:东财全链路(`quote/quotes/kline/intraday/fund_flow/lhb/financials/securities`)+ 统一 schema + 命名空间骨架 + 离线测试 + CI + PyPI。
114
+ - **v0.2**(进行中):通达信 **clean-room 二进制协议**(已落地 `quotes/quote` 五档 + 服务器池业务探针)→ 续做 `kline/intraday/ticks/xdxr`;异步全市场 + 国内 canary 节点。
115
+ - **v0.3**:数据质量标记 + 状态页 + 同花顺(问财/概念,实验性)。
116
+ - 暂缓:港美/期货/基金、指标层。
117
+
118
+ ## 稳定性 / 每日维护
119
+
120
+ - 离线层:解析器单测(冻结样本)+ schema 契约 + ruff/mypy,见 [`ci.yml`](.github/workflows/ci.yml)。
121
+ - 实网层:每日 canary([`scripts/canary.py`](scripts/canary.py))打真实接口并**分类失败**(网络/限频/schema/数据)。
122
+ GitHub runner 在境外会有误报,故 v0.1 为 soft smoke;v0.2 迁国内节点做主巡检。
123
+
124
+ ## 免责声明
125
+
126
+ 本库封装的是各平台**非官方 / 逆向**接口,仅供学习与研究使用。数据版权归东方财富、
127
+ 通达信、同花顺等平台所有;使用者须遵守各平台服务条款,自行承担合规与风险。本库已内置
128
+ 限流以做"友好访问",**不保证接口长期可用**,不对数据准确性或由此产生的任何损失负责。
129
+
130
+ ## 开发协作
131
+
132
+ 本项目维护实行**多方交叉复核**:任何实质性变更在交付前都会经过独立复核、显式标注分歧。
133
+ 工程规范见 [docs/ENGINEERING_GUIDE.md](docs/ENGINEERING_GUIDE.md)、贡献见 [CONTRIBUTING.md](CONTRIBUTING.md)。
134
+
135
+ ## License
136
+
137
+ [MIT](LICENSE)
probar-0.1.0/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # probar
2
+
3
+ 稳定、可观测的 **A 股数据接入层** —— 覆盖东方财富、通达信、同花顺,既能做量化,也能取盘中实时行情。
4
+
5
+ > 设计目标不是"接口最多",而是 **接口坏了能最快发现、最快修、用户最少踩坑**。
6
+ > 数据层优先:回测交给 backtrader / vnpy 等框架,probar 专注把三源数据统一、可靠地喂出来。
7
+
8
+ [![ci](https://github.com/anbeaman/probar/actions/workflows/ci.yml/badge.svg)](https://github.com/anbeaman/probar/actions/workflows/ci.yml)
9
+
10
+ ## 安装
11
+
12
+ ```bash
13
+ pip install probar # 核心:东方财富(pb.dc) + 通达信(pb.tdx)
14
+ pip install "probar[ths]" # 实验性:同花顺 / 问财(pb.ths,反爬 best-effort)
15
+ pip install "probar[async]" # 异步全市场扫描(v0.2)
16
+ pip install "probar[playground]" # 本地接口可视化测试台
17
+ ```
18
+
19
+ ## 接口测试台(本地)
20
+
21
+ 一个可视化网页,列出全部接口,可选接口、填参数、运行,查看输入/输出(表格 + 来源 + 耗时):
22
+
23
+ ```bash
24
+ pip install "probar[playground]"
25
+ python -m probar.playground # 打开 http://127.0.0.1:8787
26
+ ```
27
+
28
+ 左侧选数据源/接口(●已实现 / ○未实现)→ 填参数 → 运行。仅本地开发测试用,会真实联网取数。
29
+
30
+ ## 按数据源拆分的命名空间
31
+
32
+ 每个命名空间**只暴露该源真实支持的接口**。`pb.capabilities()` 记录三源能力(参考),
33
+ 各命名空间暴露其中已实现或计划实现的方法子集。
34
+
35
+ > **已实现**:`pb.dc` 的 `quote / quotes / kline / intraday / fund_flow / lhb / financials / securities`(东财全链路);
36
+ > `pb.tdx` 的 `quotes / quote`(批量实时五档,clean-room 自写二进制协议、纯标准库零依赖)。
37
+ > 其余接口已在命名空间中声明,调用抛 `NotImplementedError` 并注明计划版本;`pb.ths`(同花顺,实验性)计划 v0.3。
38
+
39
+ ```python
40
+ import probar as pb
41
+
42
+ # 东方财富(HTTP/JSON,数据最全)
43
+ pb.dc.quote("600519.SH")
44
+ pb.dc.kline("600519.SH", freq="1d", adjust="qfq")
45
+ pb.dc.fund_flow("000001.SZ")
46
+
47
+ # 通达信(自写二进制协议:批量实时五档;历史分钟/逐笔规划中)
48
+ pb.tdx.quotes(["000001.SZ", "600519.SH"]) # 一次多只,含 L1 五档盘口
49
+
50
+ # 同花顺(题材增强:问财 / 概念;实验性,v0.3 规划)
51
+ # pb.ths.wencai("近5日主力净流入为正且市值<100亿") # 规划中(stub,当前抛 NotImplementedError)
52
+
53
+ # 能力矩阵(各源能力参考;各源数据独立,不互相替换)
54
+ pb.capabilities()
55
+ ```
56
+
57
+ 高级用户可自建实例传入配置:
58
+
59
+ ```python
60
+ from probar import EastMoney
61
+ dc = EastMoney(timeout=5, proxy="http://127.0.0.1:7890")
62
+ dc.kline("000001.SZ")
63
+ ```
64
+
65
+ ## 三源分工
66
+
67
+ | 源 | 命名空间 | 定位 | 强项 | 短板 |
68
+ |---|---|---|---|---|
69
+ | 东方财富 | `pb.dc` | 综合最全(HTTP) | 实时/复权K/资金流/龙虎榜/财报/多市场最全 | 历史逐笔无;北向实时已停披露 |
70
+ | 通达信 | `pb.tdx` | 行情底座 | 批量实时、历史分钟、**历史逐笔** | 无资金流/龙虎榜/北向;复权需自算 |
71
+ | 同花顺 | `pb.ths` | 题材增强(实验) | **问财 NL 选股**、最细概念题材 | 全程反爬,best-effort |
72
+
73
+ 完整能力矩阵见 `pb.capabilities()` 或 [`probar/core/capabilities.py`](probar/core/capabilities.py)。
74
+
75
+ ## 路线图
76
+
77
+ - **v0.1**:东财全链路(`quote/quotes/kline/intraday/fund_flow/lhb/financials/securities`)+ 统一 schema + 命名空间骨架 + 离线测试 + CI + PyPI。
78
+ - **v0.2**(进行中):通达信 **clean-room 二进制协议**(已落地 `quotes/quote` 五档 + 服务器池业务探针)→ 续做 `kline/intraday/ticks/xdxr`;异步全市场 + 国内 canary 节点。
79
+ - **v0.3**:数据质量标记 + 状态页 + 同花顺(问财/概念,实验性)。
80
+ - 暂缓:港美/期货/基金、指标层。
81
+
82
+ ## 稳定性 / 每日维护
83
+
84
+ - 离线层:解析器单测(冻结样本)+ schema 契约 + ruff/mypy,见 [`ci.yml`](.github/workflows/ci.yml)。
85
+ - 实网层:每日 canary([`scripts/canary.py`](scripts/canary.py))打真实接口并**分类失败**(网络/限频/schema/数据)。
86
+ GitHub runner 在境外会有误报,故 v0.1 为 soft smoke;v0.2 迁国内节点做主巡检。
87
+
88
+ ## 免责声明
89
+
90
+ 本库封装的是各平台**非官方 / 逆向**接口,仅供学习与研究使用。数据版权归东方财富、
91
+ 通达信、同花顺等平台所有;使用者须遵守各平台服务条款,自行承担合规与风险。本库已内置
92
+ 限流以做"友好访问",**不保证接口长期可用**,不对数据准确性或由此产生的任何损失负责。
93
+
94
+ ## 开发协作
95
+
96
+ 本项目维护实行**多方交叉复核**:任何实质性变更在交付前都会经过独立复核、显式标注分歧。
97
+ 工程规范见 [docs/ENGINEERING_GUIDE.md](docs/ENGINEERING_GUIDE.md)、贡献见 [CONTRIBUTING.md](CONTRIBUTING.md)。
98
+
99
+ ## License
100
+
101
+ [MIT](LICENSE)
@@ -0,0 +1,54 @@
1
+ """probar —— 稳定、可观测的 A 股数据接入层。
2
+
3
+ 按数据源拆分独立命名空间,每个命名空间只暴露该源**真实支持**的接口:
4
+
5
+ pb.dc 东方财富(HTTP/JSON,数据最全:实时 / 复权K / 资金流 / 龙虎榜 / 财报)
6
+ pb.tdx 通达信(自写二进制协议,行情底座:批量实时五档 / 历史分钟 / 历史逐笔)
7
+ pb.ths 同花顺(题材增强:问财 / 概念,best-effort 反爬,实验性)
8
+
9
+ pb.capabilities() 返回三源能力矩阵(DataFrame)
10
+
11
+ 各源数据**各自独立、口径可能不一致**:用户按需选源,probar 不做"主源"也不做跨源替换。
12
+ 设计原则见 README。本库为非官方/逆向接口封装,使用前请阅读免责声明。
13
+ """
14
+
15
+ from ._version import __version__
16
+ from .core.capabilities import capabilities
17
+ from .core.errors import (
18
+ NetworkError,
19
+ NoData,
20
+ NotSupported,
21
+ ProbarError,
22
+ RateLimited,
23
+ SchemaChanged,
24
+ )
25
+ from .providers.eastmoney import EastMoney
26
+ from .providers.tdx import Tdx
27
+ from .providers.ths import Ths
28
+
29
+ # 默认实例:绑定到各命名空间。高级用户可自建实例传入配置,如
30
+ # from probar import EastMoney
31
+ # dc = EastMoney(timeout=5, proxy="http://127.0.0.1:7890")
32
+ dc = EastMoney()
33
+ tdx = Tdx()
34
+ ths = Ths()
35
+
36
+ __all__ = [
37
+ "__version__",
38
+ "capabilities",
39
+ # 命名空间(默认实例)
40
+ "dc",
41
+ "tdx",
42
+ "ths",
43
+ # Provider 类(自定义配置时使用)
44
+ "EastMoney",
45
+ "Tdx",
46
+ "Ths",
47
+ # 异常
48
+ "ProbarError",
49
+ "NetworkError",
50
+ "RateLimited",
51
+ "NotSupported",
52
+ "NoData",
53
+ "SchemaChanged",
54
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ """probar 共享基础设施层(L1):错误模型、数据契约、传输、限流、缓存、日历、代码归一化、能力矩阵。
2
+
3
+ 各数据源命名空间(dc/tdx/ths)都建立在这一层之上。
4
+ """
@@ -0,0 +1,36 @@
1
+ """极简 TTL 内存缓存。
2
+
3
+ 盘中快照走短 TTL,避免重复请求打爆数据源;历史数据的落盘缓存留待后续版本
4
+ (diskcache / sqlite)。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import threading
10
+ import time
11
+ from typing import Any
12
+
13
+
14
+ class TTLCache:
15
+ def __init__(self, ttl: float = 2.0, maxsize: int = 4096) -> None:
16
+ self.ttl = ttl
17
+ self.maxsize = maxsize
18
+ self._d: dict[Any, tuple[float, Any]] = {}
19
+ self._lock = threading.Lock()
20
+
21
+ def get(self, key: Any) -> Any | None:
22
+ with self._lock:
23
+ item = self._d.get(key)
24
+ if item is None:
25
+ return None
26
+ expire, value = item
27
+ if expire < time.monotonic():
28
+ self._d.pop(key, None)
29
+ return None
30
+ return value
31
+
32
+ def set(self, key: Any, value: Any, ttl: float | None = None) -> None:
33
+ with self._lock:
34
+ if len(self._d) >= self.maxsize:
35
+ self._d.clear() # 简单粗暴的逐出策略,scaffold 够用
36
+ self._d[key] = (time.monotonic() + (ttl if ttl is not None else self.ttl), value)
@@ -0,0 +1,17 @@
1
+ """交易日历(占位实现)。
2
+
3
+ v0.1 仅按工作日粗判;接入交易所节假日表(以及午休/集合竞价时段判断)留待后续版本。
4
+ TODO(v0.2): 内置或拉取 SSE/SZSE 节假日数据,补 ``is_open_now`` / ``previous_trading_day``。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from datetime import date, datetime
10
+
11
+
12
+ def is_trading_day(d: date | datetime | None = None) -> bool:
13
+ """是否为交易日(当前仅排除周末,**未含法定节假日**)。"""
14
+ d = d or date.today()
15
+ if isinstance(d, datetime):
16
+ d = d.date()
17
+ return d.weekday() < 5
@@ -0,0 +1,63 @@
1
+ """三源能力矩阵 —— 定稿。
2
+
3
+ 这是对三个**数据源本身**能力的参考记录(某源能不能提供某类数据),不是方法清单:
4
+ 各命名空间(pb.dc/tdx/ths)按路线图暴露其中**已实现或计划实现**的方法子集;
5
+ 真实可调用的方法以 ``dir(pb.dc)`` / IDE 自动补全为准。
6
+
7
+
8
+ 档位:
9
+ FULL ✅ 强,可做主实现
10
+ PART 🔸 部分/弱/需二次计算
11
+ SOFT ⚠️ 反爬脆,best-effort(同花顺多数能力)
12
+ NONE ❌ 无,命名空间里不提供该接口
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import TYPE_CHECKING
18
+
19
+ if TYPE_CHECKING:
20
+ import pandas as pd
21
+
22
+ FULL, PART, SOFT, NONE = "✅", "🔸", "⚠️", "❌"
23
+
24
+ # capability -> {dc, tdx, ths}
25
+ CAPABILITIES: dict[str, dict[str, str]] = {
26
+ "实时快照 quote": {"dc": FULL, "tdx": FULL, "ths": SOFT},
27
+ "五档盘口(仅L1)": {"dc": FULL, "tdx": FULL, "ths": SOFT},
28
+ "当日分时 intraday": {"dc": FULL, "tdx": FULL, "ths": SOFT},
29
+ "历史分时 intraday_hist": {"dc": PART, "tdx": FULL, "ths": SOFT},
30
+ "当日逐笔 ticks": {"dc": PART, "tdx": FULL, "ths": SOFT},
31
+ "历史逐笔 ticks_hist": {"dc": NONE, "tdx": PART, "ths": NONE},
32
+ "K线 日/周/月": {"dc": FULL, "tdx": FULL, "ths": SOFT},
33
+ "K线 分钟": {"dc": PART, "tdx": FULL, "ths": SOFT},
34
+ "前/后复权 adjust": {"dc": FULL, "tdx": PART, "ths": SOFT},
35
+ "资金流 fund_flow": {"dc": FULL, "tdx": NONE, "ths": SOFT},
36
+ "龙虎榜 lhb": {"dc": FULL, "tdx": NONE, "ths": SOFT},
37
+ "北向/沪深港通 hsgt": {"dc": PART, "tdx": NONE, "ths": SOFT},
38
+ "财务报表/业绩 financials": {"dc": FULL, "tdx": PART, "ths": SOFT},
39
+ "股东/解禁/分红": {"dc": FULL, "tdx": PART, "ths": SOFT},
40
+ "板块/概念成分": {"dc": FULL, "tdx": PART, "ths": SOFT},
41
+ "细粒度概念题材": {"dc": PART, "tdx": PART, "ths": SOFT},
42
+ "自然语言选股 wencai": {"dc": NONE, "tdx": NONE, "ths": SOFT},
43
+ "证券代码表 securities": {"dc": FULL, "tdx": FULL, "ths": PART},
44
+ "除权除息 xdxr": {"dc": FULL, "tdx": FULL, "ths": SOFT},
45
+ "多市场(港美/期货/基金/转债)": {"dc": FULL, "tdx": PART, "ths": SOFT},
46
+ }
47
+
48
+ # 同花顺整体为反爬 best-effort:内容(题材/问财)是三源最强,但抓取可靠性最低。
49
+ NOTES = {
50
+ "ths": "全程反爬(hexin-v),best-effort;问财与细粒度概念题材为其独有价值。",
51
+ "tdx": "无资金流/龙虎榜/北向(协议无此数据域);复权需用 xdxr 自算;逐笔为分笔明细非 L2。",
52
+ "dc": "数据最全(实时/复权/资金流/龙虎榜/财报);北向实时盘中已停披露,仅 EOD/额度。",
53
+ }
54
+
55
+
56
+ def capabilities() -> pd.DataFrame:
57
+ """返回能力矩阵(行=能力,列=dc/tdx/ths)。"""
58
+ import pandas as pd
59
+
60
+ df = pd.DataFrame(CAPABILITIES).T
61
+ df = df[["dc", "tdx", "ths"]]
62
+ df.index.name = "capability"
63
+ return df
@@ -0,0 +1,32 @@
1
+ """结构化异常模型。
2
+
3
+ 调用方可按类型精确处理:网络抖动重试、限频退避、源不支持降级、无数据跳过。
4
+ 其中 ``SchemaChanged`` 通常意味着上游接口字段变更 —— 正是每日 canary 巡检要替用户
5
+ 最先发现的那一类。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+
11
+ class ProbarError(Exception):
12
+ """probar 所有异常的基类。"""
13
+
14
+
15
+ class NetworkError(ProbarError):
16
+ """网络/超时/连接失败(已穷尽重试)。"""
17
+
18
+
19
+ class RateLimited(ProbarError):
20
+ """被数据源限频(如 HTTP 429)。"""
21
+
22
+
23
+ class NotSupported(ProbarError):
24
+ """该数据源不支持此接口(能力矩阵中标记为无)。"""
25
+
26
+
27
+ class NoData(ProbarError):
28
+ """请求合法但无数据(停牌 / 未上市 / 区间无成交)。"""
29
+
30
+
31
+ class SchemaChanged(ProbarError):
32
+ """上游响应结构与预期契约不符,接口可能已变更。"""
@@ -0,0 +1,69 @@
1
+ """基于 httpx 的同步 HTTP 传输层:统一超时、限流、退避重试、默认请求头。
2
+
3
+ 只负责"把请求安全地发出去、把 JSON 拿回来",不关心字段语义(解析交给各 provider)。
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import time
9
+ from typing import Any
10
+
11
+ import httpx
12
+
13
+ from .errors import NetworkError, RateLimited
14
+ from .rate_limit import TokenBucket
15
+
16
+ DEFAULT_HEADERS = {
17
+ "User-Agent": (
18
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
19
+ "(KHTML, like Gecko) Chrome/124.0 Safari/537.36"
20
+ ),
21
+ "Accept": "*/*",
22
+ }
23
+
24
+
25
+ class HttpClient:
26
+ def __init__(
27
+ self,
28
+ *,
29
+ timeout: float = 8.0,
30
+ rate: float = 10.0,
31
+ proxy: str | None = None,
32
+ headers: dict[str, str] | None = None,
33
+ retries: int = 3,
34
+ ) -> None:
35
+ self._bucket = TokenBucket(rate)
36
+ self._retries = max(1, retries)
37
+ client_kwargs: dict[str, Any] = {
38
+ "timeout": timeout,
39
+ "headers": {**DEFAULT_HEADERS, **(headers or {})},
40
+ "follow_redirects": True,
41
+ }
42
+ if proxy: # 仅在显式传入时才加,避免老版 httpx 不识别 proxy/proxies 之别
43
+ client_kwargs["proxy"] = proxy
44
+ self._client = httpx.Client(**client_kwargs)
45
+
46
+ def get_json(
47
+ self, url: str, params: dict[str, Any] | None = None, *, referer: str | None = None
48
+ ) -> Any:
49
+ """限流 + 重试地发起 GET 并解析 JSON。失败穷尽重试后抛 :class:`NetworkError`。"""
50
+ headers = {"Referer": referer} if referer else None
51
+ last_err: Exception | None = None
52
+ for attempt in range(self._retries):
53
+ self._bucket.acquire()
54
+ try:
55
+ resp = self._client.get(url, params=params, headers=headers)
56
+ if resp.status_code == 429:
57
+ raise RateLimited(f"429 Too Many Requests: {url}")
58
+ resp.raise_for_status()
59
+ # 非 JSON(被 WAF 拦截返回 HTML 等)时 .json() 抛 ValueError,纳入重试与分类
60
+ return resp.json()
61
+ except RateLimited:
62
+ raise
63
+ except (httpx.TransportError, httpx.HTTPStatusError, ValueError) as err:
64
+ last_err = err
65
+ time.sleep(0.3 * (attempt + 1))
66
+ raise NetworkError(f"GET {url} 失败(已重试 {self._retries} 次): {last_err!r}")
67
+
68
+ def close(self) -> None:
69
+ self._client.close()