stockroute 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.
- stockroute-0.1.0/LICENSE +21 -0
- stockroute-0.1.0/PKG-INFO +51 -0
- stockroute-0.1.0/README.md +29 -0
- stockroute-0.1.0/pyproject.toml +26 -0
- stockroute-0.1.0/setup.cfg +4 -0
- stockroute-0.1.0/src/stockroute/__init__.py +17 -0
- stockroute-0.1.0/src/stockroute/client.py +170 -0
- stockroute-0.1.0/src/stockroute/exceptions.py +48 -0
- stockroute-0.1.0/src/stockroute.egg-info/PKG-INFO +51 -0
- stockroute-0.1.0/src/stockroute.egg-info/SOURCES.txt +12 -0
- stockroute-0.1.0/src/stockroute.egg-info/dependency_links.txt +1 -0
- stockroute-0.1.0/src/stockroute.egg-info/requires.txt +2 -0
- stockroute-0.1.0/src/stockroute.egg-info/top_level.txt +1 -0
- stockroute-0.1.0/tests/test_smoke.py +19 -0
stockroute-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 StockRoute
|
|
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.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stockroute
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: StockRoute 数据 API 官方 Python SDK(A 股行情/财务/资讯,DataFrame-first,PIT 点时财务)
|
|
5
|
+
Author: StockRoute
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://m-stock.600044.xyz
|
|
8
|
+
Project-URL: Documentation, https://m-stock.600044.xyz
|
|
9
|
+
Project-URL: Repository, https://github.com/Stock-Route/stockroute-sdk
|
|
10
|
+
Keywords: A股,量化,数据,kline,financial,PIT
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: pandas>=1.5
|
|
20
|
+
Requires-Dist: requests>=2.28
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# stockroute-sdk
|
|
24
|
+
|
|
25
|
+
StockRoute 数据 API 官方 Python SDK(A 股行情 / 财务 / 资讯 / 元数据,统一 DataFrame-first 体验)。
|
|
26
|
+
|
|
27
|
+
> ✅ v0.1.0 已发布(PyPI:`pip install stockroute`)
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from stockroute import StockRoute
|
|
31
|
+
sr = StockRoute() # 读环境变量 STOCKROUTE_TOKEN
|
|
32
|
+
df = sr.kline("600519") # 日K:免费3年/基础5年/标准+全历史
|
|
33
|
+
print(df.attrs["units"]) # 字段单位口径
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 计划特性
|
|
37
|
+
- 覆盖 StockRoute 开放 API 全部端点:K线(多档历史深度)/ 点时财务(PIT 强制)/ 通用数据直查(60+ 数据集)/ 每日估值 / 全市场列表 / 快讯
|
|
38
|
+
- **DataFrame-first**:返回 pandas DataFrame,单位口径(units)与档位信息自动挂载到 `attrs`
|
|
39
|
+
- **类型化异常**:鉴权 / 档位不足 / 配额超限 / 服务限流分层清晰,报错即指引
|
|
40
|
+
- 自动分页与限速友好(429 / Retry-After 自动处理)
|
|
41
|
+
- 点时财务(PIT):`as_of` 参数强制显式,杜绝前视
|
|
42
|
+
|
|
43
|
+
## 文档
|
|
44
|
+
接口文档站:https://m-stock.600044.xyz
|
|
45
|
+
|
|
46
|
+
## 许可
|
|
47
|
+
MIT(见 LICENSE)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
StockRoute · 面向量化研究者的 A 股数据质量层
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# stockroute-sdk
|
|
2
|
+
|
|
3
|
+
StockRoute 数据 API 官方 Python SDK(A 股行情 / 财务 / 资讯 / 元数据,统一 DataFrame-first 体验)。
|
|
4
|
+
|
|
5
|
+
> ✅ v0.1.0 已发布(PyPI:`pip install stockroute`)
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from stockroute import StockRoute
|
|
9
|
+
sr = StockRoute() # 读环境变量 STOCKROUTE_TOKEN
|
|
10
|
+
df = sr.kline("600519") # 日K:免费3年/基础5年/标准+全历史
|
|
11
|
+
print(df.attrs["units"]) # 字段单位口径
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 计划特性
|
|
15
|
+
- 覆盖 StockRoute 开放 API 全部端点:K线(多档历史深度)/ 点时财务(PIT 强制)/ 通用数据直查(60+ 数据集)/ 每日估值 / 全市场列表 / 快讯
|
|
16
|
+
- **DataFrame-first**:返回 pandas DataFrame,单位口径(units)与档位信息自动挂载到 `attrs`
|
|
17
|
+
- **类型化异常**:鉴权 / 档位不足 / 配额超限 / 服务限流分层清晰,报错即指引
|
|
18
|
+
- 自动分页与限速友好(429 / Retry-After 自动处理)
|
|
19
|
+
- 点时财务(PIT):`as_of` 参数强制显式,杜绝前视
|
|
20
|
+
|
|
21
|
+
## 文档
|
|
22
|
+
接口文档站:https://m-stock.600044.xyz
|
|
23
|
+
|
|
24
|
+
## 许可
|
|
25
|
+
MIT(见 LICENSE)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
StockRoute · 面向量化研究者的 A 股数据质量层
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "stockroute"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "StockRoute 数据 API 官方 Python SDK(A 股行情/财务/资讯,DataFrame-first,PIT 点时财务)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "StockRoute"}]
|
|
13
|
+
keywords = ["A股", "量化", "数据", "kline", "financial", "PIT"]
|
|
14
|
+
dependencies = ["pandas>=1.5", "requests>=2.28"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://m-stock.600044.xyz"
|
|
25
|
+
Documentation = "https://m-stock.600044.xyz"
|
|
26
|
+
Repository = "https://github.com/Stock-Route/stockroute-sdk"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""StockRoute Python SDK——A 股数据(行情/财务/资讯/元数据)统一访问。
|
|
2
|
+
|
|
3
|
+
快速开始:
|
|
4
|
+
from stockroute import StockRoute
|
|
5
|
+
sr = StockRoute() # 读环境变量 STOCKROUTE_TOKEN
|
|
6
|
+
df = sr.kline("600519") # 近3年日K(档位越高历史越长)
|
|
7
|
+
df = sr.query("board.zt_pools") # 通用数据直查
|
|
8
|
+
df.attrs["units"] # 字段单位口径
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .client import StockRoute
|
|
12
|
+
from .exceptions import (AuthError, NotFoundError, QuotaError, RateLimited,
|
|
13
|
+
ServerError, StockRouteError, TierError)
|
|
14
|
+
|
|
15
|
+
__version__ = "0.1.0"
|
|
16
|
+
__all__ = ["StockRoute", "StockRouteError", "AuthError", "TierError",
|
|
17
|
+
"QuotaError", "RateLimited", "NotFoundError", "ServerError"]
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""StockRoute 客户端:统一鉴权/重试/类型化异常/DataFrame-first。"""
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from .exceptions import (AuthError, NotFoundError, QuotaError, RateLimited,
|
|
9
|
+
ServerError, TierError)
|
|
10
|
+
|
|
11
|
+
DEFAULT_BASE = "https://api-stock.600044.xyz"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _parse_detail(body) -> str:
|
|
15
|
+
if isinstance(body, dict):
|
|
16
|
+
for k in ("detail", "message"):
|
|
17
|
+
if body.get(k):
|
|
18
|
+
return str(body[k])
|
|
19
|
+
return str(body)[:300]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class StockRoute:
|
|
23
|
+
"""StockRoute 数据 API 客户端。
|
|
24
|
+
|
|
25
|
+
token: 访问令牌(门户「Token」页签发);缺省读环境变量 STOCKROUTE_TOKEN。
|
|
26
|
+
base: API 地址,默认公网入口,一般无需修改。
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, token: str = None, base: str = None, timeout: int = 30, retries: int = 2):
|
|
30
|
+
self.token = token or os.environ.get("STOCKROUTE_TOKEN")
|
|
31
|
+
if not self.token:
|
|
32
|
+
raise AuthError("缺少 token:构造传参,或设置环境变量 STOCKROUTE_TOKEN")
|
|
33
|
+
self.base = (base or DEFAULT_BASE).rstrip("/")
|
|
34
|
+
self.timeout = timeout
|
|
35
|
+
self.retries = retries
|
|
36
|
+
self._sess = requests.Session()
|
|
37
|
+
self._sess.headers.update({"Authorization": f"Bearer {self.token}",
|
|
38
|
+
"Accept": "application/json"})
|
|
39
|
+
|
|
40
|
+
# ── 底层 ──
|
|
41
|
+
def _get(self, path: str, params: dict = None) -> dict:
|
|
42
|
+
"""GET + 自动重试(502/503/网络抖动)+ 类型化异常。200 → JSON dict。"""
|
|
43
|
+
url = self.base + path
|
|
44
|
+
r = None
|
|
45
|
+
for i in range(self.retries + 1):
|
|
46
|
+
try:
|
|
47
|
+
r = self._sess.get(url, params=params or {}, timeout=self.timeout)
|
|
48
|
+
except requests.RequestException as e:
|
|
49
|
+
if i < self.retries:
|
|
50
|
+
time.sleep(0.5 * (i + 1))
|
|
51
|
+
continue
|
|
52
|
+
raise ServerError(f"网络错误: {e}", status=0)
|
|
53
|
+
if r.status_code in (502, 503) and i < self.retries:
|
|
54
|
+
time.sleep(0.5 * (i + 1))
|
|
55
|
+
continue
|
|
56
|
+
break
|
|
57
|
+
if r.status_code == 200:
|
|
58
|
+
return r.json()
|
|
59
|
+
detail = _parse_detail_body(r)
|
|
60
|
+
if r.status_code == 401:
|
|
61
|
+
raise AuthError(detail)
|
|
62
|
+
if r.status_code == 403 and "tier below min_points" in detail:
|
|
63
|
+
have, need = _extract_have_need(detail)
|
|
64
|
+
raise TierError(f"档位不足:需 {need} 分,当前 {have} 分。到门户「充值」页升级",
|
|
65
|
+
have=have, need=need)
|
|
66
|
+
if r.status_code == 403 and "exceeded" in detail:
|
|
67
|
+
raise QuotaError(detail)
|
|
68
|
+
if r.status_code == 429:
|
|
69
|
+
raise RateLimited(detail, retry_after=int(r.headers.get("Retry-After", "60")))
|
|
70
|
+
if r.status_code == 404:
|
|
71
|
+
raise NotFoundError(detail)
|
|
72
|
+
raise ServerError(f"HTTP {r.status_code}: {detail}", status=r.status_code)
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def _to_df(payload: dict) -> pd.DataFrame:
|
|
76
|
+
"""rows → DataFrame;units/count/total 等挂 attrs(与数据一起自然传递)。"""
|
|
77
|
+
df = pd.DataFrame(payload.get("rows") or [])
|
|
78
|
+
df.attrs["units"] = payload.get("units") or {}
|
|
79
|
+
df.attrs["count"] = payload.get("count")
|
|
80
|
+
df.attrs["total"] = payload.get("total")
|
|
81
|
+
df.attrs["raw"] = {k: v for k, v in payload.items() if k != "rows"}
|
|
82
|
+
return df
|
|
83
|
+
|
|
84
|
+
# ── 数据端点 ──
|
|
85
|
+
def kline(self, code: str, start: str = None, end: str = None) -> pd.DataFrame:
|
|
86
|
+
"""个股日 K(OHLCV/涨跌幅/复权/ST/估值尾列)。
|
|
87
|
+
历史深度按档位:免费3年 / 基础5年 / 标准及以上全历史。"""
|
|
88
|
+
p = self._get("/api/kline", _clean(code=code, start=start, end=end))
|
|
89
|
+
return self._to_df(p)
|
|
90
|
+
|
|
91
|
+
def financial(self, code: str, kind: str, as_of: str) -> pd.DataFrame:
|
|
92
|
+
"""点时财务(PIT 强制):只返回公告日严格早于 as_of 的记录,杜绝前视。
|
|
93
|
+
kind: profit/growth/balance/cashflow/dividend/forecast/express/dupont"""
|
|
94
|
+
p = self._get("/api/financial", _clean(code=code, kind=kind, as_of=as_of))
|
|
95
|
+
df = self._to_df(p)
|
|
96
|
+
df.attrs["pit_rule"] = p.get("pit_rule")
|
|
97
|
+
return df
|
|
98
|
+
|
|
99
|
+
def daily_basic(self, code: str, date: str = None) -> pd.DataFrame:
|
|
100
|
+
"""单股每日估值全套(PE/PB/股息率/换手/市值)。date 缺省=最新。"""
|
|
101
|
+
p = self._get("/api/daily_basic", _clean(code=code, date=date))
|
|
102
|
+
return self._to_df(p)
|
|
103
|
+
|
|
104
|
+
def query(self, dataset: str, code: str = None, start: str = None,
|
|
105
|
+
end: str = None, limit: int = None) -> pd.DataFrame:
|
|
106
|
+
"""通用数据直查(60+ 数据集)。可选集见 self.datasets() 或接口文档。
|
|
107
|
+
无过滤参数时返回最新一个日期切片;行数按档位封顶。"""
|
|
108
|
+
p = self._get("/api/query", _clean(dataset=dataset, code=code,
|
|
109
|
+
start=start, end=end, limit=limit))
|
|
110
|
+
return self._to_df(p)
|
|
111
|
+
|
|
112
|
+
def stocks(self, status: str = "L", industry: str = None, keyword: str = None,
|
|
113
|
+
limit: int = 1000, offset: int = 0, paginate: bool = False) -> pd.DataFrame:
|
|
114
|
+
"""全 A 股列表(代码/名称/行业分类/上市退市日)。
|
|
115
|
+
paginate=True 自动翻页拉全量(total≈3800)。"""
|
|
116
|
+
out = []
|
|
117
|
+
off = offset
|
|
118
|
+
while True:
|
|
119
|
+
p = self._get("/api/meta/stocks", _clean(status=status, industry=industry,
|
|
120
|
+
keyword=keyword, limit=limit, offset=off))
|
|
121
|
+
rows = p.get("rows") or []
|
|
122
|
+
out.extend(rows)
|
|
123
|
+
total = p.get("total")
|
|
124
|
+
off += len(rows)
|
|
125
|
+
if not paginate or not rows or (total is not None and off >= total):
|
|
126
|
+
break
|
|
127
|
+
df = pd.DataFrame(out)
|
|
128
|
+
df.attrs["total"] = total
|
|
129
|
+
df.attrs["raw"] = {"offset": offset, "limit": limit, "paginate": paginate}
|
|
130
|
+
return df
|
|
131
|
+
|
|
132
|
+
def news(self, since: str = None, code: str = None, keyword: str = None,
|
|
133
|
+
cat: str = None, src: str = None, limit: int = None) -> pd.DataFrame:
|
|
134
|
+
"""全市场快讯(增量游标 since=上次最新 ts;支持按股/关键词/类别)。"""
|
|
135
|
+
p = self._get("/api/news", _clean(since=since, code=code, keyword=keyword,
|
|
136
|
+
cat=cat, src=src, limit=limit))
|
|
137
|
+
df = self._to_df(p)
|
|
138
|
+
if len(df):
|
|
139
|
+
df.attrs["last_ts"] = df.iloc[0].get("ts") # 增量游标:下次作 since
|
|
140
|
+
return df
|
|
141
|
+
|
|
142
|
+
# ── 元数据 ──
|
|
143
|
+
def datasets(self) -> dict:
|
|
144
|
+
"""数据集契约字典(units/SLA/min_points/PIT 规则),按当前档位过滤。"""
|
|
145
|
+
return self._get("/api/datasets")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _clean(**kw) -> dict:
|
|
149
|
+
"""去 None 参数。"""
|
|
150
|
+
return {k: v for k, v in kw.items() if v is not None}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _parse_detail_body(r) -> str:
|
|
154
|
+
try:
|
|
155
|
+
body = r.json()
|
|
156
|
+
except Exception:
|
|
157
|
+
return (r.text or "")[:300]
|
|
158
|
+
if isinstance(body, dict):
|
|
159
|
+
for k in ("detail", "message"):
|
|
160
|
+
if body.get(k):
|
|
161
|
+
return str(body[k])
|
|
162
|
+
return str(body)[:300]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _extract_have_need(detail: str):
|
|
166
|
+
import re
|
|
167
|
+
m_have = re.search(r"have (\d+)pts", detail)
|
|
168
|
+
m_need = re.search(r"need (\d+)", detail)
|
|
169
|
+
return (int(m_have.group(1)) if m_have else None,
|
|
170
|
+
int(m_need.group(1)) if m_need else None)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""StockRoute SDK 异常类型——按错误语义分层,报错即指引。"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class StockRouteError(Exception):
|
|
5
|
+
"""所有 StockRoute 异常的基类。"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AuthError(StockRouteError):
|
|
9
|
+
"""401:Token 缺失/无效/已吊销。到门户「Token」页重签。"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TierError(StockRouteError):
|
|
13
|
+
"""403:当前档位不足以访问该数据集。
|
|
14
|
+
|
|
15
|
+
属性:
|
|
16
|
+
have: 当前累计积分
|
|
17
|
+
need: 该数据集所需积分
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, message: str, have=None, need=None):
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self.have = have
|
|
23
|
+
self.need = need
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class QuotaError(StockRouteError):
|
|
27
|
+
"""403/429:配额超限(月点数/月行数/日点数/日行数/月次数)。
|
|
28
|
+
message 内含具体额度;日/月配额自然周期重置。"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RateLimited(StockRouteError):
|
|
32
|
+
"""429:触发档位限速。建议按 retry_after 秒后重试。"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, message: str, retry_after: int = 60):
|
|
35
|
+
super().__init__(message)
|
|
36
|
+
self.retry_after = retry_after
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class NotFoundError(StockRouteError):
|
|
40
|
+
"""404:无数据(代码/日期窗口无匹配)或数据集不可直查。"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ServerError(StockRouteError):
|
|
44
|
+
"""5xx:服务端错误/权限服务不可达(fail-closed)。SDK 已自动重试,建议稍后再试。"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, message: str, status: int = 500):
|
|
47
|
+
super().__init__(message)
|
|
48
|
+
self.status = status
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stockroute
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: StockRoute 数据 API 官方 Python SDK(A 股行情/财务/资讯,DataFrame-first,PIT 点时财务)
|
|
5
|
+
Author: StockRoute
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://m-stock.600044.xyz
|
|
8
|
+
Project-URL: Documentation, https://m-stock.600044.xyz
|
|
9
|
+
Project-URL: Repository, https://github.com/Stock-Route/stockroute-sdk
|
|
10
|
+
Keywords: A股,量化,数据,kline,financial,PIT
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: pandas>=1.5
|
|
20
|
+
Requires-Dist: requests>=2.28
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# stockroute-sdk
|
|
24
|
+
|
|
25
|
+
StockRoute 数据 API 官方 Python SDK(A 股行情 / 财务 / 资讯 / 元数据,统一 DataFrame-first 体验)。
|
|
26
|
+
|
|
27
|
+
> ✅ v0.1.0 已发布(PyPI:`pip install stockroute`)
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from stockroute import StockRoute
|
|
31
|
+
sr = StockRoute() # 读环境变量 STOCKROUTE_TOKEN
|
|
32
|
+
df = sr.kline("600519") # 日K:免费3年/基础5年/标准+全历史
|
|
33
|
+
print(df.attrs["units"]) # 字段单位口径
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 计划特性
|
|
37
|
+
- 覆盖 StockRoute 开放 API 全部端点:K线(多档历史深度)/ 点时财务(PIT 强制)/ 通用数据直查(60+ 数据集)/ 每日估值 / 全市场列表 / 快讯
|
|
38
|
+
- **DataFrame-first**:返回 pandas DataFrame,单位口径(units)与档位信息自动挂载到 `attrs`
|
|
39
|
+
- **类型化异常**:鉴权 / 档位不足 / 配额超限 / 服务限流分层清晰,报错即指引
|
|
40
|
+
- 自动分页与限速友好(429 / Retry-After 自动处理)
|
|
41
|
+
- 点时财务(PIT):`as_of` 参数强制显式,杜绝前视
|
|
42
|
+
|
|
43
|
+
## 文档
|
|
44
|
+
接口文档站:https://m-stock.600044.xyz
|
|
45
|
+
|
|
46
|
+
## 许可
|
|
47
|
+
MIT(见 LICENSE)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
StockRoute · 面向量化研究者的 A 股数据质量层
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/stockroute/__init__.py
|
|
5
|
+
src/stockroute/client.py
|
|
6
|
+
src/stockroute/exceptions.py
|
|
7
|
+
src/stockroute.egg-info/PKG-INFO
|
|
8
|
+
src/stockroute.egg-info/SOURCES.txt
|
|
9
|
+
src/stockroute.egg-info/dependency_links.txt
|
|
10
|
+
src/stockroute.egg-info/requires.txt
|
|
11
|
+
src/stockroute.egg-info/top_level.txt
|
|
12
|
+
tests/test_smoke.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
stockroute
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""冒烟:未联网部分(异常层级/参数清洗)。真实调用见 examples/。"""
|
|
2
|
+
from stockroute import (AuthError, NotFoundError, QuotaError, RateLimited,
|
|
3
|
+
ServerError, StockRoute, StockRouteError, TierError)
|
|
4
|
+
from stockroute.client import _clean
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_exception_hierarchy():
|
|
8
|
+
assert issubclass(TierError, StockRouteError)
|
|
9
|
+
assert issubclass(QuotaError, StockRouteError)
|
|
10
|
+
assert issubclass(RateLimited, StockRouteError)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_tier_error_attrs():
|
|
14
|
+
e = TierError("需 5000 分,当前 1000 分", have=1000, need=5000)
|
|
15
|
+
assert e.have == 1000 and e.need == 5000
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_clean_drops_none():
|
|
19
|
+
assert _clean(a=1, b=None) == {"a": 1}
|