jcdata 0.2.0__tar.gz → 0.2.1__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.
- jcdata-0.2.1/PKG-INFO +198 -0
- jcdata-0.2.1/README.md +168 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/__init__.py +12 -4
- {jcdata-0.2.0 → jcdata-0.2.1}/_http.py +41 -25
- jcdata-0.2.1/config.py +23 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/data.py +158 -47
- jcdata-0.2.1/jcdata.egg-info/PKG-INFO +198 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/pyproject.toml +1 -1
- jcdata-0.2.0/PKG-INFO +0 -115
- jcdata-0.2.0/README.md +0 -85
- jcdata-0.2.0/config.py +0 -29
- jcdata-0.2.0/jcdata.egg-info/PKG-INFO +0 -115
- {jcdata-0.2.0 → jcdata-0.2.1}/_arrow.py +0 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/_params.py +0 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/_ts.py +0 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/errors.py +0 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/jcdata.egg-info/SOURCES.txt +0 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/jcdata.egg-info/dependency_links.txt +0 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/jcdata.egg-info/requires.txt +0 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/jcdata.egg-info/top_level.txt +0 -0
- {jcdata-0.2.0 → jcdata-0.2.1}/setup.cfg +0 -0
jcdata-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jcdata
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Python client for JCDATA market data and factor APIs
|
|
5
|
+
Author: JiceQuant
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Project-URL: Homepage, https://jicequant.com/
|
|
8
|
+
Project-URL: Documentation, https://jicequant.com/
|
|
9
|
+
Project-URL: Repository, https://pypi.org/project/jcdata/
|
|
10
|
+
Keywords: finance,quant,market-data,china,a-share
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: httpx>=0.24
|
|
26
|
+
Requires-Dist: pandas>=1.3
|
|
27
|
+
Requires-Dist: pyarrow>=14
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
30
|
+
|
|
31
|
+
# jcdata
|
|
32
|
+
|
|
33
|
+
`jcdata` 是 JCDATA 的 Python 客户端,用于获取市场数据、证券基础信息、因子、财务、ETF、期权、利率和宏观数据
|
|
34
|
+
|
|
35
|
+
## 安装
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install jcdata
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 认证
|
|
42
|
+
|
|
43
|
+
使用访问令牌登录。请将令牌保存在环境变量或密钥管理系统中,禁止写入源码、笔记本、日志或版本控制系统。
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import jcdata
|
|
47
|
+
|
|
48
|
+
login_info = jcdata.login("YOUR_TOKEN")
|
|
49
|
+
if not login_info.get("user_name"):
|
|
50
|
+
raise PermissionError("令牌无效、已过期或无访问权限")
|
|
51
|
+
|
|
52
|
+
print(login_info["user_name"])
|
|
53
|
+
print(login_info["cell_quota_remaining"])
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
也可使用环境变量:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Linux / macOS
|
|
60
|
+
export JCDATA_TOKEN="YOUR_TOKEN"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```powershell
|
|
64
|
+
# Windows PowerShell
|
|
65
|
+
$env:JCDATA_TOKEN = "YOUR_TOKEN"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
jcdata.login()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
令牌查找顺序为:`login(token=...)` 参数、`JCDATA_TOKEN` 环境变量、客户端配置。`login()` 返回用户名、接口使用量和剩余配额;认证未通过时这些字段为 `None`。
|
|
73
|
+
|
|
74
|
+
## 基本使用
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import jcdata
|
|
78
|
+
|
|
79
|
+
jcdata.login()
|
|
80
|
+
|
|
81
|
+
bars = jcdata.get_market_data(
|
|
82
|
+
"600000.SH",
|
|
83
|
+
"2025-01-02",
|
|
84
|
+
"2025-01-10",
|
|
85
|
+
fields=["date", "open", "high", "low", "close", "volume"],
|
|
86
|
+
)
|
|
87
|
+
frame = bars["600000.SH"]
|
|
88
|
+
|
|
89
|
+
if not frame.empty:
|
|
90
|
+
print(frame.tail())
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
多数按证券代码查询的接口返回 `dict[str, pandas.DataFrame]`:字典 key 为请求代码,DataFrame 中不依赖 `symbol` 列分组。没有匹配数据时也会保留对应的空 DataFrame,因此请显式处理 `frame.empty`。
|
|
94
|
+
|
|
95
|
+
日期参数支持 `YYYY-MM-DD` 或 `YYYYMMDD`,例如 `2025-01-02` 或 `20250102`。
|
|
96
|
+
|
|
97
|
+
## 常用接口
|
|
98
|
+
|
|
99
|
+
| 分类 | 接口 |
|
|
100
|
+
|---|---|
|
|
101
|
+
| 日 K | `get_market_data`、`iter_market_data`、`iter_market_data_batches` |
|
|
102
|
+
| 基础信息 | `get_instrument`、`get_adj_factor`、`get_settlement`、`get_future_contract` |
|
|
103
|
+
| 股票扩展 | `get_factors`、`get_valuation`、`get_market_value`、`get_finance`、`get_money_flow`、`get_margin`、`get_leader_board` |
|
|
104
|
+
| 期权 | `get_option_bars`、`get_option_greeks` |
|
|
105
|
+
| ETF | `get_etf_share`、`get_etf_tracking` |
|
|
106
|
+
| 日历与板块 | `get_trade_dates`、`get_all_sectors`、`get_stock_list_by_sector` |
|
|
107
|
+
| 利率与宏观 | `get_interest_rate`、`get_government_yield`、`get_macro_indicators` |
|
|
108
|
+
|
|
109
|
+
## 批量行情
|
|
110
|
+
|
|
111
|
+
直接传入代码列表即可查询多标的行情:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
symbols = ["600000.SH", "000001.SZ", "510300.SH"]
|
|
115
|
+
bars = jcdata.get_market_data(symbols, "2025-01-02", "2025-01-10", fields=["date", "close"])
|
|
116
|
+
|
|
117
|
+
for symbol, frame in bars.items():
|
|
118
|
+
print(symbol, len(frame))
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
混合查询不同资产时,各 DataFrame 只返回该资产适用的字段。请按单个 DataFrame 的实际列名处理,不要假设所有资产具有相同字段。
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
mixed = jcdata.get_market_data(["600000.SH", "IF2501.CFE"], "2025-01-02", "2025-01-10")
|
|
125
|
+
print(mixed["600000.SH"].columns.tolist())
|
|
126
|
+
print(mixed["IF2501.CFE"].columns.tolist())
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 复权
|
|
130
|
+
|
|
131
|
+
`get_market_data()` 支持股票、ETF 和可转债复权:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
adjusted = jcdata.get_market_data(
|
|
135
|
+
"600000.SH",
|
|
136
|
+
"2024-01-01",
|
|
137
|
+
"2025-01-31",
|
|
138
|
+
dividend_type="front",
|
|
139
|
+
fields=["date", "close"],
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
可用 `dividend_type`:`none`、`front`、`back`、`point`。使用 `point` 时必须同时传 `adjust_date`。指数不参与复权。
|
|
144
|
+
|
|
145
|
+
## 大数据读取
|
|
146
|
+
|
|
147
|
+
代码数量较多但希望按代码处理时,使用 `iter_market_data()`:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
for chunk_symbols, frames in jcdata.iter_market_data(
|
|
151
|
+
symbols,
|
|
152
|
+
"2020-01-01",
|
|
153
|
+
"2025-01-01",
|
|
154
|
+
chunk_size=60,
|
|
155
|
+
fields=["date", "close", "volume"],
|
|
156
|
+
):
|
|
157
|
+
for symbol in chunk_symbols:
|
|
158
|
+
process(frames[symbol])
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
超长历史数据使用 `iter_market_data_batches()`,并在循环内及时处理结果:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
for present_symbols, frames in jcdata.iter_market_data_batches(
|
|
165
|
+
symbols,
|
|
166
|
+
"2015-01-01",
|
|
167
|
+
"2025-01-01",
|
|
168
|
+
fields=["date", "close"],
|
|
169
|
+
stream_batch_rows=65_536,
|
|
170
|
+
):
|
|
171
|
+
for symbol in present_symbols:
|
|
172
|
+
frames[symbol].to_parquet(f"{symbol}.parquet", index=False)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
同一代码可能跨多个批次出现。请不要将全部批次累积到内存中。
|
|
176
|
+
|
|
177
|
+
## 错误处理
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
try:
|
|
181
|
+
jcdata.login()
|
|
182
|
+
bars = jcdata.get_market_data("600000.SH", "2025-01-02", "2025-01-10")
|
|
183
|
+
except jcdata.CellQuotaExceededError as exc:
|
|
184
|
+
print("配额已用尽,剩余:", exc.cell_quota_remaining)
|
|
185
|
+
except jcdata.InvalidParameterError as exc:
|
|
186
|
+
print("参数错误:", exc.param, exc.value)
|
|
187
|
+
except PermissionError as exc:
|
|
188
|
+
print("认证或授权失败:", exc)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`CellQuotaExceededError` 是 `PermissionError` 的子类,因此应优先捕获。参数问题会引发 `InvalidParameterError`,可通过 `.param` 和 `.value` 定位。对于无效代码或正常无数据区间,按代码查询接口一般返回空 DataFrame,而非异常。
|
|
192
|
+
|
|
193
|
+
## 更多说明
|
|
194
|
+
|
|
195
|
+
- 代码格式通常为 `代码.市场后缀`,例如 `600000.SH`、`000001.SZ`、`IF2501.CFE`。
|
|
196
|
+
- 使用 `get_asset_type_by_symbol()` 可识别常见代码的资产类型。
|
|
197
|
+
- 使用 `fields` 限定实际需要的列,可减少传输量和处理成本。
|
|
198
|
+
- 有访问频率或配额限制时,请避免无上限并发和紧密重试;应控制并发、缩小查询范围并保存任务进度。
|
jcdata-0.2.1/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# jcdata
|
|
2
|
+
|
|
3
|
+
`jcdata` 是 JCDATA 的 Python 客户端,用于获取市场数据、证券基础信息、因子、财务、ETF、期权、利率和宏观数据
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install jcdata
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 认证
|
|
12
|
+
|
|
13
|
+
使用访问令牌登录。请将令牌保存在环境变量或密钥管理系统中,禁止写入源码、笔记本、日志或版本控制系统。
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import jcdata
|
|
17
|
+
|
|
18
|
+
login_info = jcdata.login("YOUR_TOKEN")
|
|
19
|
+
if not login_info.get("user_name"):
|
|
20
|
+
raise PermissionError("令牌无效、已过期或无访问权限")
|
|
21
|
+
|
|
22
|
+
print(login_info["user_name"])
|
|
23
|
+
print(login_info["cell_quota_remaining"])
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
也可使用环境变量:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Linux / macOS
|
|
30
|
+
export JCDATA_TOKEN="YOUR_TOKEN"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
# Windows PowerShell
|
|
35
|
+
$env:JCDATA_TOKEN = "YOUR_TOKEN"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
jcdata.login()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
令牌查找顺序为:`login(token=...)` 参数、`JCDATA_TOKEN` 环境变量、客户端配置。`login()` 返回用户名、接口使用量和剩余配额;认证未通过时这些字段为 `None`。
|
|
43
|
+
|
|
44
|
+
## 基本使用
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import jcdata
|
|
48
|
+
|
|
49
|
+
jcdata.login()
|
|
50
|
+
|
|
51
|
+
bars = jcdata.get_market_data(
|
|
52
|
+
"600000.SH",
|
|
53
|
+
"2025-01-02",
|
|
54
|
+
"2025-01-10",
|
|
55
|
+
fields=["date", "open", "high", "low", "close", "volume"],
|
|
56
|
+
)
|
|
57
|
+
frame = bars["600000.SH"]
|
|
58
|
+
|
|
59
|
+
if not frame.empty:
|
|
60
|
+
print(frame.tail())
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
多数按证券代码查询的接口返回 `dict[str, pandas.DataFrame]`:字典 key 为请求代码,DataFrame 中不依赖 `symbol` 列分组。没有匹配数据时也会保留对应的空 DataFrame,因此请显式处理 `frame.empty`。
|
|
64
|
+
|
|
65
|
+
日期参数支持 `YYYY-MM-DD` 或 `YYYYMMDD`,例如 `2025-01-02` 或 `20250102`。
|
|
66
|
+
|
|
67
|
+
## 常用接口
|
|
68
|
+
|
|
69
|
+
| 分类 | 接口 |
|
|
70
|
+
|---|---|
|
|
71
|
+
| 日 K | `get_market_data`、`iter_market_data`、`iter_market_data_batches` |
|
|
72
|
+
| 基础信息 | `get_instrument`、`get_adj_factor`、`get_settlement`、`get_future_contract` |
|
|
73
|
+
| 股票扩展 | `get_factors`、`get_valuation`、`get_market_value`、`get_finance`、`get_money_flow`、`get_margin`、`get_leader_board` |
|
|
74
|
+
| 期权 | `get_option_bars`、`get_option_greeks` |
|
|
75
|
+
| ETF | `get_etf_share`、`get_etf_tracking` |
|
|
76
|
+
| 日历与板块 | `get_trade_dates`、`get_all_sectors`、`get_stock_list_by_sector` |
|
|
77
|
+
| 利率与宏观 | `get_interest_rate`、`get_government_yield`、`get_macro_indicators` |
|
|
78
|
+
|
|
79
|
+
## 批量行情
|
|
80
|
+
|
|
81
|
+
直接传入代码列表即可查询多标的行情:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
symbols = ["600000.SH", "000001.SZ", "510300.SH"]
|
|
85
|
+
bars = jcdata.get_market_data(symbols, "2025-01-02", "2025-01-10", fields=["date", "close"])
|
|
86
|
+
|
|
87
|
+
for symbol, frame in bars.items():
|
|
88
|
+
print(symbol, len(frame))
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
混合查询不同资产时,各 DataFrame 只返回该资产适用的字段。请按单个 DataFrame 的实际列名处理,不要假设所有资产具有相同字段。
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
mixed = jcdata.get_market_data(["600000.SH", "IF2501.CFE"], "2025-01-02", "2025-01-10")
|
|
95
|
+
print(mixed["600000.SH"].columns.tolist())
|
|
96
|
+
print(mixed["IF2501.CFE"].columns.tolist())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 复权
|
|
100
|
+
|
|
101
|
+
`get_market_data()` 支持股票、ETF 和可转债复权:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
adjusted = jcdata.get_market_data(
|
|
105
|
+
"600000.SH",
|
|
106
|
+
"2024-01-01",
|
|
107
|
+
"2025-01-31",
|
|
108
|
+
dividend_type="front",
|
|
109
|
+
fields=["date", "close"],
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
可用 `dividend_type`:`none`、`front`、`back`、`point`。使用 `point` 时必须同时传 `adjust_date`。指数不参与复权。
|
|
114
|
+
|
|
115
|
+
## 大数据读取
|
|
116
|
+
|
|
117
|
+
代码数量较多但希望按代码处理时,使用 `iter_market_data()`:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
for chunk_symbols, frames in jcdata.iter_market_data(
|
|
121
|
+
symbols,
|
|
122
|
+
"2020-01-01",
|
|
123
|
+
"2025-01-01",
|
|
124
|
+
chunk_size=60,
|
|
125
|
+
fields=["date", "close", "volume"],
|
|
126
|
+
):
|
|
127
|
+
for symbol in chunk_symbols:
|
|
128
|
+
process(frames[symbol])
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
超长历史数据使用 `iter_market_data_batches()`,并在循环内及时处理结果:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
for present_symbols, frames in jcdata.iter_market_data_batches(
|
|
135
|
+
symbols,
|
|
136
|
+
"2015-01-01",
|
|
137
|
+
"2025-01-01",
|
|
138
|
+
fields=["date", "close"],
|
|
139
|
+
stream_batch_rows=65_536,
|
|
140
|
+
):
|
|
141
|
+
for symbol in present_symbols:
|
|
142
|
+
frames[symbol].to_parquet(f"{symbol}.parquet", index=False)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
同一代码可能跨多个批次出现。请不要将全部批次累积到内存中。
|
|
146
|
+
|
|
147
|
+
## 错误处理
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
try:
|
|
151
|
+
jcdata.login()
|
|
152
|
+
bars = jcdata.get_market_data("600000.SH", "2025-01-02", "2025-01-10")
|
|
153
|
+
except jcdata.CellQuotaExceededError as exc:
|
|
154
|
+
print("配额已用尽,剩余:", exc.cell_quota_remaining)
|
|
155
|
+
except jcdata.InvalidParameterError as exc:
|
|
156
|
+
print("参数错误:", exc.param, exc.value)
|
|
157
|
+
except PermissionError as exc:
|
|
158
|
+
print("认证或授权失败:", exc)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`CellQuotaExceededError` 是 `PermissionError` 的子类,因此应优先捕获。参数问题会引发 `InvalidParameterError`,可通过 `.param` 和 `.value` 定位。对于无效代码或正常无数据区间,按代码查询接口一般返回空 DataFrame,而非异常。
|
|
162
|
+
|
|
163
|
+
## 更多说明
|
|
164
|
+
|
|
165
|
+
- 代码格式通常为 `代码.市场后缀`,例如 `600000.SH`、`000001.SZ`、`IF2501.CFE`。
|
|
166
|
+
- 使用 `get_asset_type_by_symbol()` 可识别常见代码的资产类型。
|
|
167
|
+
- 使用 `fields` 限定实际需要的列,可减少传输量和处理成本。
|
|
168
|
+
- 有访问频率或配额限制时,请避免无上限并发和紧密重试;应控制并发、缩小查询范围并保存任务进度。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""
|
|
3
|
-
JCDATA
|
|
3
|
+
JCDATA Python 客户端:用于查询市场数据和相关数据集。
|
|
4
4
|
|
|
5
5
|
使用前调用 ``jcdata.login("...")``(或设置环境变量 ``JCDATA_TOKEN``)::
|
|
6
6
|
|
|
@@ -15,10 +15,13 @@ from ._http import current_user, login, whoami
|
|
|
15
15
|
from .errors import CellQuotaExceededError, ErrorCode, InvalidParameterError, RowQuotaExceededError
|
|
16
16
|
from .data import (
|
|
17
17
|
get_adj_factor,
|
|
18
|
+
get_asset_type_by_symbol,
|
|
18
19
|
get_etf_share,
|
|
19
20
|
get_etf_tracking,
|
|
20
21
|
get_finance,
|
|
22
|
+
get_factors,
|
|
21
23
|
get_future_contract,
|
|
24
|
+
get_government_yield,
|
|
22
25
|
get_instrument,
|
|
23
26
|
get_interest_rate,
|
|
24
27
|
get_leader_board,
|
|
@@ -27,9 +30,10 @@ from .data import (
|
|
|
27
30
|
iter_market_data,
|
|
28
31
|
iter_market_data_batches,
|
|
29
32
|
get_market_value,
|
|
33
|
+
get_macro_indicators,
|
|
30
34
|
get_money_flow,
|
|
31
35
|
get_option_bars,
|
|
32
|
-
|
|
36
|
+
get_option_greeks,
|
|
33
37
|
get_settlement,
|
|
34
38
|
get_all_sectors,
|
|
35
39
|
get_stock_list_by_sector,
|
|
@@ -37,7 +41,7 @@ from .data import (
|
|
|
37
41
|
get_valuation,
|
|
38
42
|
)
|
|
39
43
|
|
|
40
|
-
__version__ = "0.1
|
|
44
|
+
__version__ = "0.2.1"
|
|
41
45
|
|
|
42
46
|
__all__ = [
|
|
43
47
|
"__version__",
|
|
@@ -49,6 +53,7 @@ __all__ = [
|
|
|
49
53
|
"InvalidParameterError",
|
|
50
54
|
"RowQuotaExceededError",
|
|
51
55
|
"get_market_data",
|
|
56
|
+
"get_asset_type_by_symbol",
|
|
52
57
|
"iter_market_data",
|
|
53
58
|
"iter_market_data_batches",
|
|
54
59
|
"get_instrument",
|
|
@@ -57,12 +62,15 @@ __all__ = [
|
|
|
57
62
|
"get_future_contract",
|
|
58
63
|
"get_valuation",
|
|
59
64
|
"get_market_value",
|
|
65
|
+
"get_government_yield",
|
|
66
|
+
"get_macro_indicators",
|
|
60
67
|
"get_finance",
|
|
68
|
+
"get_factors",
|
|
61
69
|
"get_trade_dates",
|
|
62
70
|
"get_all_sectors",
|
|
63
71
|
"get_stock_list_by_sector",
|
|
64
72
|
"get_option_bars",
|
|
65
|
-
"
|
|
73
|
+
"get_option_greeks",
|
|
66
74
|
"get_money_flow",
|
|
67
75
|
"get_margin",
|
|
68
76
|
"get_leader_board",
|
|
@@ -77,24 +77,28 @@ def _resolve_timeout(timeout_sec: float | None) -> float:
|
|
|
77
77
|
return timeout_sec if timeout_sec is not None else config.TIMEOUT_SEC
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
def _login_result(body: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
81
|
+
"""构造 login 的稳定返回结构。"""
|
|
82
|
+
body = body or {}
|
|
83
|
+
return {
|
|
84
|
+
"user_name": body.get("user_name"),
|
|
85
|
+
"api_usage_week": body.get("api_usage_week"),
|
|
86
|
+
"api_usage_month": body.get("api_usage_month"),
|
|
87
|
+
"cell_quota_remaining": body.get("cell_quota_remaining"),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
80
91
|
def login(
|
|
81
92
|
token: str | None = None,
|
|
82
93
|
*,
|
|
83
94
|
port: int | None = None,
|
|
84
95
|
timeout_sec: float | None = None,
|
|
85
|
-
) ->
|
|
96
|
+
) -> dict[str, Any]:
|
|
86
97
|
"""
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
参数:
|
|
90
|
-
token: 访问令牌,优先级:参数 > 环境变量 JCDATA_TOKEN > config.TOKEN
|
|
91
|
-
port: API 服务端口,优先级:参数 > 环境变量 JCDATA_PORT > config.PORT
|
|
92
|
-
timeout_sec: 请求超时时间(秒),默认使用 config.TIMEOUT_SEC
|
|
98
|
+
连接数据服务并校验访问令牌,返回当前用户和数据用量信息。
|
|
93
99
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
示例:
|
|
97
|
-
>>> jcdata.login("your_token")
|
|
100
|
+
返回值固定含有 ``user_name``、``api_usage_week``、``api_usage_month``、
|
|
101
|
+
``cell_quota_remaining`` 四个键。令牌未通过认证时,各值均为 ``None``。
|
|
98
102
|
"""
|
|
99
103
|
global _client, _user_name, _authenticated
|
|
100
104
|
|
|
@@ -102,21 +106,27 @@ def login(
|
|
|
102
106
|
key = _resolve_token(token)
|
|
103
107
|
resolved_port = _resolve_port(port)
|
|
104
108
|
to = _resolve_timeout(timeout_sec)
|
|
105
|
-
|
|
106
|
-
headers = {
|
|
107
|
-
"Authorization": f"Bearer {key}",
|
|
108
|
-
"X-Api-Key": key,
|
|
109
|
-
}
|
|
109
|
+
headers = {"Authorization": f"Bearer {key}", "X-Api-Key": key}
|
|
110
110
|
|
|
111
111
|
if _client is not None:
|
|
112
112
|
_client.close()
|
|
113
113
|
|
|
114
114
|
final_url = _build_base_url(url, resolved_port)
|
|
115
115
|
_client = httpx.Client(base_url=final_url, timeout=to, headers=headers)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
response = _client.get("/api/v1/auth/whoami")
|
|
117
|
+
if response.status_code in (401, 403):
|
|
118
|
+
_user_name = None
|
|
119
|
+
_authenticated = False
|
|
120
|
+
return _login_result()
|
|
121
|
+
response.raise_for_status()
|
|
122
|
+
body = response.json()
|
|
123
|
+
if not isinstance(body, dict) or not body.get("user_name"):
|
|
124
|
+
_user_name = None
|
|
125
|
+
_authenticated = False
|
|
126
|
+
return _login_result()
|
|
127
|
+
_user_name = str(body["user_name"])
|
|
128
|
+
_authenticated = True
|
|
129
|
+
return _login_result(body)
|
|
120
130
|
|
|
121
131
|
|
|
122
132
|
def whoami() -> str | None:
|
|
@@ -141,7 +151,8 @@ def _whoami_request() -> tuple[str | None, bool]:
|
|
|
141
151
|
raise PermissionError("令牌无效或已过期")
|
|
142
152
|
r.raise_for_status()
|
|
143
153
|
body = r.json()
|
|
144
|
-
|
|
154
|
+
user_name = body.get("user_name")
|
|
155
|
+
return user_name, bool(user_name)
|
|
145
156
|
|
|
146
157
|
|
|
147
158
|
def _client_or_raise() -> httpx.Client:
|
|
@@ -177,9 +188,14 @@ def _raise_for_detail(status_code: int, detail: dict | str | None) -> None:
|
|
|
177
188
|
param=detail.get("param"),
|
|
178
189
|
value=detail.get("value"),
|
|
179
190
|
)
|
|
180
|
-
if status_code
|
|
191
|
+
if status_code in (400, 422):
|
|
181
192
|
if isinstance(detail, dict):
|
|
182
|
-
raise InvalidParameterError(
|
|
193
|
+
raise InvalidParameterError(
|
|
194
|
+
str(detail.get("message") or "请求参数不合法"),
|
|
195
|
+
code=str(detail.get("code") or ErrorCode.INVALID_PARAMETER),
|
|
196
|
+
param=detail.get("param"),
|
|
197
|
+
value=detail.get("value"),
|
|
198
|
+
)
|
|
183
199
|
raise InvalidParameterError(str(detail) if detail else "请求参数不合法")
|
|
184
200
|
if status_code == 401:
|
|
185
201
|
raise PermissionError("未授权,请先 jcdata.login()")
|
|
@@ -208,7 +224,7 @@ def _retry_after_seconds(r: httpx.Response, detail: dict | str | None) -> float:
|
|
|
208
224
|
|
|
209
225
|
def _raise_http_error(r: httpx.Response) -> None:
|
|
210
226
|
detail = _parse_error_detail(r)
|
|
211
|
-
if r.status_code in (400, 401, 403, 429):
|
|
227
|
+
if r.status_code in (400, 401, 403, 422, 429):
|
|
212
228
|
_raise_for_detail(r.status_code, detail)
|
|
213
229
|
r.raise_for_status()
|
|
214
230
|
|
|
@@ -368,7 +384,7 @@ def _post_query(
|
|
|
368
384
|
|
|
369
385
|
|
|
370
386
|
def iter_stream_bars(params: dict[str, Any]) -> Iterator[dict[str, pd.DataFrame]]:
|
|
371
|
-
"""
|
|
387
|
+
"""逐批读取日 K 数据,不缓冲完整响应体。"""
|
|
372
388
|
from ._arrow import ARROW_ZSTD_MEDIA_TYPE, iter_zstd_arrow_by_symbol
|
|
373
389
|
|
|
374
390
|
client = _client_or_raise()
|
jcdata-0.2.1/config.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""jcdata 客户端默认配置。
|
|
3
|
+
|
|
4
|
+
生产环境请通过环境变量或 ``jcdata.login(token=...)`` 提供访问令牌。
|
|
5
|
+
不要将访问令牌写入源码、配置文件或版本控制系统。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
# 服务地址。一般无需修改;私有部署可按本单位要求覆盖。
|
|
11
|
+
BASE_URL = "https://api.jicequant.com"
|
|
12
|
+
PORT = None
|
|
13
|
+
|
|
14
|
+
# 单次请求超时(秒)。
|
|
15
|
+
TIMEOUT_SEC = 3600.0
|
|
16
|
+
|
|
17
|
+
# 不在发行包中保存令牌。此变量仅保留配置兼容性;优先使用
|
|
18
|
+
# login(token=...) 或环境变量 JCDATA_TOKEN。
|
|
19
|
+
TOKEN = ""
|
|
20
|
+
|
|
21
|
+
# 多代码查询的客户端行为配置。
|
|
22
|
+
USE_POST_BULK = True
|
|
23
|
+
POST_MIN_SYMBOLS = 2
|