quantdb-sdk 0.2.2__tar.gz → 0.2.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.2.2 → quantdb_sdk-0.2.4}/CHANGELOG.md +10 -0
- {quantdb_sdk-0.2.2/quantdb_sdk.egg-info → quantdb_sdk-0.2.4}/PKG-INFO +1 -1
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/pyproject.toml +1 -1
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk/__init__.py +1 -1
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk/__main__.py +2 -2
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk/async_client.py +1 -1
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk/client.py +18 -4
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4/quantdb_sdk.egg-info}/PKG-INFO +1 -1
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/tests/test_client.py +11 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/LICENSE +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/MANIFEST.in +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/README.md +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk/_utils.py +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk/errors.py +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk/py.typed +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk.egg-info/SOURCES.txt +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk.egg-info/dependency_links.txt +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk.egg-info/entry_points.txt +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk.egg-info/requires.txt +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/quantdb_sdk.egg-info/top_level.txt +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/setup.cfg +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/tests/test_async_client.py +0 -0
- {quantdb_sdk-0.2.2 → quantdb_sdk-0.2.4}/tests/test_technical_indicators.py +0 -0
|
@@ -3,6 +3,16 @@
|
|
|
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.2.4] - 2026-07-26
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- **Windows CLI 编码**:移除 `✓/✗` 字符,GBK 控制台可正常执行 `quantdb check`。
|
|
10
|
+
|
|
11
|
+
## [0.2.3] - 2026-07-26
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **财务本地仓库**:`mount_local_dataset()` 支持财务 V1、ETF/可转债与更多日频子类;财务数据以 `Symbol + m_timetag/endDate/time` 业务主键去重。
|
|
15
|
+
|
|
6
16
|
## [0.2.2] - 2026-07-26
|
|
7
17
|
|
|
8
18
|
### Added
|
|
@@ -56,7 +56,7 @@ def main() -> int:
|
|
|
56
56
|
|
|
57
57
|
if args.command == "check":
|
|
58
58
|
me = client.get_me()
|
|
59
|
-
print(
|
|
59
|
+
print("[OK] API Key 有效")
|
|
60
60
|
print(f" 用户: {me.get('username', 'N/A')}")
|
|
61
61
|
print(f" ID: {me.get('id', 'N/A')}")
|
|
62
62
|
return 0
|
|
@@ -81,7 +81,7 @@ def main() -> int:
|
|
|
81
81
|
return 0
|
|
82
82
|
|
|
83
83
|
except Exception as e:
|
|
84
|
-
print(f"
|
|
84
|
+
print(f"[ERROR] {e}", file=sys.stderr)
|
|
85
85
|
return 1
|
|
86
86
|
|
|
87
87
|
return 0
|
|
@@ -37,7 +37,7 @@ class AsyncQuantDBClient:
|
|
|
37
37
|
):
|
|
38
38
|
self.api_host = api_host.rstrip("/")
|
|
39
39
|
self.timeout = timeout
|
|
40
|
-
headers = {"User-Agent": "QuantDB-Python-SDK/0.2.
|
|
40
|
+
headers = {"User-Agent": "QuantDB-Python-SDK/0.2.4"}
|
|
41
41
|
if api_key:
|
|
42
42
|
headers["X-API-Key"] = api_key
|
|
43
43
|
elif token:
|
|
@@ -48,7 +48,7 @@ class QuantDBClient:
|
|
|
48
48
|
self.session = requests.Session()
|
|
49
49
|
# User-Agent 中的版本与 pyproject.toml 同步,用于服务端日志归因
|
|
50
50
|
# 维护提示:每次版本号变化必须同步改这里(init 里的 __version__ 走 metadata 自动同步)
|
|
51
|
-
self.session.headers.update({"User-Agent": "QuantDB-Python-SDK/0.2.
|
|
51
|
+
self.session.headers.update({"User-Agent": "QuantDB-Python-SDK/0.2.4"})
|
|
52
52
|
|
|
53
53
|
if api_key:
|
|
54
54
|
self.headers = {"X-API-Key": api_key}
|
|
@@ -713,7 +713,17 @@ class QuantDBClient:
|
|
|
713
713
|
"""挂载本地 V1/V2 同构数据集;V2 patch 与 daily 文件均参与去重。"""
|
|
714
714
|
warehouse = self.get_local_warehouse(save_dir)
|
|
715
715
|
root = os.path.abspath(save_dir or default_download_dir()).replace("\\", "/")
|
|
716
|
-
views = {
|
|
716
|
+
views = {
|
|
717
|
+
"daily_unadjusted": "1_kline_data/daily_unadjusted", "daily_forward": "1_kline_data/daily_forward",
|
|
718
|
+
"daily_backward": "1_kline_data/daily_backward", "index_daily": "1_kline_data/index_daily",
|
|
719
|
+
"margin_trading": "2_base_sector/margin_trading", "valuation": "5_technical_derived/valuation",
|
|
720
|
+
"technical_indicators": "5_technical_derived/technical_indicators", "market_sentiment": "5_technical_derived/market_sentiment",
|
|
721
|
+
"features_daily": "6_ml_datasets/features_daily", "balance": "3_financial_data/balance",
|
|
722
|
+
"income": "3_financial_data/income", "cashflow": "3_financial_data/cashflow",
|
|
723
|
+
"capital": "3_financial_data/capital", "holder_num": "3_financial_data/holder_num",
|
|
724
|
+
"pershare_index": "3_financial_data/pershare_index", "dividend_factors": "3_financial_data/dividend_factors",
|
|
725
|
+
"etf_pcf": "4_bond_etf/etf_pcf", "convertible_bond": "4_bond_etf/convertible_bond",
|
|
726
|
+
}
|
|
717
727
|
if dataset not in views: raise ValidationError(f"不支持的 V2 数据集: {dataset}")
|
|
718
728
|
name = view_name or re.sub(r"[^a-zA-Z0-9_]", "_", dataset)
|
|
719
729
|
state_path = os.path.join(root.replace("/", os.sep), "quantdb_sync.sqlite")
|
|
@@ -732,9 +742,13 @@ class QuantDBClient:
|
|
|
732
742
|
quoted = ", ".join("'" + p.replace("'", "''") + "'" for p in files)
|
|
733
743
|
relation = f"read_parquet([{quoted}], union_by_name=true)"
|
|
734
744
|
columns = {row[0] for row in warehouse.conn.execute(f"DESCRIBE SELECT * FROM {relation}").fetchall()}
|
|
735
|
-
if
|
|
745
|
+
symbol_column = "symbol" if "symbol" in columns else ("Symbol" if "Symbol" in columns else None)
|
|
746
|
+
date_column = next((column for column in ("time", "m_timetag", "endDate", "date") if column in columns), None)
|
|
747
|
+
if symbol_column and date_column:
|
|
736
748
|
order = "coalesce(release_id, '') DESC" if "release_id" in columns else "0"
|
|
737
|
-
|
|
749
|
+
symbol_sql = '"' + symbol_column.replace('"', '""') + '"'
|
|
750
|
+
date_sql = '"' + date_column.replace('"', '""') + '"'
|
|
751
|
+
warehouse.conn.execute(f"CREATE OR REPLACE VIEW {name} AS SELECT * EXCLUDE (rn) FROM (SELECT *, row_number() OVER (PARTITION BY {symbol_sql}, {date_sql} ORDER BY {order}) rn FROM {relation}) WHERE rn=1")
|
|
738
752
|
else:
|
|
739
753
|
warehouse.conn.execute(f"CREATE OR REPLACE VIEW {name} AS SELECT * FROM {relation}")
|
|
740
754
|
warehouse._views[name] = f"{dataset}"
|
|
@@ -230,3 +230,14 @@ def test_duckdb_warehouse(tmp_path):
|
|
|
230
230
|
df = wh.query("SELECT * FROM stock_val WHERE val > 15")
|
|
231
231
|
assert len(df) == 1
|
|
232
232
|
assert df.iloc[0]["code"] == "600519.SH"
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def test_mount_local_financial_dataset_deduplicates_business_key(tmp_path):
|
|
236
|
+
pytest.importorskip("duckdb")
|
|
237
|
+
directory = tmp_path / "3_financial_data" / "balance"
|
|
238
|
+
directory.mkdir(parents=True)
|
|
239
|
+
pd.DataFrame({"Symbol": ["600000.SH"], "m_timetag": [20260331], "value": [1]}).to_parquet(directory / "a.parquet")
|
|
240
|
+
pd.DataFrame({"Symbol": ["600000.SH"], "m_timetag": [20260331], "value": [2]}).to_parquet(directory / "b.parquet")
|
|
241
|
+
client = QuantDBClient(api_host=API_HOST, api_key="test-key")
|
|
242
|
+
warehouse = client.mount_local_dataset("balance", str(tmp_path))
|
|
243
|
+
assert len(warehouse.query("SELECT * FROM balance")) == 1
|
|
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
|