quantdb-sdk 0.1.3__tar.gz → 0.1.4__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.
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/CHANGELOG.md +7 -0
- {quantdb_sdk-0.1.3/quantdb_sdk.egg-info → quantdb_sdk-0.1.4}/PKG-INFO +2 -2
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/README.md +1 -1
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/pyproject.toml +1 -1
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/async_client.py +12 -6
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/client.py +15 -10
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4/quantdb_sdk.egg-info}/PKG-INFO +2 -2
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/tests/test_async_client.py +5 -2
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/tests/test_client.py +6 -4
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/LICENSE +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/MANIFEST.in +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/__init__.py +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/__main__.py +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/_utils.py +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/errors.py +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/py.typed +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/SOURCES.txt +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/dependency_links.txt +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/entry_points.txt +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/requires.txt +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/top_level.txt +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/setup.cfg +0 -0
- {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/tests/test_technical_indicators.py +0 -0
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
所有 notable 变更都会记录在此文件。格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
4
4
|
版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
|
5
5
|
|
|
6
|
+
## [0.1.4] - 2026-07-25
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- **用户名密码登录**:先初始化 HTTP 会话再登录,修复 `QuantDBClient(username=..., password=...)` 初始化时访问未创建会话的错误。
|
|
10
|
+
- **API Key 删除**:`delete_api_key()` / `a_delete_api_key()` 改为调用服务端实际的 `DELETE /api/v1/auth/api-keys/{id}` 接口,并明确接受 API Key ID。
|
|
11
|
+
- **流量用量**:`get_usage()` / `a_get_usage()` 将余额购买的额外流量纳入剩余流量和活跃状态计算,并新增 `purchased_gb` 返回字段。
|
|
12
|
+
|
|
6
13
|
## [0.1.3] - 2026-07-25
|
|
7
14
|
|
|
8
15
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantdb-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: QuantDB 量化数据平台官方 Python SDK
|
|
5
5
|
Author: QuantDB Team
|
|
6
6
|
License: MIT
|
|
@@ -90,7 +90,7 @@ client = QuantDBClient(username="admin", password="admin123")
|
|
|
90
90
|
|
|
91
91
|
## 流量说明
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
免费注册用户获赠 100 MB 一次性体验流量;订阅用户每月含 30 GB 下载流量,超出部分按 ¥1/GB 从账户余额扣减。余额不足时下载会被拦截。
|
|
94
94
|
|
|
95
95
|
## 文档
|
|
96
96
|
|
|
@@ -35,7 +35,7 @@ class AsyncQuantDBClient:
|
|
|
35
35
|
):
|
|
36
36
|
self.api_host = api_host.rstrip("/")
|
|
37
37
|
self.timeout = timeout
|
|
38
|
-
headers = {"User-Agent": "QuantDB-Python-SDK/0.1.
|
|
38
|
+
headers = {"User-Agent": "QuantDB-Python-SDK/0.1.4"}
|
|
39
39
|
if api_key:
|
|
40
40
|
headers["X-API-Key"] = api_key
|
|
41
41
|
elif token:
|
|
@@ -82,6 +82,10 @@ class AsyncQuantDBClient:
|
|
|
82
82
|
resp = await self._request("POST", path, json=json)
|
|
83
83
|
return self._check_response(resp)
|
|
84
84
|
|
|
85
|
+
async def _delete(self, path: str) -> dict:
|
|
86
|
+
resp = await self._request("DELETE", path)
|
|
87
|
+
return self._check_response(resp)
|
|
88
|
+
|
|
85
89
|
def _check_response(self, response: httpx.Response) -> dict:
|
|
86
90
|
if response.status_code == 200:
|
|
87
91
|
try:
|
|
@@ -123,15 +127,17 @@ class AsyncQuantDBClient:
|
|
|
123
127
|
used = data.get("used_traffic", 0)
|
|
124
128
|
limit = data.get("traffic_limit", 0)
|
|
125
129
|
credit = data.get("credit_limit", 0)
|
|
130
|
+
purchased = data.get("purchased_traffic", 0)
|
|
126
131
|
subscription = data.get("subscription", {}) or {}
|
|
127
132
|
return {
|
|
128
133
|
"used_gb": bytes_to_gb(used),
|
|
129
134
|
"limit_gb": bytes_to_gb(limit),
|
|
130
135
|
"credit_gb": bytes_to_gb(credit),
|
|
131
|
-
"
|
|
136
|
+
"purchased_gb": bytes_to_gb(purchased),
|
|
137
|
+
"remaining_gb": max(0.0, bytes_to_gb(limit + credit + purchased - used)),
|
|
132
138
|
"balance_yuan": data.get("balance_yuan", 0.0),
|
|
133
139
|
"subscription": subscription,
|
|
134
|
-
"is_active": subscription.get("status") == "active" and used < limit + credit,
|
|
140
|
+
"is_active": subscription.get("status") == "active" and used < limit + credit + purchased,
|
|
135
141
|
}
|
|
136
142
|
|
|
137
143
|
async def a_register(self, username: str, email: str, password: str) -> Dict[str, Any]:
|
|
@@ -159,16 +165,16 @@ class AsyncQuantDBClient:
|
|
|
159
165
|
"""创建新的 API Key(每个账号限制 1 个有效 Key)。"""
|
|
160
166
|
return await self._post("/api/v1/auth/api-keys", json={"description": description})
|
|
161
167
|
|
|
162
|
-
async def a_delete_api_key(self,
|
|
168
|
+
async def a_delete_api_key(self, key_id: int) -> Dict[str, Any]:
|
|
163
169
|
"""删除指定的 API Key。
|
|
164
170
|
|
|
165
171
|
Args:
|
|
166
|
-
|
|
172
|
+
key_id: ``a_list_api_keys()`` 返回的 API Key ID。
|
|
167
173
|
|
|
168
174
|
Returns:
|
|
169
175
|
删除结果信息。
|
|
170
176
|
"""
|
|
171
|
-
return await self.
|
|
177
|
+
return await self._delete(f"/api/v1/auth/api-keys/{key_id}")
|
|
172
178
|
|
|
173
179
|
# ========== 订阅与支付 ==========
|
|
174
180
|
|
|
@@ -42,6 +42,10 @@ class QuantDBClient:
|
|
|
42
42
|
self.timeout = timeout
|
|
43
43
|
self.token: Optional[str] = None
|
|
44
44
|
self.headers: Dict[str, str] = {}
|
|
45
|
+
self.session = requests.Session()
|
|
46
|
+
# User-Agent 中的版本与 pyproject.toml 同步,用于服务端日志归因
|
|
47
|
+
# 维护提示:每次版本号变化必须同步改这里(init 里的 __version__ 走 metadata 自动同步)
|
|
48
|
+
self.session.headers.update({"User-Agent": "QuantDB-Python-SDK/0.1.4"})
|
|
45
49
|
|
|
46
50
|
if api_key:
|
|
47
51
|
self.headers = {"X-API-Key": api_key}
|
|
@@ -50,10 +54,6 @@ class QuantDBClient:
|
|
|
50
54
|
else:
|
|
51
55
|
raise ValueError("必须提供 api_key 或 username+password")
|
|
52
56
|
|
|
53
|
-
self.session = requests.Session()
|
|
54
|
-
# User-Agent 中的版本与 pyproject.toml 同步,用于服务端日志归因
|
|
55
|
-
# 维护提示:每次版本号变化必须同步改这里(init 里的 __version__ 走 metadata 自动同步)
|
|
56
|
-
self.session.headers.update({"User-Agent": "QuantDB-Python-SDK/0.1.3"})
|
|
57
57
|
if self.headers:
|
|
58
58
|
self.session.headers.update(self.headers)
|
|
59
59
|
|
|
@@ -112,6 +112,10 @@ class QuantDBClient:
|
|
|
112
112
|
resp = self._request("POST", path, json=json)
|
|
113
113
|
return self._check_response(resp)
|
|
114
114
|
|
|
115
|
+
def _delete(self, path: str) -> dict:
|
|
116
|
+
resp = self._request("DELETE", path)
|
|
117
|
+
return self._check_response(resp)
|
|
118
|
+
|
|
115
119
|
def _check_response(self, response: requests.Response) -> dict:
|
|
116
120
|
if response.status_code == 200:
|
|
117
121
|
try:
|
|
@@ -163,15 +167,17 @@ class QuantDBClient:
|
|
|
163
167
|
used = data.get("used_traffic", 0)
|
|
164
168
|
limit = data.get("traffic_limit", 0)
|
|
165
169
|
credit = data.get("credit_limit", 0)
|
|
170
|
+
purchased = data.get("purchased_traffic", 0)
|
|
166
171
|
subscription = data.get("subscription", {}) or {}
|
|
167
172
|
return {
|
|
168
173
|
"used_gb": bytes_to_gb(used),
|
|
169
174
|
"limit_gb": bytes_to_gb(limit),
|
|
170
175
|
"credit_gb": bytes_to_gb(credit),
|
|
171
|
-
"
|
|
176
|
+
"purchased_gb": bytes_to_gb(purchased),
|
|
177
|
+
"remaining_gb": max(0.0, bytes_to_gb(limit + credit + purchased - used)),
|
|
172
178
|
"balance_yuan": data.get("balance_yuan", 0.0),
|
|
173
179
|
"subscription": subscription,
|
|
174
|
-
"is_active": subscription.get("status") == "active" and used < limit + credit,
|
|
180
|
+
"is_active": subscription.get("status") == "active" and used < limit + credit + purchased,
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
def register(self, username: str, email: str, password: str) -> Dict[str, Any]:
|
|
@@ -203,16 +209,16 @@ class QuantDBClient:
|
|
|
203
209
|
"""创建新的 API Key(每个账号限制 1 个有效 Key)。"""
|
|
204
210
|
return self._post("/api/v1/auth/api-keys", json={"description": description})
|
|
205
211
|
|
|
206
|
-
def delete_api_key(self,
|
|
212
|
+
def delete_api_key(self, key_id: int) -> Dict[str, Any]:
|
|
207
213
|
"""删除指定的 API Key。
|
|
208
214
|
|
|
209
215
|
Args:
|
|
210
|
-
|
|
216
|
+
key_id: ``list_api_keys()`` 返回的 API Key ID。
|
|
211
217
|
|
|
212
218
|
Returns:
|
|
213
219
|
删除结果信息。
|
|
214
220
|
"""
|
|
215
|
-
return self.
|
|
221
|
+
return self._delete(f"/api/v1/auth/api-keys/{key_id}")
|
|
216
222
|
|
|
217
223
|
# ========== 订阅与支付 ==========
|
|
218
224
|
|
|
@@ -609,4 +615,3 @@ class DuckDBWarehouse:
|
|
|
609
615
|
def list_views(self) -> Dict[str, str]:
|
|
610
616
|
"""列出当前已注册的视图及其对应本地路径。"""
|
|
611
617
|
return dict(self._views)
|
|
612
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantdb-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: QuantDB 量化数据平台官方 Python SDK
|
|
5
5
|
Author: QuantDB Team
|
|
6
6
|
License: MIT
|
|
@@ -90,7 +90,7 @@ client = QuantDBClient(username="admin", password="admin123")
|
|
|
90
90
|
|
|
91
91
|
## 流量说明
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
免费注册用户获赠 100 MB 一次性体验流量;订阅用户每月含 30 GB 下载流量,超出部分按 ¥1/GB 从账户余额扣减。余额不足时下载会被拦截。
|
|
94
94
|
|
|
95
95
|
## 文档
|
|
96
96
|
|
|
@@ -72,6 +72,7 @@ async def test_a_get_usage_with_subscription():
|
|
|
72
72
|
"used_traffic": 1073741824,
|
|
73
73
|
"traffic_limit": 53687091200,
|
|
74
74
|
"credit_limit": 0,
|
|
75
|
+
"purchased_traffic": 1073741824,
|
|
75
76
|
"balance_yuan": 0.0,
|
|
76
77
|
"subscription": {"plan_id": "pro-annual", "status": "active"},
|
|
77
78
|
},
|
|
@@ -81,6 +82,8 @@ async def test_a_get_usage_with_subscription():
|
|
|
81
82
|
usage = await client.a_get_usage()
|
|
82
83
|
assert usage["used_gb"] == 1.0
|
|
83
84
|
assert usage["limit_gb"] == 50.0
|
|
85
|
+
assert usage["purchased_gb"] == 1.0
|
|
86
|
+
assert usage["remaining_gb"] == 50.0
|
|
84
87
|
assert usage["subscription"]["status"] == "active"
|
|
85
88
|
assert usage["is_active"] is True
|
|
86
89
|
|
|
@@ -115,11 +118,11 @@ async def test_a_create_order():
|
|
|
115
118
|
@pytest.mark.asyncio
|
|
116
119
|
@respx.mock
|
|
117
120
|
async def test_a_delete_api_key():
|
|
118
|
-
respx.
|
|
121
|
+
respx.delete(f"{API_HOST}/api/v1/auth/api-keys/42").mock(
|
|
119
122
|
return_value=httpx.Response(200, json={"message": "API Key 撤销成功"})
|
|
120
123
|
)
|
|
121
124
|
async with AsyncQuantDBClient(api_host=API_HOST, api_key="test-key") as client:
|
|
122
|
-
result = await client.a_delete_api_key(
|
|
125
|
+
result = await client.a_delete_api_key(42)
|
|
123
126
|
assert result["message"] == "API Key 撤销成功"
|
|
124
127
|
|
|
125
128
|
|
|
@@ -74,6 +74,7 @@ def test_get_usage_with_subscription():
|
|
|
74
74
|
"used_traffic": 1073741824,
|
|
75
75
|
"traffic_limit": 32212254720,
|
|
76
76
|
"credit_limit": 0,
|
|
77
|
+
"purchased_traffic": 1073741824,
|
|
77
78
|
"balance_yuan": 0.0,
|
|
78
79
|
"subscription": {"plan_id": "pro-monthly", "status": "active"},
|
|
79
80
|
},
|
|
@@ -83,6 +84,8 @@ def test_get_usage_with_subscription():
|
|
|
83
84
|
usage = client.get_usage()
|
|
84
85
|
assert usage["used_gb"] == 1.0
|
|
85
86
|
assert usage["limit_gb"] == 30.0
|
|
87
|
+
assert usage["purchased_gb"] == 1.0
|
|
88
|
+
assert usage["remaining_gb"] == 30.0
|
|
86
89
|
assert usage["subscription"]["status"] == "active"
|
|
87
90
|
assert usage["is_active"] is True
|
|
88
91
|
|
|
@@ -114,13 +117,13 @@ def test_create_order():
|
|
|
114
117
|
|
|
115
118
|
@responses.activate
|
|
116
119
|
def test_delete_api_key():
|
|
117
|
-
responses.
|
|
118
|
-
f"{API_HOST}/api/v1/auth/api-keys/
|
|
120
|
+
responses.delete(
|
|
121
|
+
f"{API_HOST}/api/v1/auth/api-keys/42",
|
|
119
122
|
json={"message": "API Key 撤销成功"},
|
|
120
123
|
status=200,
|
|
121
124
|
)
|
|
122
125
|
client = QuantDBClient(api_host=API_HOST, api_key="test-key")
|
|
123
|
-
result = client.delete_api_key(
|
|
126
|
+
result = client.delete_api_key(42)
|
|
124
127
|
assert result["message"] == "API Key 撤销成功"
|
|
125
128
|
|
|
126
129
|
|
|
@@ -149,4 +152,3 @@ def test_duckdb_warehouse(tmp_path):
|
|
|
149
152
|
df = wh.query("SELECT * FROM stock_val WHERE val > 15")
|
|
150
153
|
assert len(df) == 1
|
|
151
154
|
assert df.iloc[0]["code"] == "600519.SH"
|
|
152
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|