deeptrade-quant 0.4.0__tar.gz → 0.4.1__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.
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/PKG-INFO +1 -1
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/__init__.py +1 -1
- deeptrade_quant-0.4.1/deeptrade/core/migrations/core/20260512_001_drop_legacy_tushare_cache.sql +10 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/tushare_client.py +40 -3
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/pyproject.toml +1 -1
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_db.py +2 -2
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_tushare_client.py +34 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/.gitignore +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/LICENSE +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/README.md +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/cli.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/cli_config.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/cli_data.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/cli_plugin.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/__init__.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/config.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/config_migrations.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/db.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/dep_installer.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/github_fetch.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/llm_client.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/llm_manager.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/logging_config.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/migrations/__init__.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/migrations/core/20260509_001_init.sql +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/migrations/core/__init__.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/paths.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/plugin_manager.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/plugin_source.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/registry.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/run_status.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/secrets.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/plugins_api/__init__.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/plugins_api/base.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/plugins_api/events.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/plugins_api/llm.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/plugins_api/metadata.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/theme.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/__init__.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/cli/__init__.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/cli/test_config_cmd.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/cli/test_plugin_cmd.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/cli/test_routing.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/conftest.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/__init__.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_config.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_config_migrations.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_github_fetch.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_llm_client.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_llm_manager.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_paths.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_plugin_dependencies.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_plugin_install.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_plugin_source.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_plugin_upgrade.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_registry.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_secrets.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_tushare_classifier.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/core/test_tushare_retry_r1.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/plugins_api/__init__.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/plugins_api/test_protocol.py +0 -0
- {deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/tests/test_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deeptrade-quant
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: LLM-driven A-share (Shanghai/Shenzhen main board) stock screening CLI
|
|
5
5
|
Project-URL: Homepage, https://github.com/ty19880929/deeptrade
|
|
6
6
|
Project-URL: Repository, https://github.com/ty19880929/deeptrade
|
deeptrade_quant-0.4.1/deeptrade/core/migrations/core/20260512_001_drop_legacy_tushare_cache.sql
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- v0.4.1 — drop legacy tushare_cache_blob entries.
|
|
2
|
+
--
|
|
3
|
+
-- Pre-v0.4.1 payloads were stored as a bare JSON array, then read back via
|
|
4
|
+
-- pd.read_json which triggered a pandas FutureWarning on string columns whose
|
|
5
|
+
-- names matched its date heuristic (trade_date, cal_date, ...). The cache
|
|
6
|
+
-- read/write path now wraps payloads as {version:1, schema:{...}, data:[...]}
|
|
7
|
+
-- and restores dtypes explicitly. Old rows are incompatible with that reader,
|
|
8
|
+
-- so wipe the table; TushareClient._ensure_cache_table re-creates it lazily on
|
|
9
|
+
-- the next write.
|
|
10
|
+
DROP TABLE IF EXISTS tushare_cache_blob;
|
|
@@ -19,7 +19,6 @@ Cache class buckets:
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
21
|
import hashlib
|
|
22
|
-
import io
|
|
23
22
|
import json
|
|
24
23
|
import logging
|
|
25
24
|
import threading
|
|
@@ -896,6 +895,15 @@ class TushareClient:
|
|
|
896
895
|
")"
|
|
897
896
|
)
|
|
898
897
|
|
|
898
|
+
# Payload wrapper format (v0.4.1+):
|
|
899
|
+
# {"version": 1, "schema": {col: dtype_str}, "data": [...records...]}
|
|
900
|
+
# Bypasses pd.read_json's date-column heuristic (which warns on string
|
|
901
|
+
# columns named like dates — trade_date / cal_date / ann_date) and
|
|
902
|
+
# restores dtypes explicitly from the recorded schema. Pre-v0.4.1 rows
|
|
903
|
+
# were a bare records array; those are wiped by core migration
|
|
904
|
+
# 20260512_001_drop_legacy_tushare_cache.sql, so no legacy branch here.
|
|
905
|
+
_CACHE_PAYLOAD_VERSION = 1
|
|
906
|
+
|
|
899
907
|
def _write_cached(
|
|
900
908
|
self,
|
|
901
909
|
api_name: str,
|
|
@@ -906,7 +914,14 @@ class TushareClient:
|
|
|
906
914
|
self._ensure_cache_table()
|
|
907
915
|
body = json.dumps(params, sort_keys=True, default=str)
|
|
908
916
|
h = hashlib.sha256(body.encode("utf-8")).hexdigest()
|
|
909
|
-
|
|
917
|
+
records_json = df.to_json(orient="records", date_format="iso")
|
|
918
|
+
payload = json.dumps(
|
|
919
|
+
{
|
|
920
|
+
"version": self._CACHE_PAYLOAD_VERSION,
|
|
921
|
+
"schema": {col: str(dt) for col, dt in df.dtypes.items()},
|
|
922
|
+
"data": json.loads(records_json) if records_json else [],
|
|
923
|
+
}
|
|
924
|
+
)
|
|
910
925
|
with self._db.transaction():
|
|
911
926
|
self._db.execute(
|
|
912
927
|
"DELETE FROM tushare_cache_blob "
|
|
@@ -937,12 +952,34 @@ class TushareClient:
|
|
|
937
952
|
)
|
|
938
953
|
if row is None:
|
|
939
954
|
return pd.DataFrame()
|
|
940
|
-
|
|
955
|
+
wrapper = json.loads(row[0])
|
|
956
|
+
df = self._restore_cached_frame(wrapper)
|
|
941
957
|
if fields:
|
|
942
958
|
cols = [c.strip() for c in fields.split(",") if c.strip() in df.columns]
|
|
943
959
|
df = df[cols]
|
|
944
960
|
return df
|
|
945
961
|
|
|
962
|
+
@classmethod
|
|
963
|
+
def _restore_cached_frame(cls, wrapper: dict[str, Any]) -> pd.DataFrame:
|
|
964
|
+
schema: dict[str, str] = wrapper["schema"]
|
|
965
|
+
data: list[dict[str, Any]] = wrapper["data"]
|
|
966
|
+
df = pd.DataFrame.from_records(data, columns=list(schema.keys()))
|
|
967
|
+
for col, dtype_str in schema.items():
|
|
968
|
+
if col not in df.columns:
|
|
969
|
+
continue
|
|
970
|
+
if dtype_str.startswith("datetime"):
|
|
971
|
+
df[col] = pd.to_datetime(df[col], errors="coerce")
|
|
972
|
+
elif dtype_str == "object":
|
|
973
|
+
continue
|
|
974
|
+
else:
|
|
975
|
+
try:
|
|
976
|
+
df[col] = df[col].astype(dtype_str)
|
|
977
|
+
except (TypeError, ValueError):
|
|
978
|
+
# Best-effort: if a numeric/bool column can't be coerced
|
|
979
|
+
# back (e.g. all-null), leave the inferred dtype.
|
|
980
|
+
pass
|
|
981
|
+
return df
|
|
982
|
+
|
|
946
983
|
|
|
947
984
|
# ---------------------------------------------------------------------------
|
|
948
985
|
# Fallback predicate (DESIGN §13.2 + S4)
|
|
@@ -50,9 +50,9 @@ def test_init_is_idempotent(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) ->
|
|
|
50
50
|
def test_apply_core_migrations_records_version(fresh_db: Database) -> None:
|
|
51
51
|
"""Framework owns no business tables."""
|
|
52
52
|
applied = apply_core_migrations(fresh_db)
|
|
53
|
-
assert applied == ["20260509_001"]
|
|
53
|
+
assert applied == ["20260509_001", "20260512_001"]
|
|
54
54
|
rows = fresh_db.fetchall("SELECT version FROM schema_migrations ORDER BY version")
|
|
55
|
-
assert tuple(rows) == (("20260509_001",),)
|
|
55
|
+
assert tuple(rows) == (("20260509_001",), ("20260512_001",))
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
def test_apply_core_migrations_skips_applied_versions(fresh_db: Database) -> None:
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
import warnings
|
|
8
9
|
from datetime import datetime, timedelta
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
|
|
@@ -452,3 +453,36 @@ def test_cache_hit_requires_payload_present(
|
|
|
452
453
|
client.call("limit_list_d", trade_date="20260427")
|
|
453
454
|
# Should hit transport (because no payload despite state=ok)
|
|
454
455
|
assert len(transport.calls) == 1
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
# ---------------------------------------------------------------------------
|
|
459
|
+
# Cache payload round-trip — dtypes preserved, no pandas FutureWarning
|
|
460
|
+
# ---------------------------------------------------------------------------
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def test_cache_payload_round_trip_preserves_dtypes(client: TushareClient) -> None:
|
|
464
|
+
"""v0.4.1 wrapper format restores dtypes explicitly so plugins don't see
|
|
465
|
+
pandas' read_json date-heuristic warnings on tushare-style date columns."""
|
|
466
|
+
df = pd.DataFrame(
|
|
467
|
+
{
|
|
468
|
+
"trade_date": ["20260427", "20260428"], # YYYYMMDD strings — triggers heuristic
|
|
469
|
+
"ts_code": ["000001.SZ", "000002.SZ"],
|
|
470
|
+
"close": [12.5, 14.7],
|
|
471
|
+
"vol": [1000, 2000],
|
|
472
|
+
"is_st": [True, False],
|
|
473
|
+
"ann_dt": pd.to_datetime(["2026-04-27", "2026-04-28"]), # real datetime64[ns]
|
|
474
|
+
}
|
|
475
|
+
)
|
|
476
|
+
api, td, params = "limit_list_d", "20260427", {"trade_date": "20260427"}
|
|
477
|
+
|
|
478
|
+
client._write_cached(api, td, params, df) # noqa: SLF001 — exercising internal contract
|
|
479
|
+
|
|
480
|
+
with warnings.catch_warnings():
|
|
481
|
+
warnings.simplefilter("error", FutureWarning)
|
|
482
|
+
out = client._read_cached(api, td, params, fields=None) # noqa: SLF001
|
|
483
|
+
|
|
484
|
+
assert list(out.columns) == list(df.columns)
|
|
485
|
+
for col in df.columns:
|
|
486
|
+
assert out[col].dtype == df[col].dtype, f"dtype drift on {col}: {out[col].dtype}"
|
|
487
|
+
assert out["trade_date"].tolist() == ["20260427", "20260428"]
|
|
488
|
+
assert (out["ann_dt"] == df["ann_dt"]).all()
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{deeptrade_quant-0.4.0 → deeptrade_quant-0.4.1}/deeptrade/core/migrations/core/20260509_001_init.sql
RENAMED
|
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
|
|
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
|
|
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
|