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.
Files changed (23) hide show
  1. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/CHANGELOG.md +7 -0
  2. {quantdb_sdk-0.1.3/quantdb_sdk.egg-info → quantdb_sdk-0.1.4}/PKG-INFO +2 -2
  3. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/README.md +1 -1
  4. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/pyproject.toml +1 -1
  5. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/async_client.py +12 -6
  6. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/client.py +15 -10
  7. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4/quantdb_sdk.egg-info}/PKG-INFO +2 -2
  8. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/tests/test_async_client.py +5 -2
  9. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/tests/test_client.py +6 -4
  10. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/LICENSE +0 -0
  11. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/MANIFEST.in +0 -0
  12. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/__init__.py +0 -0
  13. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/__main__.py +0 -0
  14. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/_utils.py +0 -0
  15. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/errors.py +0 -0
  16. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk/py.typed +0 -0
  17. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/SOURCES.txt +0 -0
  18. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/dependency_links.txt +0 -0
  19. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/entry_points.txt +0 -0
  20. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/requires.txt +0 -0
  21. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/quantdb_sdk.egg-info/top_level.txt +0 -0
  22. {quantdb_sdk-0.1.3 → quantdb_sdk-0.1.4}/setup.cfg +0 -0
  23. {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
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
- 每月前 30 GB 下载流量免费,超出部分按 ¥1/GB 计费;账户余额不足时,下载会被限流,充值后即可恢复。
93
+ 免费注册用户获赠 100 MB 一次性体验流量;订阅用户每月含 30 GB 下载流量,超出部分按 ¥1/GB 从账户余额扣减。余额不足时下载会被拦截。
94
94
 
95
95
  ## 文档
96
96
 
@@ -53,7 +53,7 @@ client = QuantDBClient(username="admin", password="admin123")
53
53
 
54
54
  ## 流量说明
55
55
 
56
- 每月前 30 GB 下载流量免费,超出部分按 ¥1/GB 计费;账户余额不足时,下载会被限流,充值后即可恢复。
56
+ 免费注册用户获赠 100 MB 一次性体验流量;订阅用户每月含 30 GB 下载流量,超出部分按 ¥1/GB 从账户余额扣减。余额不足时下载会被拦截。
57
57
 
58
58
  ## 文档
59
59
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quantdb-sdk"
7
- version = "0.1.3"
7
+ version = "0.1.4"
8
8
  description = "QuantDB 量化数据平台官方 Python SDK"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -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.3"}
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
- "remaining_gb": max(0.0, bytes_to_gb(limit + credit - used)),
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, key_token: str) -> Dict[str, Any]:
168
+ async def a_delete_api_key(self, key_id: int) -> Dict[str, Any]:
163
169
  """删除指定的 API Key。
164
170
 
165
171
  Args:
166
- key_token: 要删除的 API Key 令牌。
172
+ key_id: ``a_list_api_keys()`` 返回的 API Key ID。
167
173
 
168
174
  Returns:
169
175
  删除结果信息。
170
176
  """
171
- return await self._post(f"/api/v1/auth/api-keys/{key_token}/delete")
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
- "remaining_gb": max(0.0, bytes_to_gb(limit + credit - used)),
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, key_token: str) -> Dict[str, Any]:
212
+ def delete_api_key(self, key_id: int) -> Dict[str, Any]:
207
213
  """删除指定的 API Key。
208
214
 
209
215
  Args:
210
- key_token: 要删除的 API Key 令牌。
216
+ key_id: ``list_api_keys()`` 返回的 API Key ID。
211
217
 
212
218
  Returns:
213
219
  删除结果信息。
214
220
  """
215
- return self._post(f"/api/v1/auth/api-keys/{key_token}/delete")
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
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
- 每月前 30 GB 下载流量免费,超出部分按 ¥1/GB 计费;账户余额不足时,下载会被限流,充值后即可恢复。
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.post(f"{API_HOST}/api/v1/auth/api-keys/qdb_abc123/delete").mock(
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("qdb_abc123")
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.post(
118
- f"{API_HOST}/api/v1/auth/api-keys/qdb_abc123/delete",
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("qdb_abc123")
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