finclaw-data-sdk 0.1.3__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.
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: finclaw-data-sdk
3
+ Version: 0.1.3
4
+ Summary: A 股量化数据 HTTP 客户端(QuantClient),只依赖 pandas
5
+ Author-email: dekeky <dekeky@163.com>
6
+ Project-URL: Homepage, https://github.com/chocochato0713/finclaw-data
7
+ Project-URL: Repository, https://github.com/chocochato0713/finclaw-data
8
+ Keywords: finclaw,ashare,quant,kline,clickhouse
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Topic :: Office/Business :: Financial
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: Financial and Insurance Industry
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: pandas>=2.0
22
+
23
+ # finclaw-data-sdk
24
+
25
+ A 股量化数据 HTTP 客户端。安装后 `from finclaw_data_sdk import QuantClient`,只依赖 pandas,不含拉取与 ClickHouse 业务代码。
26
+
27
+ ```bash
28
+ pip install finclaw-data-sdk
29
+ ```
30
+
31
+ ```python
32
+ from finclaw_data_sdk import QuantClient, cols
33
+
34
+ c = QuantClient("http://127.0.0.1:8000")
35
+ bars = c.kline(symbol="600519", start="20240801", end="20240805", adj="qfq")
36
+ snap = c.snapshot(trade_date="20240819", symbols=["600519", "000001"], adj="qfq")
37
+ df = c.fina(symbol="600519", start="20240101", end="20241231", fields=cols.fina.EPS_BASIC)
38
+ idx = c.index_list()
39
+ idx_bars = c.index_kline(index="000300", start="20240801", end="20240805", fields=cols.index_kline.CLOSE)
40
+ cons = c.index_constituents(index="000300", fields=cols.index_constituents.SYMBOL)
41
+ ```
42
+
43
+ 日期参数为 `YYYYMMDD`,区间两端包含。需先启动本仓库的 `finclaw-data serve`。直连 ClickHouse 请安装服务包 `finclaw-data`,使用 `QuantClient.from_clickhouse()`。
@@ -0,0 +1,21 @@
1
+ # finclaw-data-sdk
2
+
3
+ A 股量化数据 HTTP 客户端。安装后 `from finclaw_data_sdk import QuantClient`,只依赖 pandas,不含拉取与 ClickHouse 业务代码。
4
+
5
+ ```bash
6
+ pip install finclaw-data-sdk
7
+ ```
8
+
9
+ ```python
10
+ from finclaw_data_sdk import QuantClient, cols
11
+
12
+ c = QuantClient("http://127.0.0.1:8000")
13
+ bars = c.kline(symbol="600519", start="20240801", end="20240805", adj="qfq")
14
+ snap = c.snapshot(trade_date="20240819", symbols=["600519", "000001"], adj="qfq")
15
+ df = c.fina(symbol="600519", start="20240101", end="20241231", fields=cols.fina.EPS_BASIC)
16
+ idx = c.index_list()
17
+ idx_bars = c.index_kline(index="000300", start="20240801", end="20240805", fields=cols.index_kline.CLOSE)
18
+ cons = c.index_constituents(index="000300", fields=cols.index_constituents.SYMBOL)
19
+ ```
20
+
21
+ 日期参数为 `YYYYMMDD`,区间两端包含。需先启动本仓库的 `finclaw-data serve`。直连 ClickHouse 请安装服务包 `finclaw-data`,使用 `QuantClient.from_clickhouse()`。
@@ -0,0 +1,36 @@
1
+ [project]
2
+ name = "finclaw-data-sdk"
3
+ version = "0.1.3"
4
+ description = "A 股量化数据 HTTP 客户端(QuantClient),只依赖 pandas"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ authors = [
8
+ { name = "dekeky", email = "dekeky@163.com" },
9
+ ]
10
+ dependencies = [
11
+ "pandas>=2.0",
12
+ ]
13
+ keywords = ["finclaw", "ashare", "quant", "kline", "clickhouse"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Operating System :: OS Independent",
22
+ "Topic :: Office/Business :: Financial",
23
+ "Intended Audience :: Developers",
24
+ "Intended Audience :: Financial and Insurance Industry",
25
+ ]
26
+ [project.urls]
27
+ Homepage = "https://github.com/chocochato0713/finclaw-data"
28
+ Repository = "https://github.com/chocochato0713/finclaw-data"
29
+
30
+ [build-system]
31
+ requires = ["setuptools>=68"]
32
+ build-backend = "setuptools.build_meta"
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["python"]
36
+ include = ["finclaw_data_sdk*"]
@@ -0,0 +1,7 @@
1
+ """A 股量化数据 HTTP SDK(不含拉取、存储等服务端业务)。"""
2
+
3
+ from . import cols
4
+ from .client import QuantClient, QuantClientError
5
+
6
+ __version__ = "0.1.3"
7
+ __all__ = ["QuantClient", "QuantClientError", "cols", "__version__"]
@@ -0,0 +1,267 @@
1
+ """量化数据 HTTP 客户端。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from datetime import date
7
+ from typing import Any
8
+ from urllib.error import HTTPError, URLError
9
+ from urllib.parse import urlencode
10
+ from urllib.request import Request, urlopen
11
+
12
+ import pandas as pd
13
+
14
+
15
+ def _to_ymd(value: str | date | None) -> str | None:
16
+ if value is None:
17
+ return None
18
+ if isinstance(value, date):
19
+ return value.strftime("%Y%m%d")
20
+ return str(value).replace("-", "")[:8]
21
+
22
+
23
+ def _join_symbols(symbols: list[str] | str | None, symbol: str | None = None) -> str | None:
24
+ codes = symbols if symbols is not None else symbol
25
+ if codes is None:
26
+ return None
27
+ if isinstance(codes, list):
28
+ return ",".join(str(s).zfill(6) for s in codes)
29
+ return str(codes)
30
+
31
+
32
+ class QuantClientError(RuntimeError):
33
+ pass
34
+
35
+
36
+ class QuantClient:
37
+ """远程 HTTP 客户端。依赖仅 pandas,不导入服务端业务代码。"""
38
+
39
+ def __init__(self, base_url: str = "http://127.0.0.1:8000", timeout: float = 1800.0) -> None:
40
+ self.base_url = base_url.rstrip("/")
41
+ self.timeout = timeout
42
+
43
+ def _get(self, path: str, **params: Any) -> dict[str, Any]:
44
+ query = {k: v for k, v in params.items() if v is not None and v != ""}
45
+ url = f"{self.base_url}{path}"
46
+ if query:
47
+ url = f"{url}?{urlencode(query)}"
48
+ req = Request(url, headers={"Accept": "application/json"})
49
+ try:
50
+ with urlopen(req, timeout=self.timeout) as resp:
51
+ return json.loads(resp.read().decode("utf-8"))
52
+ except HTTPError as exc:
53
+ body = exc.read().decode("utf-8", errors="replace")
54
+ raise QuantClientError(f"HTTP {exc.code}: {body}") from exc
55
+ except URLError as exc:
56
+ raise QuantClientError(f"request failed: {exc}") from exc
57
+
58
+ @staticmethod
59
+ def _to_frame(body: dict[str, Any]) -> pd.DataFrame:
60
+ rows = body.get("rows") or []
61
+ return pd.DataFrame(rows)
62
+
63
+ def catalog(self) -> dict[str, Any]:
64
+ """库存目录:各表行数、标的数、日期覆盖。"""
65
+ return self._get("/v1/catalog")
66
+
67
+ def catalog_preview(
68
+ self,
69
+ dataset: str,
70
+ symbol: str | None = None,
71
+ index: str | None = None,
72
+ start: str | date | None = None,
73
+ end: str | date | None = None,
74
+ limit: int = 50,
75
+ offset: int = 0,
76
+ ) -> pd.DataFrame:
77
+ """数据集分页预览,不走详情查询接口。"""
78
+ return self._to_frame(
79
+ self._get(
80
+ f"/v1/catalog/{dataset}/preview",
81
+ symbol=symbol,
82
+ index=index,
83
+ start=_to_ymd(start),
84
+ end=_to_ymd(end),
85
+ limit=limit,
86
+ offset=offset,
87
+ )
88
+ )
89
+
90
+ def stocks(
91
+ self,
92
+ listed: int | None = None,
93
+ symbol: str | None = None,
94
+ fields: str | None = None,
95
+ limit: int | None = None,
96
+ ) -> pd.DataFrame:
97
+ return self._to_frame(
98
+ self._get("/v1/stocks", listed=listed, symbol=symbol, fields=fields, limit=limit)
99
+ )
100
+
101
+ def calendar(
102
+ self,
103
+ start: str | date | None = None,
104
+ end: str | date | None = None,
105
+ trading_only: int = 1,
106
+ fields: str | None = None,
107
+ limit: int | None = None,
108
+ ) -> pd.DataFrame:
109
+ return self._to_frame(
110
+ self._get(
111
+ "/v1/calendar",
112
+ start=_to_ymd(start),
113
+ end=_to_ymd(end),
114
+ trading_only=trading_only,
115
+ fields=fields,
116
+ limit=limit,
117
+ )
118
+ )
119
+
120
+ def kline(
121
+ self,
122
+ symbol: str | None = None,
123
+ symbols: list[str] | str | None = None,
124
+ start: str | date | None = None,
125
+ end: str | date | None = None,
126
+ adj: str = "none",
127
+ fields: str | None = None,
128
+ limit: int | None = None,
129
+ ) -> pd.DataFrame:
130
+ """时序日 K:单股或多股 + 日期区间。adj=none|qfq|hfq。不传 limit 则不限制行数。"""
131
+ params: dict[str, Any] = {
132
+ "start": _to_ymd(start),
133
+ "end": _to_ymd(end),
134
+ "adj": adj,
135
+ "fields": fields,
136
+ }
137
+ if limit is not None:
138
+ params["limit"] = limit
139
+ if symbols is not None:
140
+ params["symbols"] = _join_symbols(symbols)
141
+ else:
142
+ params["symbol"] = symbol
143
+ return self._to_frame(self._get("/v1/kline", **params))
144
+
145
+ def snapshot(
146
+ self,
147
+ trade_date: str | date,
148
+ symbols: list[str] | str | None = None,
149
+ adj: str = "none",
150
+ fields: str | None = None,
151
+ limit: int | None = None,
152
+ ) -> pd.DataFrame:
153
+ """单日截面:不传 symbols 则全市场当日。"""
154
+ return self._to_frame(
155
+ self._get(
156
+ "/v1/snapshot",
157
+ trade_date=_to_ymd(trade_date),
158
+ symbols=_join_symbols(symbols),
159
+ adj=adj,
160
+ fields=fields,
161
+ limit=limit,
162
+ )
163
+ )
164
+
165
+ def fina(
166
+ self,
167
+ symbol: str | None = None,
168
+ symbols: list[str] | str | None = None,
169
+ start: str | date | None = None,
170
+ end: str | date | None = None,
171
+ fields: str | None = None,
172
+ limit: int | None = None,
173
+ ) -> pd.DataFrame:
174
+ return self._to_frame(
175
+ self._get(
176
+ "/v1/fina",
177
+ symbol=symbol if symbols is None else None,
178
+ symbols=_join_symbols(symbols),
179
+ start=_to_ymd(start),
180
+ end=_to_ymd(end),
181
+ fields=fields,
182
+ limit=limit,
183
+ )
184
+ )
185
+
186
+ def adj_factor(
187
+ self,
188
+ symbol: str | None = None,
189
+ symbols: list[str] | str | None = None,
190
+ start: str | date | None = None,
191
+ end: str | date | None = None,
192
+ fields: str | None = None,
193
+ limit: int | None = None,
194
+ ) -> pd.DataFrame:
195
+ """除权/复权因子(ex_date, fore/back/adjust_factor)。"""
196
+ return self._to_frame(
197
+ self._get(
198
+ "/v1/adj-factor",
199
+ symbol=symbol if symbols is None else None,
200
+ symbols=_join_symbols(symbols),
201
+ start=_to_ymd(start),
202
+ end=_to_ymd(end),
203
+ fields=fields,
204
+ limit=limit,
205
+ )
206
+ )
207
+
208
+ def index_list(
209
+ self,
210
+ index: str | None = None,
211
+ fields: str | None = None,
212
+ limit: int | None = None,
213
+ ) -> pd.DataFrame:
214
+ """指数目录(index_meta)。"""
215
+ return self._to_frame(
216
+ self._get("/v1/index", index=index, fields=fields, limit=limit)
217
+ )
218
+
219
+ def index_kline(
220
+ self,
221
+ index: str | None = None,
222
+ indices: list[str] | str | None = None,
223
+ start: str | date | None = None,
224
+ end: str | date | None = None,
225
+ fields: str | None = None,
226
+ limit: int | None = None,
227
+ ) -> pd.DataFrame:
228
+ """指数日 K 时序。不传 limit 则不限制行数。"""
229
+ params: dict[str, Any] = {
230
+ "start": _to_ymd(start),
231
+ "end": _to_ymd(end),
232
+ "fields": fields,
233
+ }
234
+ if limit is not None:
235
+ params["limit"] = limit
236
+ if indices is not None:
237
+ params["indices"] = _join_symbols(indices)
238
+ else:
239
+ params["index"] = index
240
+ return self._to_frame(self._get("/v1/index/kline", **params))
241
+
242
+ def index_constituents(
243
+ self,
244
+ index: str,
245
+ date: str | date | None = None,
246
+ fields: str | None = None,
247
+ limit: int | None = None,
248
+ ) -> pd.DataFrame:
249
+ """指数成分股(asof 快照)。"""
250
+ return self._to_frame(
251
+ self._get(
252
+ "/v1/index/constituents",
253
+ index=index,
254
+ date=_to_ymd(date),
255
+ fields=fields,
256
+ limit=limit,
257
+ )
258
+ )
259
+
260
+ def indices(
261
+ self,
262
+ index: str | None = None,
263
+ fields: str | None = None,
264
+ limit: int | None = None,
265
+ ) -> pd.DataFrame:
266
+ """指数目录(`index_list` 别名)。"""
267
+ return self.index_list(index=index, fields=fields, limit=limit)
@@ -0,0 +1,116 @@
1
+ """各 `QuantClient` 方法返回的列名。
2
+
3
+ 用法::
4
+
5
+ from finclaw_data_sdk import QuantClient, cols
6
+
7
+ c.kline(..., fields=cols.kline.CLOSE)
8
+ c.fina(..., fields=cols.fina.EPS_BASIC)
9
+ c.index_kline(..., fields=cols.index_kline.CLOSE)
10
+ c.index_constituents(..., fields=cols.index_constituents.SYMBOL)
11
+ """
12
+
13
+
14
+ class stocks:
15
+ """`stocks()` 股池。"""
16
+
17
+ SYMBOL = "symbol" # 股票代码
18
+ EXCHANGE = "exchange" # 交易所
19
+ NAME = "name" # 简称
20
+ CODE = "code" # Baostock 代码,如 sh.600519
21
+ IPO_DATE = "ipoDate" # 上市日
22
+ OUT_DATE = "outDate" # 退市日,在市为 None
23
+ TYPE = "type" # 证券类型,1=股票
24
+ STATUS = "status" # 上市状态,1=在市 0=退市
25
+
26
+
27
+ class calendar:
28
+ """`calendar()` 交易日历。"""
29
+
30
+ TRADE_DATE = "trade_date" # 日历日
31
+ IS_TRADING = "is_trading" # 1=交易日 0=非交易日
32
+
33
+
34
+ class kline:
35
+ """`kline()` 日 K 时序。`snapshot()` 列相同。"""
36
+
37
+ SYMBOL = "symbol" # 股票代码
38
+ DATE = "date" # 交易日
39
+ OPEN = "open" # 开盘价
40
+ HIGH = "high" # 最高价
41
+ LOW = "low" # 最低价
42
+ CLOSE = "close" # 收盘价
43
+ PRECLOSE = "preclose" # 昨收
44
+ VOLUME = "volume" # 成交量
45
+ AMOUNT = "amount" # 成交额
46
+ TURN = "turn" # 换手率(表字段 turnover)
47
+ PCT_CHG = "pct_chg" # 涨跌幅
48
+ PE_TTM = "pe_ttm" # 市盈率 TTM
49
+ PB_MRQ = "pb_mrq" # 市净率 MRQ
50
+ PS_TTM = "ps_ttm" # 市销率 TTM
51
+ PCF_NCF_TTM = "pcf_ncf_ttm" # 市现率 TTM
52
+ TRADE_STATUS = "trade_status" # 交易状态,1=正常
53
+ IS_ST = "is_st" # 是否 ST
54
+ ADJ_TYPE = "adj_type" # 复权类型 none|qfq|hfq
55
+
56
+
57
+ class snapshot(kline):
58
+ """`snapshot()` 单日截面,列与 `kline` 相同。"""
59
+
60
+
61
+ class fina:
62
+ """`fina()` 报告期累计财务指标。另有维度列 symbol / exchange / report_date / notice_date / season_label。"""
63
+
64
+ NOTICE_DATE = "notice_date" # 公告日;回测应用 notice_date ≤ 交易日
65
+ EPS_BASIC = "eps_basic" # 基本每股收益
66
+ BPS = "bps" # 每股净资产
67
+ ROE_DILUTED = "roe_diluted" # 加权净资产收益率(东财 ROEJQ;列名沿用)
68
+ ROA = "roa" # 总资产净利率
69
+ GROSS_PROFIT_RATIO = "gross_profit_ratio" # 销售毛利率
70
+ NET_PROFIT_RATIO = "net_profit_ratio" # 销售净利率
71
+ TOTAL_OPERATE_REVENUE = "total_operate_revenue" # 营业总收入
72
+ PARENT_NET_PROFIT = "parent_net_profit" # 归母净利润
73
+ DEDU_PARENT_PROFIT = "dedu_parent_profit" # 扣非归母净利润
74
+ REVENUE_YOY = "revenue_yoy" # 营业总收入同比
75
+ PARENT_NET_PROFIT_YOY = "parent_net_profit_yoy" # 归母净利润同比
76
+ REVENUE_QOQ = "revenue_qoq" # 营业总收入环比
77
+ NET_PROFIT_QOQ = "net_profit_qoq" # 净利润环比
78
+
79
+
80
+ class adj_factor:
81
+ """`adj_factor()` 除权 / 复权因子。"""
82
+
83
+ SYMBOL = "symbol" # 股票代码
84
+ EXCHANGE = "exchange" # 交易所
85
+ EX_DATE = "ex_date" # 除权除息日
86
+ FORE_ADJUST_FACTOR = "fore_adjust_factor" # 前复权因子
87
+ BACK_ADJUST_FACTOR = "back_adjust_factor" # 后复权因子
88
+ ADJUST_FACTOR = "adjust_factor" # Baostock 原始调整因子
89
+
90
+
91
+ class index_kline:
92
+ """`index_kline()` 指数日 K。无估值列、无复权。"""
93
+
94
+ INDEX_CODE = "index_code" # 指数代码
95
+ EXCHANGE = "exchange" # 交易所
96
+ DATE = "date" # 交易日
97
+ OPEN = "open" # 开盘
98
+ HIGH = "high" # 最高
99
+ LOW = "low" # 最低
100
+ CLOSE = "close" # 收盘
101
+ PRECLOSE = "preclose" # 昨收
102
+ VOLUME = "volume" # 成交量
103
+ AMOUNT = "amount" # 成交额
104
+ TURN = "turn" # 换手率(表字段 turnover)
105
+ PCT_CHG = "pct_chg" # 涨跌幅
106
+
107
+
108
+ class index_constituents:
109
+ """`index_constituents()` 指数成分股 asof 快照。不含权重。"""
110
+
111
+ INDEX_CODE = "index_code" # 指数代码
112
+ SNAPSHOT_DATE = "snapshot_date" # 成分快照日
113
+ SYMBOL = "symbol" # 成分股代码
114
+ EXCHANGE = "exchange" # 交易所
115
+ NAME = "name" # 成分股简称
116
+ SOURCE = "source" # baostock 或 csindex
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: finclaw-data-sdk
3
+ Version: 0.1.3
4
+ Summary: A 股量化数据 HTTP 客户端(QuantClient),只依赖 pandas
5
+ Author-email: dekeky <dekeky@163.com>
6
+ Project-URL: Homepage, https://github.com/chocochato0713/finclaw-data
7
+ Project-URL: Repository, https://github.com/chocochato0713/finclaw-data
8
+ Keywords: finclaw,ashare,quant,kline,clickhouse
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Topic :: Office/Business :: Financial
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: Financial and Insurance Industry
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: pandas>=2.0
22
+
23
+ # finclaw-data-sdk
24
+
25
+ A 股量化数据 HTTP 客户端。安装后 `from finclaw_data_sdk import QuantClient`,只依赖 pandas,不含拉取与 ClickHouse 业务代码。
26
+
27
+ ```bash
28
+ pip install finclaw-data-sdk
29
+ ```
30
+
31
+ ```python
32
+ from finclaw_data_sdk import QuantClient, cols
33
+
34
+ c = QuantClient("http://127.0.0.1:8000")
35
+ bars = c.kline(symbol="600519", start="20240801", end="20240805", adj="qfq")
36
+ snap = c.snapshot(trade_date="20240819", symbols=["600519", "000001"], adj="qfq")
37
+ df = c.fina(symbol="600519", start="20240101", end="20241231", fields=cols.fina.EPS_BASIC)
38
+ idx = c.index_list()
39
+ idx_bars = c.index_kline(index="000300", start="20240801", end="20240805", fields=cols.index_kline.CLOSE)
40
+ cons = c.index_constituents(index="000300", fields=cols.index_constituents.SYMBOL)
41
+ ```
42
+
43
+ 日期参数为 `YYYYMMDD`,区间两端包含。需先启动本仓库的 `finclaw-data serve`。直连 ClickHouse 请安装服务包 `finclaw-data`,使用 `QuantClient.from_clickhouse()`。
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ python/finclaw_data_sdk/__init__.py
4
+ python/finclaw_data_sdk/client.py
5
+ python/finclaw_data_sdk/cols.py
6
+ python/finclaw_data_sdk.egg-info/PKG-INFO
7
+ python/finclaw_data_sdk.egg-info/SOURCES.txt
8
+ python/finclaw_data_sdk.egg-info/dependency_links.txt
9
+ python/finclaw_data_sdk.egg-info/requires.txt
10
+ python/finclaw_data_sdk.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+