quantdb-sdk 0.3.5__tar.gz → 0.4.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.
Files changed (38) hide show
  1. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/CHANGELOG.md +16 -0
  2. {quantdb_sdk-0.3.5/quantdb_sdk.egg-info → quantdb_sdk-0.4.0}/PKG-INFO +1 -1
  3. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/pyproject.toml +1 -1
  4. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/__init__.py +6 -1
  5. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/__main__.py +32 -1
  6. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/async_client.py +17 -1
  7. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/client.py +18 -1
  8. quantdb_sdk-0.4.0/quantdb_sdk/examples/demo_07_data_hub.py +66 -0
  9. quantdb_sdk-0.4.0/quantdb_sdk/hub.py +968 -0
  10. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0/quantdb_sdk.egg-info}/PKG-INFO +1 -1
  11. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/SOURCES.txt +3 -0
  12. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_async_client.py +22 -3
  13. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_client.py +23 -8
  14. quantdb_sdk-0.4.0/tests/test_data_hub.py +317 -0
  15. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_update_pipeline.py +4 -2
  16. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/LICENSE +0 -0
  17. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/MANIFEST.in +0 -0
  18. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/README.md +0 -0
  19. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/_utils.py +0 -0
  20. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/errors.py +0 -0
  21. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/__init__.py +0 -0
  22. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_01_connect_basics.py +0 -0
  23. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_02_kline_v2_daily.py +0 -0
  24. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_03_sync_full_market.py +0 -0
  25. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_04_duckdb_warehouse.py +0 -0
  26. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_05_read_local_data.py +0 -0
  27. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_06_tick_and_factors.py +0 -0
  28. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/layout.py +0 -0
  29. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/py.typed +0 -0
  30. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/dependency_links.txt +0 -0
  31. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/entry_points.txt +0 -0
  32. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/requires.txt +0 -0
  33. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/top_level.txt +0 -0
  34. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/setup.cfg +0 -0
  35. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_hub_models.py +0 -0
  36. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_layout.py +0 -0
  37. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_technical_indicators.py +0 -0
  38. {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_utils.py +0 -0
@@ -3,6 +3,22 @@
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.4.0] - 2026-10-05
7
+
8
+ ### Added
9
+
10
+ - **统一数据入口** **`client.data`**(同步 `DataHub` / 异步 `AsyncDataHub`,模块 `quantdb_sdk.hub`):只凭 **dataset 名**即可跨 6 大分类目录取数,不必再手填 `category_id + sub_category`。SDK 侧解析分类后复用现有端点,**零后端改动**。
11
+
12
+ - **发现**:`datasets(category, access, keyword)` 列出 29 个数据集目录;`info(dataset)` 返回注册表字段并叠加云端元数据(覆盖区间 / 文件数 / 文件大小)。
13
+ - **本地零流量**:`local_files` / `local_path` / `read_local` / `read`(本地优先,缺失时显式回退在线)。数据根目录可配置(`root` 参数,缺省读 `QUANTDB_DATA_DIR`);多形态 glob 兼容 V2 `dt=` 分区、tick 平铺/子目录分片、V1 逐股票文件;V2/tick 无日期列时从路径注入 `trade_date`。
14
+ - **在线**:`preview` / `load` / `download` / `query` / `sync` / `mount`,逐项委托既有方法。`query` 按数据集自动路由:日线三兄弟走 `query_kline`、ETF 走 `query_etf_kline`、tick 走 `query_tick` 并按 symbol 后置过滤、其余 V2 走通用日分区分支、V1 逐股票走 `layout="v1"` + 日期列过滤。
15
+ - 异步方法统一 `a_` 前缀;异步端不含 `mount`(无 `a_mount_local_dataset`,DuckDB 仓库为同步)。
16
+ - 新增 `DATASET_REGISTRY` 注册表(29 项),`category_id` 复用 `SYNC_DATASET_CATEGORIES`,`category_dir` 复用 `CATEGORY_DIRS`,避免重复维护分类事实源。
17
+
18
+ - **命令行** **`quantdb datasets [name]`**:列出全部数据集目录或查看某个数据集详情(无需 API Key)。
19
+
20
+ - 新增演示脚本 `quantdb_sdk/examples/demo_07_data_hub.py`。
21
+
6
22
  ## \[0.3.5] - 2026-09-26
7
23
 
8
24
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantdb-sdk
3
- Version: 0.3.5
3
+ Version: 0.4.0
4
4
  Summary: QuantDB 量化数据平台官方 Python SDK
5
5
  Author: QuantDB Team
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quantdb-sdk"
7
- version = "0.3.5"
7
+ version = "0.4.0"
8
8
  description = "QuantDB 量化数据平台官方 Python SDK"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -5,12 +5,13 @@ try:
5
5
  # 注意:此处用 PyPI 包名(连字符)查找版本号,不是模块名(下划线)
6
6
  __version__ = version("quantdb-sdk")
7
7
  except ImportError:
8
- __version__ = "0.3.4"
8
+ __version__ = "0.4.0"
9
9
 
10
10
  from . import layout
11
11
  from ._utils import scan_local_directory
12
12
  from .async_client import AsyncQuantDBClient
13
13
  from .client import DuckDBWarehouse, QuantDBClient
14
+ from .hub import DATASET_REGISTRY, AsyncDataHub, DataHub
14
15
  from .errors import (
15
16
  AuthError,
16
17
  InsufficientTrafficError,
@@ -33,6 +34,10 @@ __all__ = [
33
34
  "QuantDBClient",
34
35
  "AsyncQuantDBClient",
35
36
  "DuckDBWarehouse",
37
+ # 统一数据入口(quantdb_sdk.hub)
38
+ "DataHub",
39
+ "AsyncDataHub",
40
+ "DATASET_REGISTRY",
36
41
  "QuantDBError",
37
42
  "AuthError",
38
43
  "InsufficientTrafficError",
@@ -4,6 +4,8 @@
4
4
  python -m quantdb --version
5
5
  python -m quantdb --help
6
6
  python -m quantdb layout # 查看本地数据目录规范
7
+ python -m quantdb datasets # 列出全部数据集目录(无需 API Key)
8
+ python -m quantdb datasets l1_factors # 查看某个数据集详情
7
9
  QUANTDB_API_KEY=qdb_xxx quantdb check # 验证 API Key 是否有效
8
10
 
9
11
  演示脚本见 quantdb_sdk.examples(demo_01 ~ demo_06),
@@ -13,6 +15,7 @@
13
15
  import argparse
14
16
  import os
15
17
  import sys
18
+ from typing import Optional
16
19
 
17
20
  from . import __version__, layout
18
21
  from .client import QuantDBClient
@@ -38,6 +41,27 @@ def cmd_layout() -> int:
38
41
  return 0
39
42
 
40
43
 
44
+ def cmd_datasets(name: Optional[str] = None) -> int:
45
+ """列出全部数据集目录,或查看某个数据集详情(无需 API Key)。"""
46
+ from .hub import DATASET_REGISTRY
47
+
48
+ if name:
49
+ info = DATASET_REGISTRY.get(name)
50
+ if info is None:
51
+ print(f"[ERROR] 未知数据集: {name}", file=sys.stderr)
52
+ return 1
53
+ print(f"{info.dataset} {info.name} ({info.category_id} {info.category_dir})")
54
+ print(f" 描述: {info.description}")
55
+ print(f" 访问: {info.access} 布局: {info.layout} 选择器: {', '.join(info.selectors) or '-'}")
56
+ print(f" 关键词: {', '.join(info.keywords)}")
57
+ return 0
58
+
59
+ print(f"共 {len(DATASET_REGISTRY)} 个数据集:")
60
+ for info in DATASET_REGISTRY.values():
61
+ print(f" [{info.category_id}] {info.dataset:<20s} {info.name:<16s} {info.access:<12s} {info.layout}")
62
+ return 0
63
+
64
+
41
65
  def main() -> int:
42
66
  parser = argparse.ArgumentParser(
43
67
  prog="quantdb",
@@ -71,6 +95,11 @@ def main() -> int:
71
95
  # layout 命令:打印目录规范
72
96
  layout_parser = subparsers.add_parser("layout", help="查看本地数据目录规范(无需 API Key)")
73
97
 
98
+ # datasets 命令:列出数据集目录(无需 API Key)
99
+ datasets_parser = subparsers.add_parser("datasets", help="列出全部数据集目录(无需 API Key)")
100
+ datasets_parser.add_argument("name", nargs="?", default=None,
101
+ help="可选:查看某个数据集详情,如 l1_factors")
102
+
74
103
  sync_parser = subparsers.add_parser("sync", help="以 release/Manifest 增量同步一个数据集")
75
104
  sync_parser.add_argument("dataset", help="数据集,如 daily_forward、balance、etf_pcf")
76
105
  sync_parser.add_argument("--save-dir", default=None,
@@ -83,9 +112,11 @@ def main() -> int:
83
112
  parser.print_help()
84
113
  return 0
85
114
 
86
- # layout 命令无需 API Key,直接打印目录规范
115
+ # layout / datasets 命令无需 API Key
87
116
  if args.command == "layout":
88
117
  return cmd_layout()
118
+ if args.command == "datasets":
119
+ return cmd_datasets(args.name)
89
120
 
90
121
  api_key = os.getenv(args.api_key_env)
91
122
  if not api_key:
@@ -28,7 +28,7 @@ from .errors import (
28
28
  )
29
29
 
30
30
  # 维护提示:每次发版必须与 pyproject.toml 版本号同步
31
- _USER_AGENT = "QuantDB-Python-SDK/0.3.5"
31
+ _USER_AGENT = "QuantDB-Python-SDK/0.4.0"
32
32
 
33
33
 
34
34
  def _etag_hex(value: Optional[str]) -> Optional[str]:
@@ -83,6 +83,9 @@ class AsyncQuantDBClient:
83
83
  # 进程内 parquet ETag 缓存:同对象(ETag 未变)不重复下载,避免重复计费。
84
84
  self._cache: Dict[str, Dict[str, Any]] = {}
85
85
 
86
+ # 统一数据入口(quantdb_sdk.hub.AsyncDataHub)惰性实例,见 data property。
87
+ self._data_hub: Optional[Any] = None
88
+
86
89
  async def close(self) -> None:
87
90
  """关闭底层 httpx 客户端。"""
88
91
  await self.client.aclose()
@@ -92,6 +95,19 @@ class AsyncQuantDBClient:
92
95
  """清空进程内 Parquet 缓存(强制下次重新下载最新数据)。"""
93
96
  self._cache.clear()
94
97
 
98
+ @property
99
+ def data(self) -> "AsyncDataHub":
100
+ """统一数据入口(异步):只凭 dataset 名跨 6 大分类目录取数。
101
+
102
+ 返回 :class:`quantdb_sdk.hub.AsyncDataHub`,惰性创建,方法名统一 ``a_`` 前缀。
103
+ 不含挂载方法(异步端无 ``a_mount_local_dataset``);如需 DuckDB 挂载视图,
104
+ 请改用同步客户端 ``client.data.mount(...)``。
105
+ """
106
+ if self._data_hub is None:
107
+ from .hub import AsyncDataHub # 函数内 import,避免与 hub 循环依赖
108
+ self._data_hub = AsyncDataHub(self)
109
+ return self._data_hub
110
+
95
111
  @asynccontextmanager
96
112
  async def _download_stream(self, params: Dict[str, Any], headers: Optional[Dict[str, str]] = None):
97
113
  """下载专用流式请求:手动处理服务端 302 CDN 直连跳转。
@@ -32,7 +32,7 @@ from .errors import (
32
32
  )
33
33
 
34
34
  # 维护提示:每次发版必须与 pyproject.toml 版本号同步
35
- _USER_AGENT = "QuantDB-Python-SDK/0.3.5"
35
+ _USER_AGENT = "QuantDB-Python-SDK/0.4.0"
36
36
 
37
37
 
38
38
  def _etag_hex(value: Optional[str]) -> Optional[str]:
@@ -92,6 +92,9 @@ class QuantDBClient:
92
92
  # 只需下载一次。进程退出即失效(无跨进程副作用)。
93
93
  self._cache: Dict[str, Dict[str, Any]] = {}
94
94
 
95
+ # 统一数据入口(quantdb_sdk.hub.DataHub)惰性实例,见 data property。
96
+ self._data_hub: Optional[Any] = None
97
+
95
98
  retries = Retry(
96
99
  total=max_retries,
97
100
  backoff_factor=0.5,
@@ -263,6 +266,20 @@ class QuantDBClient:
263
266
  """
264
267
  self._cache.clear()
265
268
 
269
+ @property
270
+ def data(self) -> "DataHub":
271
+ """统一数据入口:只凭 dataset 名跨 6 大分类目录取数。
272
+
273
+ 返回 :class:`quantdb_sdk.hub.DataHub`,惰性创建。支持发现(``datasets`` /
274
+ ``info``)、本地零流量读取(``local_files`` / ``local_path`` / ``read_local`` /
275
+ ``read``)、在线查询(``preview`` / ``load`` / ``download`` / ``query``)与
276
+ 同步挂载(``sync`` / ``mount``)。
277
+ """
278
+ if self._data_hub is None:
279
+ from .hub import DataHub # 函数内 import,避免与 hub 循环依赖
280
+ self._data_hub = DataHub(self)
281
+ return self._data_hub
282
+
266
283
  @staticmethod
267
284
  def _validate_layout(layout: str) -> Literal["auto", "v1", "v2"]:
268
285
  if layout not in {"auto", "v1", "v2"}:
@@ -0,0 +1,66 @@
1
+ """demo_07 统一数据入口 client.data(DataHub)。
2
+
3
+ 只凭 dataset 名跨 6 大分类目录取数,不必手填 category_id + sub_category:
4
+ 1. datasets() — 列出 29 个数据集目录(零流量)
5
+ 2. info() — 覆盖区间 + 文件大小
6
+ 3. read_local() — 本地零流量读取(root 可配,缺省读 QUANTDB_DATA_DIR)
7
+ 4. preview() — 在线预览(不消耗下载流量)
8
+ 5. query() — 在线查询(消耗下载流量)
9
+
10
+ 运行::
11
+
12
+ QUANTDB_API_KEY=qdb_xxx python -m quantdb_sdk.examples.demo_07_data_hub
13
+ QUANTDB_API_KEY=qdb_xxx python -m quantdb_sdk.examples.demo_07_data_hub --dataset l1_factors
14
+ """
15
+
16
+ import argparse
17
+ import os
18
+
19
+ from quantdb_sdk import QuantDBClient
20
+ from quantdb_sdk.errors import NotFoundError, QuantDBError
21
+
22
+
23
+ def main() -> None:
24
+ p = argparse.ArgumentParser(description=__doc__)
25
+ p.add_argument("--api-host", default="https://www.quantdb.cn")
26
+ p.add_argument("--api-key", default=None, help="默认读环境变量 QUANTDB_API_KEY")
27
+ p.add_argument("--root", default=None, help="本地数据根目录(默认读 QUANTDB_DATA_DIR / 回退默认下载目录)")
28
+ p.add_argument("--symbol", default="600519.SH")
29
+ p.add_argument("--dataset", default="daily_forward")
30
+ args = p.parse_args()
31
+
32
+ api_key = args.api_key or os.getenv("QUANTDB_API_KEY")
33
+ if not api_key:
34
+ raise SystemExit("请设置 QUANTDB_API_KEY 或传 --api-key")
35
+
36
+ client = QuantDBClient(api_host=args.api_host, api_key=api_key)
37
+ hub = client.data
38
+
39
+ # 1. 数据集目录(零流量)
40
+ print("== 数据集目录(前 8 个)==")
41
+ print(hub.datasets().head(8).to_string(index=False))
42
+
43
+ # 2. 元数据(覆盖区间 + 文件大小)
44
+ info = hub.info(args.dataset)
45
+ print(f"\n== {args.dataset} 元数据 ==")
46
+ print(f" {info['name']} 分类={info['category_dir']} 区间={info['min_date']}~{info['max_date']}")
47
+ print(f" 文件数={info['file_count']} 大小={info['file_size_gb']} GB")
48
+
49
+ # 3. 本地零流量读取(root 可配)
50
+ print(f"\n== 本地读取 {args.dataset} ==")
51
+ try:
52
+ local = hub.read_local(args.dataset, symbol=args.symbol, root=args.root, latest=True)
53
+ print(local.head(3).to_string(index=False))
54
+ except NotFoundError:
55
+ print(f" 本地未找到 {args.dataset};可先 client.data.sync('{args.dataset}') 同步")
56
+
57
+ # 4. 在线预览(不消耗下载流量)
58
+ try:
59
+ print(f"\n== 在线预览 {args.dataset} ==")
60
+ print(hub.preview(args.dataset, symbol=args.symbol, limit=3).to_string(index=False))
61
+ except QuantDBError as e:
62
+ print(f" 预览失败: {e}")
63
+
64
+
65
+ if __name__ == "__main__":
66
+ main()