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.
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/CHANGELOG.md +16 -0
- {quantdb_sdk-0.3.5/quantdb_sdk.egg-info → quantdb_sdk-0.4.0}/PKG-INFO +1 -1
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/pyproject.toml +1 -1
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/__init__.py +6 -1
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/__main__.py +32 -1
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/async_client.py +17 -1
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/client.py +18 -1
- quantdb_sdk-0.4.0/quantdb_sdk/examples/demo_07_data_hub.py +66 -0
- quantdb_sdk-0.4.0/quantdb_sdk/hub.py +968 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0/quantdb_sdk.egg-info}/PKG-INFO +1 -1
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/SOURCES.txt +3 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_async_client.py +22 -3
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_client.py +23 -8
- quantdb_sdk-0.4.0/tests/test_data_hub.py +317 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_update_pipeline.py +4 -2
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/LICENSE +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/MANIFEST.in +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/README.md +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/_utils.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/errors.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/__init__.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_01_connect_basics.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_02_kline_v2_daily.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_03_sync_full_market.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_04_duckdb_warehouse.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_05_read_local_data.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/examples/demo_06_tick_and_factors.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/layout.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk/py.typed +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/dependency_links.txt +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/entry_points.txt +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/requires.txt +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/quantdb_sdk.egg-info/top_level.txt +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/setup.cfg +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_hub_models.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_layout.py +0 -0
- {quantdb_sdk-0.3.5 → quantdb_sdk-0.4.0}/tests/test_technical_indicators.py +0 -0
- {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
|
|
@@ -5,12 +5,13 @@ try:
|
|
|
5
5
|
# 注意:此处用 PyPI 包名(连字符)查找版本号,不是模块名(下划线)
|
|
6
6
|
__version__ = version("quantdb-sdk")
|
|
7
7
|
except ImportError:
|
|
8
|
-
__version__ = "0.
|
|
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.
|
|
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.
|
|
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()
|