deeptrade-quant 0.4.1__tar.gz → 0.5.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.
- deeptrade_quant-0.5.0/CHANGELOG.md +876 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/PKG-INFO +27 -6
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/README.md +26 -5
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/__init__.py +1 -1
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/cli.py +46 -25
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/cli_config.py +66 -61
- deeptrade_quant-0.5.0/deeptrade/cli_data.py +33 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/cli_plugin.py +60 -44
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/config.py +9 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/db.py +24 -1
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/dep_installer.py +2 -1
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/github_fetch.py +1 -1
- deeptrade_quant-0.5.0/deeptrade/core/migrations/core/20260515_002_affected_tables.sql +13 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/plugin_manager.py +265 -87
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/plugin_source.py +1 -1
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/registry.py +1 -1
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/plugins_api/__init__.py +4 -0
- deeptrade_quant-0.5.0/deeptrade/plugins_api/errors.py +45 -0
- deeptrade_quant-0.5.0/deeptrade/plugins_api/metadata.py +248 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/pyproject.toml +5 -2
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/cli/test_config_cmd.py +24 -1
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/cli/test_plugin_cmd.py +44 -3
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/cli/test_routing.py +11 -7
- deeptrade_quant-0.5.0/tests/cli/test_user_facing_strings_are_chinese.py +120 -0
- deeptrade_quant-0.5.0/tests/core/test_db.py +252 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_plugin_dependencies.py +6 -3
- deeptrade_quant-0.5.0/tests/core/test_plugin_security.py +640 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_plugin_upgrade.py +5 -3
- deeptrade_quant-0.5.0/tests/plugins_api/test_errors.py +77 -0
- deeptrade_quant-0.4.1/deeptrade/cli_data.py +0 -33
- deeptrade_quant-0.4.1/deeptrade/plugins_api/metadata.py +0 -113
- deeptrade_quant-0.4.1/tests/core/test_db.py +0 -127
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/.gitignore +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/LICENSE +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/__init__.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/config_migrations.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/llm_client.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/llm_manager.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/logging_config.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/migrations/__init__.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/migrations/core/20260509_001_init.sql +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/migrations/core/20260512_001_drop_legacy_tushare_cache.sql +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/migrations/core/__init__.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/paths.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/run_status.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/secrets.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/core/tushare_client.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/plugins_api/base.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/plugins_api/events.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/plugins_api/llm.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/deeptrade/theme.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/__init__.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/cli/__init__.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/conftest.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/__init__.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_config.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_config_migrations.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_github_fetch.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_llm_client.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_llm_manager.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_paths.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_plugin_install.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_plugin_source.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_registry.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_secrets.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_tushare_classifier.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_tushare_client.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/core/test_tushare_retry_r1.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/plugins_api/__init__.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/plugins_api/test_protocol.py +0 -0
- {deeptrade_quant-0.4.1 → deeptrade_quant-0.5.0}/tests/test_smoke.py +0 -0
|
@@ -0,0 +1,876 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to DeepTrade. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and SemVer.
|
|
4
|
+
|
|
5
|
+
## [v0.5.0] — 2026-05-15 — 插件信任边界 + CLI 中文化
|
|
6
|
+
|
|
7
|
+
本版主线落地 2026-05-15 评审研判文档 `DeepTrade-review-assessment-and-fix-plan-2026-05-15.md` 中的 v0.5 路线:把"插件可以声明并清掉框架核心表"这条被忽视的信任边界明确收口,让框架的 SQL 迁移有边界、插件加载有 sys.modules 守卫、升级路径不会被静默改动的历史 migration 蒙混过关;同时把命令行用户向文案统一为中文,建立首道防回退的回归测试。整体不引入新框架概念(拒绝了报告里的 RuntimeContext 大对象、独立 venv、SQL DSL 等重型方案),保持"框架轻、插件重"的原则。
|
|
8
|
+
|
|
9
|
+
实现按 5 个 PR 切片推进;既有插件不需要任何源码改动,但 v0.6 会把 v0.5 用 DeprecationWarning 提示的 `table_prefix` 缺省派生策略升级为硬失败——本版起插件作者应开始把声明的表名收敛到 `<plugin_id 派生前缀>_*` 命名空间。
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `deeptrade/plugins_api/metadata.py`:新增公开常量 `RESERVED_TABLE_NAMES`(10 个框架自有表)与 `RESERVED_TOP_PACKAGES`(15 个框架 / 公共依赖的顶层包名)。`PluginMetadata` 追加 4 个 `model_validator`:
|
|
14
|
+
- `_tables_not_reserved`:拒绝声明任何 `RESERVED_TABLE_NAMES` 中的表(H1)。
|
|
15
|
+
- `_tables_match_prefix`:`table_prefix` 显式声明则全表强制匹配;省略时按 `plugin_id.replace('-','_') + '_'` 派生并对不匹配的表发 `DeprecationWarning`。v0.6 转硬失败。
|
|
16
|
+
- `_entrypoint_top_pkg_matches_plugin_id`:entrypoint 顶层包名必须等于 `plugin_id.replace('-','_')`,且不在 `RESERVED_TOP_PACKAGES` 中(H3 / T04)。
|
|
17
|
+
- 新增 `table_prefix: str | None`(pattern `^[a-z][a-z0-9_]{0,15}_$`)字段。
|
|
18
|
+
- `deeptrade/core/migrations/core/20260515_002_affected_tables.sql`:给 `plugin_schema_migrations` 加 `affected_tables TEXT DEFAULT NULL` 列,由框架在每条 migration 应用后写入实际新增的表名(JSON 数组)。purge 路径优先按此列反查归属,`plugin_tables` 仅作为兼容老库的兜底(H2 / T03)。
|
|
19
|
+
- `deeptrade/core/plugin_manager.py::_apply_migrations`:重写为按每条 migration 做 schema-diff(`information_schema.tables` 前后快照)+ 3 道边界检查:
|
|
20
|
+
- 新增表必须在 `meta.tables` 声明集合内(创建未声明表 → `PluginInstallError`);
|
|
21
|
+
- 删除表不可命中 `RESERVED_TABLE_NAMES`(DROP 核心表 → `PluginInstallError`);
|
|
22
|
+
- 删除表必须当前归属本插件(`plugin_tables` 反查)。
|
|
23
|
+
- `deeptrade/core/plugin_manager.py::_load_entrypoint`:新增 `sys.modules["deeptrade"]` 守卫(T07)—— import 前后比较框架模块的对象身份与 `__file__`,被替换则立即还原并 `PluginInstallError`;新增 `isinstance(instance, Plugin)` 校验(T08),缺 `dispatch` / `validate_static` / `metadata` 任一属性即拒绝。
|
|
24
|
+
- `deeptrade/core/plugin_manager.py::upgrade`:在跑任何迁移前先比对 `(version, checksum)`——若 metadata 中某条 migration 的 version 已在 DB 中但 checksum 不一致,直接 `PluginInstallError("checksum changed ... historical migrations are immutable")`(T09 / M4)。
|
|
25
|
+
- `deeptrade/core/plugin_manager.py::_purge_plugin_tables`:从 `uninstall(purge=True)` 中拆出独立方法。`affected_tables` 全部非空则按其与 `plugin_tables(purge=True)` 取交集;任一行为 NULL(v0.4 老库)则退化到全 `plugin_tables`;无论走哪条路径都在 DROP 前再次拦截 `RESERVED_TABLE_NAMES` 并 `logger.error` 留痕(H1 / T06)。
|
|
26
|
+
- `deeptrade/cli_plugin.py::cmd_enable`:补 `PluginInstallError` 捕获分支(T11),把 manager 给出的 "install_path missing, reinstall before enabling" 提示透传,`Exit(2)`。
|
|
27
|
+
- `deeptrade/cli_config.py::cmd_show`:表格之后追加黄色 `⚠ N 个 secret 以明文存储` 提示(T13)。
|
|
28
|
+
- `deeptrade/core/config.py::ConfigService.count_plaintext_secrets()`:上述 CLI 警告的支撑方法,封装 `SecretStore.list_records()` 过滤,避免 CLI 触私有属性。
|
|
29
|
+
- `tests/core/test_plugin_security.py`:v0.5 trust-boundary 全套负向回归,38 个用例覆盖 RESERVED 集合、`table_prefix` warn/hard、entrypoint 顶层包约束(参数化跑 14 个保留包名)、schema-diff 拒创建未声明表、拒 DROP 核心表、purge 即便 plugin_tables 被污染仍跳过核心表、Plugin Protocol 缺 dispatch 拒装、upgrade 拒收已篡改的历史 migration。
|
|
30
|
+
- `tests/cli/test_user_facing_strings_are_chinese.py`:T16 中文化回归锁,11 个用例覆盖 `--help / config show / config show --help / plugin list / plugin install --help` 的中文关键字断言 + 6 条参数化反向断言(钉死被替换掉的英文短语)。
|
|
31
|
+
- 新增覆盖:`tests/cli/test_plugin_cmd.py::test_enable_missing_install_path_message`(T24)、`tests/cli/test_config_cmd.py::test_config_show_warns_plaintext_secrets` 与 `_no_warning_when_no_secrets_set`(T26)。
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- `deeptrade/core/plugin_manager.py`:删除自实现的 `_iter_statements` SQL 多语句分割器,改为 `self._db.execute(sql)` 直接喂入整段——DuckDB 原生支持 `;` 分隔多语句执行(T10)。
|
|
36
|
+
- `deeptrade/cli.py` / `cli_config.py` / `cli_plugin.py` / `cli_data.py` + `plugin_manager.summarize_for_install`:用户向文案全面中文化(T15)。范围限定为 `typer.echo` / Rich Table 标题与列名 / `questionary.*` prompt / `typer.Option/Argument` 的 `help` / `@app.command` docstring 首行 / `✘ / ✔ / ⚠` 句子;明确**保留英文**:`raise XxxError(...)` 消息、`logger.*`、源码注释 + docstring 第二行及之后、命令名本身——保证异常 / 日志 / stack trace 在英文社区仍可搜可贴。
|
|
37
|
+
- `deeptrade/cli_plugin.py::cmd_uninstall`:docstring + `--purge` option help 重写为中文,准确描述实际两段语义(默认 = 删文件 + disable,可重装恢复;`--purge` = 同时 DROP 表与所有 metadata 行,不可恢复)。实际行为不动。
|
|
38
|
+
- `pyproject.toml::[tool.hatch.build.targets.sdist].include`:删除不存在的 `/DESIGN.md`、`/PLAN.md`(v0.2.0 删除的 docs 残留),改为收录 `/CHANGELOG.md`(T14)。
|
|
39
|
+
- `deeptrade/{cli_data, core/github_fetch, core/dep_installer, core/plugin_source, core/registry}.py` 五处模块 docstring 中的 `docs/*_design.md` 失效引用全部改指 `CHANGELOG.md` 对应版本段(项目 docs 目录已在 v0.2.0 删除,不予恢复)。
|
|
40
|
+
- `README.md` 保留 IDs 段落补一句:`db` 长度未达 plugin_id 正则下限(`^[a-z][a-z0-9-]{2,31}$`,至少 3 字符),由正则隐式拒绝;其余四个由 `RESERVED_PLUGIN_IDS` 在安装时显式拦截。
|
|
41
|
+
|
|
42
|
+
### Migration notes
|
|
43
|
+
|
|
44
|
+
- **插件作者侧零强制动作**:本版只对边界违例发 `DeprecationWarning`(仅 `table_prefix` 派生不匹配这一项)。v0.6 会把它转硬失败,因此官方插件 + 第三方插件**建议**在下个版本前把表名收敛到 `<plugin_id 派生前缀>_*` 命名空间(如 `limit-up-board` → 派生 `limit_up_board_`,所有表以此开头),或在 `deeptrade_plugin.yaml` 顶层显式声明 `table_prefix: <prefix>_`。
|
|
45
|
+
- **v0.4 已装插件平滑升级**:`plugin_schema_migrations.affected_tables` 是新列,老插件历史行将以 NULL 存在;`uninstall --purge` 检测到 NULL 自动退化到 v0.4 的 `plugin_tables` 路径,行为不变。重新跑一次 install/upgrade 会让新写入的 migration 行带上 `affected_tables`,自动进入更严格的路径。
|
|
46
|
+
- **DB 自动迁移**:v0.4.2 起 `Database()` 已默认 `auto_migrate=True`,本版新增的 `20260515_002_affected_tables.sql` 在用户下次执行任意 `deeptrade <...>` 命令时静默应用,无需手动 `db upgrade`。设置 `DEEPTRADE_SKIP_AUTO_MIGRATE=1` 仍是逃生通道。
|
|
47
|
+
- **CLI 输出语言切换**:异常 / 日志 / `--help` 中 Typer 自动生成的 `Usage:` / `Options:` 头仍是英文(Typer 行为,不打算改),其余命令输出全部中文化。监控 / 自动化脚本如有按英文短语 grep 输出的,需要按 `tests/cli/test_user_facing_strings_are_chinese.py` 中的中文关键字相应更新。
|
|
48
|
+
|
|
49
|
+
## [v0.4.2] — 2026-05-12 — Database 构造自动迁移 + DEEPTRADE_DEBUG=1 显示堆栈
|
|
50
|
+
|
|
51
|
+
定位到一个用户报错:升级到 0.4.1 后第一次跑 `deeptrade limit-up-board lgb train ...` 直接报 `✘ TypeError: list indices must be integers or slices, not str`。链路追到 `TushareClient._restore_cached_frame` —— 0.4.1 改用 `{"version","schema","data"}` 包裹格式读取,但旧裸数组的缓存行没被清掉。根因不是缓存代码本身,而是迁移触发链路:`apply_core_migrations` 只在 `deeptrade db init` / `db upgrade` 显式调用,包升级后用户不手动跑就一直拿不到 `20260512_001_drop_legacy_tushare_cache.sql` 的清空动作。同时报错只有一行无堆栈,定位也很慢——这是双层 swallow(插件 + 框架都没渲染 traceback)。
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- `deeptrade/core/db.py::Database.__init__` 新增 `auto_migrate: bool = True` kwarg;构造时若未关闭(且 `DEEPTRADE_SKIP_AUTO_MIGRATE` 未置位)就自动调一次 `apply_core_migrations(self)`。任何打开 DB 的代码路径(CLI 插件分发、PluginManager、未来的 SDK 调用)都因此跟进 schema,包升级后无需手动 `db upgrade` 即可避开 0.4.1 那种"新读路径碰旧数据"的尴尬。
|
|
56
|
+
- `deeptrade/cli.py` 的 `init` / `db init` / `db upgrade` 显式传 `auto_migrate=False`,自己调 `apply_core_migrations` 收集"本次新跑了哪些版本"以打印 `✔ Schema applied: <version>` 行;其他命令走默认 auto-migrate 路径。
|
|
57
|
+
- `deeptrade/cli.py::_dispatch` 给插件 `dispatch()` 包了一层兜底:捕获 `BaseException`(放过 `SystemExit` / `KeyboardInterrupt`)后用 `render_exception` 渲染、按 `typer.Exit(1)` 传播。已经自己 catch 的插件不受影响;不 catch 的插件也能在框架层看到一致的输出格式。
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- `deeptrade/plugins_api/errors.py`:`render_exception(exc, *, header_glyph="✘")` 与 `debug_enabled()`。约定环境变量 `DEEPTRADE_DEBUG=1`(接受 `1` / `true` / `yes` / `on`,大小写不敏感)——开启时输出完整 `traceback.format_exception` 文本(自动包含 `__cause__` 链与 exception group),否则维持 `✘ {ExcType}: {msg}` 一行。两者均不带尾部换行,调用方自加。从 `plugins_api/__init__.py` 重导出。
|
|
62
|
+
- 逃生通道 `DEEPTRADE_SKIP_AUTO_MIGRATE=1`:当一次失败的迁移把所有 CLI 命令都堵住时,置位该变量绕过 auto-migrate 还原现场。文档位于 README troubleshooting 段(非主流程)。
|
|
63
|
+
- `tests/core/test_db.py`:5 个新用例覆盖 auto-migrate 行为——新建 DB 自动跑迁移、`auto_migrate=False` 跳过、`DEEPTRADE_SKIP_AUTO_MIGRATE=1` 跳过、迁移失败硬上抛、**预置旧裸数组 `tushare_cache_blob` 行 → 再开 DB 必须被 0.4.1 的 drop 迁移清掉**(本次故事的回归锁)。
|
|
64
|
+
- `tests/plugins_api/test_errors.py`:默认模式一行、`DEEPTRADE_DEBUG=1` 含 `Traceback` + 链式 `__cause__`、自定义 glyph、`0` 视为关闭、`debug_enabled` env 反映。
|
|
65
|
+
|
|
66
|
+
### Migration notes
|
|
67
|
+
|
|
68
|
+
- **用户侧零动作**:从 0.4.1 升上来的用户下一次跑任何 `deeptrade <...>` 命令时,`Database()` 都会自动应用 `20260512_001_drop_legacy_tushare_cache.sql`,TypeError 自愈。仍想留旧缓存(不推荐)的用户可用 `DEEPTRADE_SKIP_AUTO_MIGRATE=1 deeptrade ...` 临时绕过。
|
|
69
|
+
- **插件作者**:仍可保留自己的 `except Exception` 兜底;推荐改用 `from deeptrade.plugins_api import render_exception`,写成 `sys.stderr.write(render_exception(e) + "\n")` 即可让 `DEEPTRADE_DEBUG=1` 透传栈信息。`api_version` 不动("1");旧插件不调新工具也照常工作。
|
|
70
|
+
- **测试夹具变更**:`tests/core/test_db.py::fresh_db` 改用 `auto_migrate=False` 以保留"未迁移"语义;其余 `apply_core_migrations(fresh_db)` 用例无须改动(幂等)。
|
|
71
|
+
|
|
72
|
+
## [v0.4.1] — 2026-05-12 — Tushare 缓存 dtype 还原 + 消除 pandas FutureWarning
|
|
73
|
+
|
|
74
|
+
插件在缓存命中路径上反馈 pandas 2.2+ 抛出 `FutureWarning: The behavior of 'to_datetime' with 'unit' when parsing strings is deprecated`。根因在框架侧 `TushareClient._read_cached`:`pd.read_json(..., orient="records")` 默认 `convert_dates=True`,按列名启发式(含 `date / _at / _time / timestamp / modified`)调 `pd.to_datetime(values, errors='ignore', unit='ms')`,tushare 列名几乎全部命中,每次缓存读取都会触发警告;更危险的是 pandas 未来版本会静默改变 string+`unit` 的语义。
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
|
|
78
|
+
- `deeptrade/core/tushare_client.py::TushareClient._write_cached` / `_read_cached` 改用 `{"version":1, "schema":{col:dtype_str}, "data":[...records...]}` 的包裹格式存取缓存;读取走 `json.loads + DataFrame.from_records + _restore_cached_frame`,按 schema 显式还原 dtype(`datetime64[*]` → `pd.to_datetime`;非 `object` 数值/布尔列 → `astype`;`object` 跳过)。彻底绕开 `pd.read_json` 的列名启发式,根因消除。
|
|
79
|
+
- 框架不引入任何 tushare 业务字段名清单——`schema` 字典从 `df.dtypes.astype(str).to_dict()` 自动派生,保持"框架不持有业务知识"的原则。
|
|
80
|
+
- 依赖瘦身:删除 `import io`(旧实现唯一用途)。
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
|
|
84
|
+
- `deeptrade/core/migrations/core/20260512_001_drop_legacy_tushare_cache.sql`:升级时一次性 `DROP TABLE IF EXISTS tushare_cache_blob`;旧裸数组格式的缓存条目与新读取路径不兼容,惰性建表 `_ensure_cache_table` 会在下次写入时重建。
|
|
85
|
+
- `tests/core/test_tushare_client.py::test_cache_payload_round_trip_preserves_dtypes`:以 `trade_date(YYYYMMDD字符串)` + `ts_code(object)` + `close(float64)` + `vol(int64)` + `is_st(bool)` + `ann_dt(datetime64[ns])` 全字段 round-trip,`warnings.simplefilter("error", FutureWarning)` 把 warning 当错误抓,锁住回归。
|
|
86
|
+
|
|
87
|
+
### Migration notes
|
|
88
|
+
|
|
89
|
+
- **缓存清空一次**:升级到 0.4.1 后第一次 `apply_core_migrations` 会执行 `DROP TABLE IF EXISTS tushare_cache_blob`;下一次按 `trade_date` 拉取的 API 会走一次远程,之后正常命中新格式缓存。`tushare_sync_state` 不受影响(仍记录 status=ok),但 `_cache_hit` 在缓存表不存在时已会判为 miss,行为安全。
|
|
90
|
+
- **插件无需改动**:`TushareClient.call(...)` 返回的 DataFrame dtype 与首次远程拉取时一致——之前依赖 `.dt` 访问器或显式 dtype 的插件代码无须调整。
|
|
91
|
+
|
|
92
|
+
## [v0.4.0] — 2026-05-12 — 插件级依赖管理 + 框架依赖瘦身
|
|
93
|
+
|
|
94
|
+
两项主线变更合并发布:
|
|
95
|
+
|
|
96
|
+
1. **插件可以声明并由框架自动安装自己的 Python 依赖**。`deeptrade_plugin.yaml::dependencies` 接受 PEP 508 specifier;`plugin install / upgrade` 期间走 `uv pip install` → `python -m pip install` 两级回退,已装且满足跳过,不满足硬拒绝并归因到具体冲突方。设计文档:`docs/DeepTrade/plugin_dependency_management_design.md`(仓库外)。
|
|
97
|
+
2. **框架主依赖瘦身**:`pandas` / `tushare` 不再是 framework 的 `Requires-Dist`,仅作为 `optional-dependencies.plugin-runtime` / `dev` extras 存在。`deeptrade.core.tushare_client.TushareClient` 这一专为插件准备的 wrapper 行为不变,但其运行时依赖现在由插件自己声明。受影响的插件清单与改造指引见 `docs/DeepTrade/plugin_required_dependencies.md`(仓库外)。
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
- `PluginMetadata.dependencies: list[str]`(PEP 508 specifier;`extra="forbid"` 通过默认空列表向后兼容旧 yaml);Pydantic 校验拒绝非法 spec、VCS/URL 形式、重名包(大小写无关)。
|
|
102
|
+
- `deeptrade/core/dep_installer.py`:`parse_specs / plan_install / detect_installer / run_install`;installer 探测优先 `uv`(带 `--python <sys.executable>` 强制装入框架解释器)→ 回退 `python -m pip`;环境变量 `DEEPTRADE_DEP_INSTALL_TIMEOUT` 覆盖默认 300s 超时;marker 不匹配的 requirement 自动跳过。
|
|
103
|
+
- `PluginManager._handle_dependencies` / `_build_dep_ownership`:在 `install` / `upgrade` 的 `copytree` 之后、migrations 事务之前解析并安装 deps;冲突错误信息归因到"框架核心依赖"或具体 `plugin <id>`(反查既装插件的 `metadata_yaml`)。
|
|
104
|
+
- `plugin install` / `plugin upgrade` CLI 新增 `--no-deps`、`--reinstall-deps`;`summarize_for_install` 增 `dependencies` 行;`--no-deps` 启用时摘要区会显式提示。
|
|
105
|
+
- `pyproject.toml::optional-dependencies.plugin-runtime` extras:把 `tushare>=1.4` / `pandas>=2.2` 显式收集到这个组,便于本地通过 `uv sync --extra plugin-runtime` 调试 TushareClient。
|
|
106
|
+
- `tests/core/test_plugin_dependencies.py`:29 个用例覆盖元数据校验、planning、installer 探测、PluginManager 集成(安装 / 跳过 / 冲突 / 失败清理 / `--no-deps` / `--reinstall-deps` / 归因到其他插件)、upgrade 行为、CLI 摘要 + flag 透传。
|
|
107
|
+
|
|
108
|
+
### Changed
|
|
109
|
+
|
|
110
|
+
- `pyproject.toml::dependencies`:移除 `tushare>=1.4` 与 `pandas>=2.2`;其余 11 条框架直接使用的依赖(typer / questionary / rich / duckdb / openai / pydantic / tenacity / pyyaml / keyring / click / packaging)保持不变。
|
|
111
|
+
- `pyproject.toml::optional-dependencies.dev`:补入 `tushare>=1.4` / `pandas>=2.2`,使 `uv sync --all-extras` 仍能跑通 `tests/core/test_tushare_client.py` 等触及 wrapper 的测试。
|
|
112
|
+
- `deeptrade/__init__.py::__version__` → `0.4.0`;`pyproject.toml::project.version` → `0.4.0`(两处同步更新,遵 CLAUDE.md 发版流程)。
|
|
113
|
+
|
|
114
|
+
### Migration notes
|
|
115
|
+
|
|
116
|
+
- **框架瘦身的兼容性边界**:升级到 0.4.0 框架但**沿用旧版本插件**的用户,如果之前是用 `pip install deeptrade-quant` 一并装下了 pandas / tushare,环境里这两个库仍在,旧插件依然能跑。但**重新部署 / 新建虚拟环境**时,如果插件 yaml 没声明 `pandas` / `tushare`,运行期会以 `ModuleNotFoundError` 暴露——这就是新的"插件应该自己声明依赖"的预期行为。
|
|
117
|
+
- **官方插件改造**:`DeepTradePluginOfficial` 仓中的 `limit_up_board` / `volume_anomaly` 等策略需要把 `pandas>=2.2` / `tushare>=1.4` 写进自家 `deeptrade_plugin.yaml::dependencies`。具体指引见 `plugin_required_dependencies.md`。
|
|
118
|
+
- **离线 / 私有源用户**:本版本不支持自定义 `index_url`;如需禁用网络安装,用 `deeptrade plugin install <source> --no-deps` 跳过 dep 安装步骤,自行 `pip install` 准备好环境。
|
|
119
|
+
- **回滚不卸 deps**:dep 安装成功但后续 migrations / `validate_static` 失败时,已装的依赖**不会被反向卸载**——共享依赖风险下"装哪些卸哪些"会误伤其他插件。设计文档 §4.5 有详细说明。
|
|
120
|
+
|
|
121
|
+
### Out of scope (recorded for follow-up)
|
|
122
|
+
|
|
123
|
+
- `deeptrade.core.tushare_client` 仍位于框架代码树;下一步可考虑把它整体迁出框架(独立 PyPI 包或新的 `type=service` 插件类型),届时框架 wheel 完全不带 tushare 相关代码。
|
|
124
|
+
- 不引入 `requirements.lock` 风格的依赖锁定;当前每次 install 都按 specifier 实时解析。
|
|
125
|
+
- 不引入企业级 `index_url` / 私有 PyPI 源;v1 直接走 PyPI 默认源。
|
|
126
|
+
|
|
127
|
+
## [v0.3.1] — 2026-05-11 — Tushare 传输层韧性修复
|
|
128
|
+
|
|
129
|
+
`TushareSDKTransport.call` 用字符串关键字判断异常类型,长跑训练下 httpx 的 `RemoteProtocolError("Response ended prematurely")` 等传输层瞬断错误被错归为不可重试的 `TushareError`,绕过了 tenacity 重试白名单和 5xx → 缓存兜底链路,导致打板策略 lightgbm 训练(单轮 3000+ 次 Tushare 调用)频繁因网络抖动终止。本版本重写分类器并扩展重试策略。设计文档见 `docs/DeepTrade/tushare_transport_resilience_plan.md`(仓库外)。
|
|
130
|
+
|
|
131
|
+
### Added
|
|
132
|
+
|
|
133
|
+
- `TushareTransportError`(`TushareServerError` 的子类):传输层瞬态错误的专门异常类型;自动复用现有重试白名单与 5xx 缓存兜底路径,调用方零改动。
|
|
134
|
+
- `_classify_tushare_exception` / `_is_transient_transport_error` / `_extract_http_status` 三个模块级辅助函数,实现"按异常类型 → HTTP 状态码 → 字符串关键字"的三级分类,类型识别覆盖 httpx / requests / urllib3 / stdlib 三栈。
|
|
135
|
+
- `app_config.tushare_max_retries`(默认 7,范围 1-20,dotted key `tushare.max_retries`):tenacity stop_after_attempt 的最大尝试次数;从原来硬编码 5 提到 7。
|
|
136
|
+
- `TushareClient.__init__` 新增可选 `max_retries: int = 7` 参数。
|
|
137
|
+
- `tests/core/test_tushare_classifier.py`:分类器全套单测(37 个用例),含 "Response ended prematurely" 回归保护。
|
|
138
|
+
- `tests/core/test_tushare_retry_r1.py`:硬约束 R1 回归测试——每次 tenacity 重试都必须重新经过 `_TokenBucket.acquire()`;任何把 `bucket.acquire()` 移出 `_do_fetch` 的重构都会被这个测试拦下。
|
|
139
|
+
|
|
140
|
+
### Changed
|
|
141
|
+
|
|
142
|
+
- `TushareSDKTransport.call`:原 `except Exception` 中的字符串匹配块全部替换为对 `_classify_tushare_exception` 的调用;不再有 `"5" in msg[:3]` 这种 in 检查的 bug;未识别的异常默认归类为 `TushareTransportError`(可重试),而非历史的 `TushareError`(终态)——这是核心设计反转,意图是远程网络服务的"未知错"绝大多数是瞬态。
|
|
143
|
+
- `TushareClient._fetch_with_retries`:从 `@retry` 装饰器形式改为显式 `Retrying(...)( _do_fetch, ...)` 调用,便于通过 `__init__` 注入 `max_retries`;函数体拆为 `_do_fetch`,`self._bucket.acquire()` 仍是 `_do_fetch` 的第一行——见 `__init__` 中的 R1 注释。
|
|
144
|
+
- 重试退避策略:`wait_exponential(multiplier=1, min=1, max=15)` → `wait_exponential_jitter(initial=1, max=30, jitter=2)`,加 jitter 散开并发重试的"羊群效应";`stop_after_attempt(5)` → `stop_after_attempt(max_retries)`(默认 7),最坏等待预算从 ~30s 抬到 ~70s。
|
|
145
|
+
- Tushare 限流文案识别扩展:除 "频率"/"限流"/"rate"/"429" 外,新增对 "每分钟…次"(如 "抱歉,您每分钟最多访问该接口500次")的匹配。
|
|
146
|
+
- `tests/core/test_tushare_client.py` 中 4 处 `monkeypatch.setattr(TushareClient._fetch_with_retries.retry, "sleep", ...)` 改为 `monkeypatch.setattr(cli._retrying, "sleep", ...)`,配合 `Retrying` 实例化下放到 `__init__`。
|
|
147
|
+
- `deeptrade/__init__.py`:版本 bump 至 `0.3.1`。
|
|
148
|
+
|
|
149
|
+
### Out of scope (recorded for follow-up)
|
|
150
|
+
|
|
151
|
+
- `_TokenBucket` 只 decay 不 recover:撞过几次 429 后 rps 单调下降到 0.1,`TushareClient` 实例生命周期内不自愈。lub 训练每轮新建 client 所以单轮内不至于雪崩,但跨长跑场景需在后续版本治理。
|
|
152
|
+
- plugin 侧 `collect_training_window` 缺 per-day try/except、缺中间检查点——属插件韧性,不在框架范围。
|
|
153
|
+
|
|
154
|
+
## [v0.3.0] — 2026-05-11 — 移除 channel 插件类型 + 内置 notifier
|
|
155
|
+
|
|
156
|
+
本版本移除 `channel` 插件类型与框架内置的 notifier 链。IM 推送在实测中需要登录、轮询、发送多步流程,插件一次性 `dispatch(argv)` 的生命周期与之不匹配,整体能力将以框架级 ChatGateway 模块的形式重做(**本版本不含 ChatGateway 实现**,仅完成清理)。
|
|
157
|
+
|
|
158
|
+
### Removed
|
|
159
|
+
|
|
160
|
+
- `deeptrade/plugins_api/channel.py`(`ChannelPlugin` Protocol)
|
|
161
|
+
- `deeptrade/plugins_api/notify.py`(`NotificationPayload` / `NotificationSection` / `NotificationItem`)
|
|
162
|
+
- `deeptrade/core/notifier.py`(`NoopNotifier` / `MultiplexNotifier` / `AsyncDispatchNotifier` / `build_notifier` / `notify` / `notification_session`)
|
|
163
|
+
- `tests/core/test_notifier.py` / `tests/plugins_api/test_notify.py` 整文件
|
|
164
|
+
- `tests/plugins_api/test_protocol.py` 中 `ChannelPlugin` 相关 case
|
|
165
|
+
- `deeptrade.notify` / `deeptrade.notification_session` 顶层导出
|
|
166
|
+
|
|
167
|
+
### Changed
|
|
168
|
+
|
|
169
|
+
- `PluginMetadata.type` 收窄为 `Literal["strategy"]`(字段保留,便于未来扩展新 plugin 类型)。
|
|
170
|
+
- `apply_core_migrations` 新增 v0.3.0 数据迁移 `migrate_purge_non_strategy_plugins`:启动时清理 `plugins.type != 'strategy'` 的历史记录、`plugin_tables` / `plugin_schema_migrations` 关联行,并删除对应 install 目录。该迁移必须先于 `PluginManager.list_all()` 执行,否则旧 `channel` 行会触发 Pydantic 校验失败。
|
|
171
|
+
- `deeptrade/__init__.py`:版本 bump 至 `0.3.0`,移除 notify 顶层 re-export。
|
|
172
|
+
- README / CLAUDE.md:删除 channel / notify 段落与架构图相关条目;CLAUDE.md 新增"IM / notifications"短段说明能力暂缺、ChatGateway 是计划替代物。
|
|
173
|
+
- 官方 strategy 插件 `limit_up_board` / `volume_anomaly` 的 `runtime.py`:删除未被调用的 `notify()` / `is_notify_enabled()` 方法与 `NotificationPayload` import(这些方法在实际业务流中无任何调用点)。
|
|
174
|
+
|
|
175
|
+
### Migration notes
|
|
176
|
+
|
|
177
|
+
- **从 0.2.x 升级**:`pipx upgrade deeptrade-quant`。下次任何 `deeptrade ...` 命令首次落地时,`apply_core_migrations` 会自动清理 `stdout-channel` 等 `type=channel` 的历史插件记录及其 install 目录(行为等价于 `deeptrade plugin uninstall <id> --purge`),并在日志输出对应警告。
|
|
178
|
+
- 官方注册表(`DeepTradePluginOfficial`)的 `stdout-channel` 条目将在配套发版中下线,请勿再尝试安装。
|
|
179
|
+
- 业务流仍需要 IM 推送的用户:临时方案是在策略代码内直接 `import httpx` 自行调用 webhook;统一的 ChatGateway 能力将在后续版本提供。
|
|
180
|
+
|
|
181
|
+
## [v0.2.0] — 2026-05-09 — 框架瘦身(builtin 插件物理移除)· PR-8 cutover
|
|
182
|
+
|
|
183
|
+
本版本完成了框架与插件的物理解耦——`deeptrade-quant` wheel 不再携带任何插件代码。所有官方插件(`limit-up-board` / `volume-anomaly` / `stdout-channel`)必须通过 `deeptrade plugin install <短名>` 从注册表安装。
|
|
184
|
+
|
|
185
|
+
### Removed
|
|
186
|
+
|
|
187
|
+
- `deeptrade/strategies_builtin/` 整目录(含 `limit_up_board` / `volume_anomaly`)
|
|
188
|
+
- `deeptrade/channels_builtin/` 整目录(含 `stdout`)
|
|
189
|
+
- `tests/strategies_builtin/` 整目录(140 个测试随插件代码迁移到 `DeepTradePluginOfficial`)
|
|
190
|
+
- `mypy.ini` 中针对 builtin 的 `ignore_errors`(已无需要忽略的目标)
|
|
191
|
+
|
|
192
|
+
### Changed
|
|
193
|
+
|
|
194
|
+
- README.md / docs/quick-start.md / docs/plugin-development.md /
|
|
195
|
+
guide/plugin/strategy/volume-anomaly.md:命令示例从本地 builtin 路径切换为短名(`deeptrade plugin install limit-up-board` 等)。
|
|
196
|
+
- `deeptrade/core/notifier.py` docstring:移除 `channels_builtin/ mirrors strategies_builtin/` 的过期引用。
|
|
197
|
+
|
|
198
|
+
### Migration notes
|
|
199
|
+
|
|
200
|
+
- **从 0.1.x 升级**:`pipx upgrade deeptrade-quant`。已通过本地路径安装的 builtin 插件**不会**被升级删除(plugin 数据/代码独立于框架 wheel),可继续使用,也可执行 `deeptrade plugin upgrade <短名>` 切换到注册表版本。
|
|
201
|
+
- **历史快照可回溯**:`git checkout archive/with-builtin-plugins-v0.1.0-preview` 取回含 builtin 的最后状态。
|
|
202
|
+
- **回滚发版**:用户可 `pipx install deeptrade-quant==0.1.0` 锁定上一稳定版本。
|
|
203
|
+
|
|
204
|
+
### Wheel size impact
|
|
205
|
+
|
|
206
|
+
| 指标 | v0.1.0 | v0.2.0 |
|
|
207
|
+
|------|--------|--------|
|
|
208
|
+
| wheel 文件数 | 74 | 37 |
|
|
209
|
+
| Python 源文件(mypy 扫描) | 65 | 31 |
|
|
210
|
+
| 内置 .sql migration | 4 | 1(仅框架核心) |
|
|
211
|
+
|
|
212
|
+
## [v0.1.0] — 2026-05-09 — 框架与插件解耦 · PyPI 首个稳定发布(PR-1 ~ PR-6)
|
|
213
|
+
|
|
214
|
+
PyPI distribution name: **`deeptrade-quant`**(CLI 命令仍是 `deeptrade`)。
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pipx install deeptrade-quant # or: uv tool install deeptrade-quant
|
|
218
|
+
deeptrade plugin search # browse the official registry
|
|
219
|
+
deeptrade plugin install limit-up-board
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
参考 `docs/distribution-and-plugin-install-design.md`。本版本是 §10 中
|
|
223
|
+
PR-1~PR-6 的合集;builtin 插件目录仍随 wheel 一起发布(兼容旧的本地路径
|
|
224
|
+
安装),下一个版本(PR-8 cutover)将完成瘦身。
|
|
225
|
+
|
|
226
|
+
### Added
|
|
227
|
+
|
|
228
|
+
- **从 PyPI 分发**:项目元数据、LICENSE、GitHub Actions(`ci.yml` /
|
|
229
|
+
`release.yml`)就位;`tag v*` 触发 OIDC Trusted Publisher 直发 PyPI。
|
|
230
|
+
- **官方插件注册表客户端**(`deeptrade.core.registry`):从
|
|
231
|
+
`raw.githubusercontent.com/ty19880929/DeepTradePluginOfficial/main/registry/index.json`
|
|
232
|
+
拉取,ETag 缓存到 `~/.deeptrade/plugins/registry-cache.json`;网络故障
|
|
233
|
+
时回退到本地缓存。
|
|
234
|
+
- **GitHub tarball 拉取**(`deeptrade.core.github_fetch`):stdlib 实现
|
|
235
|
+
(`urllib` + `tarfile`),无第三方 HTTP 依赖;`GITHUB_TOKEN` 环境变量
|
|
236
|
+
自动用于 rate-limit / 私有仓库(未来扩展);解压做 path-traversal
|
|
237
|
+
防护。
|
|
238
|
+
- **来源解析层**(`deeptrade.core.plugin_source`):`SourceResolver` 把
|
|
239
|
+
用户输入(短名 / GitHub URL / 本地路径)统一解析为本地目录,强制
|
|
240
|
+
`min_framework_version` 校验。
|
|
241
|
+
- **新命令** `deeptrade plugin search [keyword] [--no-cache]`:列出注册
|
|
242
|
+
表中可用的插件;`--no-cache` 旁路 ETag 缓存。
|
|
243
|
+
- **`plugin info` 注册表 fallback**:未安装但在注册表中时显示注册表条
|
|
244
|
+
目 + 安装命令提示。
|
|
245
|
+
|
|
246
|
+
### Changed
|
|
247
|
+
|
|
248
|
+
- `deeptrade plugin install <SOURCE>` 现支持三种来源:短名(注册表)、
|
|
249
|
+
完整 git 仓库 URL、本地路径。判定顺序:本地目录存在 → git URL → 短
|
|
250
|
+
名。`--ref <tag|branch|sha>` 可指定具体 ref(默认拉该插件最新 release
|
|
251
|
+
tag)。
|
|
252
|
+
- `deeptrade plugin upgrade <SOURCE>` 同上,且加入 SemVer 比较:
|
|
253
|
+
- 待装 == 已装:`exit 0` + "已是最新版本 vX"
|
|
254
|
+
- 待装 > 已装:执行升级
|
|
255
|
+
- 待装 < 已装:`exit 2` + 提示先 `uninstall --purge`(**禁止降级**,
|
|
256
|
+
因为框架未建模 migration 回滚)
|
|
257
|
+
- `PluginManager.upgrade()` 返回类型改为 `InstalledPlugin | UpgradeNoop`。
|
|
258
|
+
|
|
259
|
+
### Fixed
|
|
260
|
+
|
|
261
|
+
- `pyproject.toml` 删除了与 `packages = ["deeptrade"]` 冲突的
|
|
262
|
+
`[tool.hatch.build.targets.wheel.force-include]` 段(曾导致 wheel 内
|
|
263
|
+
`deeptrade/core/migrations/` 路径 duplicate name,让 PyPI 上传失败)。
|
|
264
|
+
- `deeptrade.core.notifier`:channel 实例 cast 到 `ChannelPlugin`(mypy
|
|
265
|
+
narrowing 修复)。
|
|
266
|
+
- 一批预存在的 ruff lint(UP045 / F401 / I001 / W292 / B023)清理。
|
|
267
|
+
|
|
268
|
+
### Notes
|
|
269
|
+
|
|
270
|
+
- builtin 插件(`limit-up-board` / `volume-anomaly` / `stdout-channel`)
|
|
271
|
+
代码已迁移到 [`DeepTradePluginOfficial`](https://github.com/ty19880929/DeepTradePluginOfficial)
|
|
272
|
+
独立仓库,并发布了首个 release tag(`limit-up-board/v0.4.0` 等)。
|
|
273
|
+
本版本框架仍然包含旧 builtin 子树作为兼容期保留;下一版本(瘦身后)
|
|
274
|
+
将物理移除。
|
|
275
|
+
- archive tag `archive/with-builtin-plugins-v0.1.0-preview` 永久保留含
|
|
276
|
+
builtin 子树的最后状态,可随时 `git checkout` 取回。
|
|
277
|
+
|
|
278
|
+
## [volume-anomaly v0.6.0] — 2026-05-08 — 显式分维度评分(PR-6)
|
|
279
|
+
|
|
280
|
+
> 仅升级 volume-anomaly 插件版本(0.5.0 → 0.6.0);框架版本不变。
|
|
281
|
+
> 升级方式:`deeptrade plugin upgrade <plugin-source>`,会自动应用新增的
|
|
282
|
+
> migration(`20260601_002_dimension_scores.sql`)。
|
|
283
|
+
|
|
284
|
+
> ⚠ **Schema 变更**:`VATrendCandidate.dimension_scores` 现为**必填字段**;
|
|
285
|
+
> 旧 LLM 响应不再可解析(但持久化到 `va_stage_results.raw_response_json`
|
|
286
|
+
> 不受影响——升级前的历史数据完整保留可读,仅新写入要求新 schema)。
|
|
287
|
+
|
|
288
|
+
参考 `docs/volume_anomaly_wave2_design.md` § 2。
|
|
289
|
+
|
|
290
|
+
### Added (volume-anomaly plugin)
|
|
291
|
+
|
|
292
|
+
- **新子模型 `VADimensionScores`**:6 个维度(`washout` / `pattern` /
|
|
293
|
+
`capital` / `sector` / `historical` / `risk`)每个 0–100 整数评分,
|
|
294
|
+
其中 `risk` 为反向极性(高分 = 高风险),其余为正向极性。
|
|
295
|
+
- **`VATrendCandidate.dimension_scores` 必填**(F8):每只候选都必须输出
|
|
296
|
+
6 维评分,由 LLM 自洽与 `launch_score` 大致一致(F3 / F14 软约束,
|
|
297
|
+
不强制公式)。
|
|
298
|
+
- **Prompt 评分尺度锚定**:`VA_TREND_SYSTEM` 加入【dimension_scores 评分尺度】
|
|
299
|
+
段(0-30 / 30-60 / 60-80 / 80-100 四档语义),每个判断维度(A–F)末尾
|
|
300
|
+
显式标注对应 `dimension_scores.<name>`。
|
|
301
|
+
- **Few-Shot 示例同步**:`prompts_examples.py` A/B 示例补 `dimension_scores`
|
|
302
|
+
对象,与示例评分尺度一致。
|
|
303
|
+
- **拆 6 列持久化**(G6):`va_stage_results` 新增 `dim_washout` /
|
|
304
|
+
`dim_pattern` / `dim_capital` / `dim_sector` / `dim_historical` /
|
|
305
|
+
`dim_risk` 6 个 `DOUBLE` 列。`runner._write_stage_results` 写入
|
|
306
|
+
对应列;旧行 `dim_*` 全部为 `NULL`,stats SQL 自动过滤。
|
|
307
|
+
原始 JSON 仍写入 `raw_response_json` 作为审计备份。
|
|
308
|
+
- **stats `--by dimension_scores`**:新支持子选项,输出 6 个维度评分与
|
|
309
|
+
`ret_t3` 的 Pearson 相关系数(DuckDB `CORR(...)` 内置聚合)。
|
|
310
|
+
- **报告紧凑表达**:`write_analyze_report` 在 imminent_launch / watching
|
|
311
|
+
表中新增"W/P/C/S/H/R"列,紧凑展示 6 维分数(如 `80/75/70/75/60/25`)。
|
|
312
|
+
|
|
313
|
+
### Implementation notes
|
|
314
|
+
|
|
315
|
+
- 拆列而非单 JSON 列的理由(G6):`stats --by dimension_scores` 要求
|
|
316
|
+
6 维与 `ret_t3` 做 Pearson,`AVG`/`CORR` 在拆列模式下纯 SQL 表达;
|
|
317
|
+
`JSON_EXTRACT` 在 SQL 层昂贵且 DuckDB 跨版本兼容性差。
|
|
318
|
+
- 输出 token 预算上调:`DEFAULT_AVG_OUTPUT_TOKENS_PER_CANDIDATE`
|
|
319
|
+
900 → 1100(吸纳 6 维评分 + alpha 字段),`plan_batches` 按新预算切批。
|
|
320
|
+
- F13 决策:6 维(W/P/C/S/H/R),不加 liquidity。
|
|
321
|
+
|
|
322
|
+
### Breaking changes
|
|
323
|
+
|
|
324
|
+
- 旧 LLM 响应(无 `dimension_scores`)会在 Pydantic 解析阶段抛
|
|
325
|
+
`ValidationError`。已落地的批次再 retry 时需更新 prompt(已自动包含)。
|
|
326
|
+
- 回滚路径:revert PR-6 不需要 drop 列(`NULL` 列不影响旧逻辑),但
|
|
327
|
+
已经按新 schema 持久化的行将无法被旧版本读取——因此降级时**保留
|
|
328
|
+
`raw_response_json` 而忽略 `dimension_scores_json`**。
|
|
329
|
+
|
|
330
|
+
## [volume-anomaly v0.5.0] — 2026-05-08 — RPS / 大盘相对 alpha(PR-5)
|
|
331
|
+
|
|
332
|
+
> 仅升级 volume-anomaly 插件版本(0.4.0 → 0.5.0);框架版本不变。
|
|
333
|
+
> 升级方式:`deeptrade plugin upgrade <plugin-source>`。本版本不新增 migration。
|
|
334
|
+
|
|
335
|
+
参考 `docs/volume_anomaly_wave2_design.md` § 1。
|
|
336
|
+
|
|
337
|
+
### Added (volume-anomaly plugin)
|
|
338
|
+
|
|
339
|
+
- **沪深 300 alpha 字段**(P1-1):候选行新增 `alpha_5d_pct` / `alpha_20d_pct` /
|
|
340
|
+
`alpha_60d_pct`(个股相对沪深 300 同期累计收益差,单位 %)+ `baseline_index_code`
|
|
341
|
+
+ `rel_strength_label ∈ {leading, in_line, lagging}`(基于 alpha_20d_pct
|
|
342
|
+
±5% 分档)。让 LLM 在判断"放量异动"时区分跟随性反弹与抗跌强势。
|
|
343
|
+
- **新 tushare 权限 `index_daily`(optional)**:250d 拉一次沪深 300 基准
|
|
344
|
+
收盘价;G1—与个股 daily 长度对称、cache 友好。
|
|
345
|
+
- **维度 D 改名 / 扩写**:`VA_TREND_SYSTEM` 中维度 D 由"板块强度"改为
|
|
346
|
+
"**板块与市场相对强度**",提示 LLM 同时考虑板块层 (`sector_strength_*`)
|
|
347
|
+
与市场层 (`alpha_*_pct` / `rel_strength_label`)。
|
|
348
|
+
- **Few-Shot 示例补充 alpha 引用**:`prompts_examples.py` 的 A/B 示例
|
|
349
|
+
各加一条 `alpha_20d_pct` 引用,确保 anchoring 与新维度一致。
|
|
350
|
+
- **G8 显式降级提示**:`index_daily` 权限缺失或 fetch 失败时,runner emit
|
|
351
|
+
一条 `EventLevel.WARN` LOG 事件,明确提示用户"alpha 字段降级为 None;
|
|
352
|
+
如需启用 alpha,请确认 Tushare 账户已开通 index_daily 权限"。
|
|
353
|
+
|
|
354
|
+
### Behavior change
|
|
355
|
+
|
|
356
|
+
- analyze 阶段每次 run 多 1 次 `index_daily` 调用(cache 友好;首次冷拉
|
|
357
|
+
~5s),无新数据消费 SLA 影响。
|
|
358
|
+
- LLM 输入每候选 +30–40 tokens(5d/20d/60d alpha + label);
|
|
359
|
+
在 200K 输入预算下完全可接受。
|
|
360
|
+
|
|
361
|
+
### Implementation notes
|
|
362
|
+
|
|
363
|
+
- alpha 计算口径:`alpha_n = stock_pct_chg_n − baseline_pct_chg_n`(简单收益)。
|
|
364
|
+
- `rel_strength_label` 仅基于 `alpha_20d_pct`;alpha_20d 缺失时
|
|
365
|
+
`rel_strength_label = None`,但 `baseline_index_code` 永远输出(元数据)。
|
|
366
|
+
- F2 决策:行业相对 alpha 暂不做,留给波次 3 视效果再加。
|
|
367
|
+
|
|
368
|
+
## [volume-anomaly v0.4.0] — 2026-05-08 — T+N 自动回测闭环(PR-4)
|
|
369
|
+
|
|
370
|
+
> 仅升级 volume-anomaly 插件版本(0.3.0 → 0.4.0);框架版本不变。
|
|
371
|
+
> 升级方式:`deeptrade plugin upgrade <plugin-source>`,会自动应用新增的
|
|
372
|
+
> migration(`20260601_001_realized_returns.sql`)。
|
|
373
|
+
|
|
374
|
+
参考 `docs/volume_anomaly_wave2_design.md` § 3。
|
|
375
|
+
|
|
376
|
+
### Added (volume-anomaly plugin)
|
|
377
|
+
|
|
378
|
+
- **新表 `va_realized_returns`**:每条 hit 一行(PK=`anomaly_date+ts_code`),
|
|
379
|
+
存 T+1/T+3/T+5/T+10 收盘价、对应收益、5d/10d 窗口最大涨幅与回撤。
|
|
380
|
+
`data_status ∈ {pending, partial, complete}`(G5 严格三态:T+1 未到→pending;
|
|
381
|
+
max_horizon 未到 OR 任一 horizon 数据缺失→partial;max_horizon 已到且全填→complete)。
|
|
382
|
+
- **新子命令 `evaluate`**:
|
|
383
|
+
```
|
|
384
|
+
deeptrade volume-anomaly evaluate [--lookback-days N] [--trade-date YYYYMMDD]
|
|
385
|
+
[--backfill-all] [--force-recompute]
|
|
386
|
+
```
|
|
387
|
+
从 `va_anomaly_history` 全集(G3)里取 anomaly_date 在 lookback 内的 hits,
|
|
388
|
+
按 trade-day horizon 解析 T+1/T+3/T+5/T+10 实际 trade_date,复用
|
|
389
|
+
`_fetch_daily_history_by_date` 的 cache 拉取收盘价,UPSERT 进
|
|
390
|
+
`va_realized_returns`。`complete` 行默认跳过;`--force-recompute` 强制重算。
|
|
391
|
+
- **新子命令 `stats`**:
|
|
392
|
+
```
|
|
393
|
+
deeptrade volume-anomaly stats [--from] [--to] [--by prediction|pattern|launch_score_bin]
|
|
394
|
+
```
|
|
395
|
+
纯只读 SQL 聚合 `va_stage_results JOIN va_realized_returns`,输出每桶的
|
|
396
|
+
样本数 / T+3 平均收益 / T+3 胜率 / T+5 最大涨幅均。`launch_score_bin`
|
|
397
|
+
默认分箱 `0-40 / 40-60 / 60-80 / 80-100`(G4)。
|
|
398
|
+
- **`evaluate` 写 va_runs / va_events**(G10):`mode='evaluate'` 与
|
|
399
|
+
screen / analyze / prune 同级,可在 `history` 子命令中查看。
|
|
400
|
+
|
|
401
|
+
### Implementation notes
|
|
402
|
+
|
|
403
|
+
- 所有 horizon 时间换算用 `TradeCalendar.next_open` 跳过周末/节假日。
|
|
404
|
+
- `va_realized_returns.t_close` 冗余存(G9)——让 stats 在 va_anomaly_history
|
|
405
|
+
被异常清空时仍可读取。
|
|
406
|
+
- 不新增 tushare API 权限(仍只用 `daily`)。
|
|
407
|
+
- 不引入新插件级 config 表(F6);horizon 列表写死在 module-level
|
|
408
|
+
`EVALUATE_HORIZONS = (1, 3, 5, 10)`。
|
|
409
|
+
|
|
410
|
+
## [volume-anomaly v0.3.0] — 2026-05-08 — 波次 1(PR-1 + PR-2 + PR-3)
|
|
411
|
+
|
|
412
|
+
> 仅升级 volume-anomaly 插件版本(0.2.0 → 0.3.0);框架版本不变。
|
|
413
|
+
> 升级方式:`deeptrade plugin upgrade <plugin-source>`。本版本不新增 migration。
|
|
414
|
+
|
|
415
|
+
参考 `docs/volume_anomaly_wave1_design.md`(§ 1–5 共五项 P0 优化)。
|
|
416
|
+
|
|
417
|
+
### Added (volume-anomaly plugin)
|
|
418
|
+
|
|
419
|
+
**PR-1 — Screen 规则**
|
|
420
|
+
|
|
421
|
+
- **上影线过滤**(P0-1):T-day 规则后、换手率前新增一道过滤,
|
|
422
|
+
`upper_shadow_ratio = (high − max(open, close)) / range > upper_shadow_ratio_max`
|
|
423
|
+
的候选直接淘汰,避免"避雷针"型的纯上影 K 线进入候选。诊断字段
|
|
424
|
+
`n_after_upper_shadow` 暴露在漏斗中;hit 行新增 `upper_shadow_ratio` 字段。
|
|
425
|
+
- **按流通市值分桶的换手率阈值**(P0-2):用 `daily_basic.circ_mv` 把候选按
|
|
426
|
+
流通市值(亿元)切 4 档,每档使用独立的 `[turnover_min, turnover_max]`:
|
|
427
|
+
`≤50亿: [5, 15]` / `50–200亿: [3.5, 12]` / `200–1000亿: [2.5, 9]` /
|
|
428
|
+
`>1000亿: [1.5, 6]`。边界值归"较小桶"(E4)。`circ_mv` 缺失时退化到
|
|
429
|
+
全局 `turnover_min/max`,并写入 `data_unavailable`。诊断字段
|
|
430
|
+
`turnover_bucket_hits` / `n_missing_circ_mv` / `circ_mv_missing_codes`
|
|
431
|
+
暴露在 screen 报告中;hit 行新增 `circ_mv_yi` / `turnover_bucket` 字段。
|
|
432
|
+
|
|
433
|
+
**PR-2 — Analyze 候选行特征**
|
|
434
|
+
|
|
435
|
+
- **VCP 三维收敛指标**(P0-3):候选行新增
|
|
436
|
+
`atr_10d_pct` / `atr_10d_quantile_in_60d`(10 日 ATR 在近 60 日 ATR 序列中的分位数)/
|
|
437
|
+
`bbw_20d`(20 日 Bollinger 带宽)/ `bbw_compression_ratio`
|
|
438
|
+
(当前 BBW / 近 60 日 BBW 均值,<1 表示在收敛)。零新数据,复用现有
|
|
439
|
+
60d 历史窗口(实际从 250d 切片)。
|
|
440
|
+
- **120/250 日阻力位距离**(P0-4):`collect_analyze_bundle` 默认
|
|
441
|
+
`history_lookback` 由 60 → 250;`_build_candidate_row` 内部切片处理。
|
|
442
|
+
候选行新增 `high_120d` / `high_250d` / `low_120d` /
|
|
443
|
+
`dist_to_120d_high_pct` / `dist_to_250d_high_pct` /
|
|
444
|
+
`is_above_120d_high` / `is_above_250d_high` / `pos_in_120d_range`。
|
|
445
|
+
E3-A:仅 120d 区间位置,不补 `low_250d`。E7-C:任一窗口数据不足则
|
|
446
|
+
对应字段降级为 `None`,不阻塞主流程。
|
|
447
|
+
- **首次冷拉延迟**:扩展 250d 窗口的首次 fetch 约多 30–45 秒
|
|
448
|
+
(Tushare RPS=6 默认下),后续走 cache 零增量。
|
|
449
|
+
|
|
450
|
+
**PR-3 — LLM Prompt 对齐**
|
|
451
|
+
|
|
452
|
+
- **Few-Shot 锚定**(P0-5):新增 `prompts_examples.py::VA_TREND_FEWSHOT`,
|
|
453
|
+
在 `VA_TREND_SYSTEM` 末尾拼接两个示例(VCP 突破 / 高位上影诱多),
|
|
454
|
+
引导 LLM 在不同 provider 间保持一致的 `launch_score` 尺度。
|
|
455
|
+
- **维度 A 提示扩展**:在【判断维度】A 段中加入"整理期波动率收敛"指引,
|
|
456
|
+
显式提示 LLM 引用 `atr_10d_quantile_in_60d` / `bbw_compression_ratio`。
|
|
457
|
+
- **字段一致性单测**:`test_prompt_consistency.py` 用正则提取
|
|
458
|
+
示例中的 `"field": "<X>"`,断言每个 X 都属于
|
|
459
|
+
`_build_candidate_row` 输出键集合 OR screen hit 行字段集合。
|
|
460
|
+
防止后续字段重命名时 prompt 失配。
|
|
461
|
+
|
|
462
|
+
### Default behavior change (heads-up)
|
|
463
|
+
|
|
464
|
+
- 默认开启 **上影线过滤**:`ScreenRules.upper_shadow_ratio_max = 0.35`。
|
|
465
|
+
如需保持旧行为,在 `screen_rules` 配置中显式传 `"upper_shadow_ratio_max": null`。
|
|
466
|
+
- 默认开启 **分桶换手率**:`ScreenRules.turnover_buckets = DEFAULT_TURNOVER_BUCKETS`。
|
|
467
|
+
如需保持旧行为,在 `screen_rules` 配置中显式传 `"turnover_buckets": null`,
|
|
468
|
+
此时回退到 `[turnover_min, turnover_max]` 全局阈值。
|
|
469
|
+
- 默认 analyze 历史窗口由 60 → 250 个交易日(E2-A 单 fetch 复用)。
|
|
470
|
+
首次冷启动会多消耗 30–45s(RPS=6 默认),后续走 cache 零增量。
|
|
471
|
+
|
|
472
|
+
### Implementation notes
|
|
473
|
+
|
|
474
|
+
- `turnover_buckets` JSON 配置中最后一档可用 `null` 表示无穷上界,
|
|
475
|
+
`from_dict` 内转 `math.inf`。`as_dict()` 反向把 `math.inf` 序列化为 `null`,
|
|
476
|
+
使 `screen_stats.json` 仍是合规的标准 JSON(不依赖 `Infinity` 扩展)。
|
|
477
|
+
- 不动 framework;不新增 tushare API 权限(`circ_mv` 已包含在
|
|
478
|
+
`daily_basic` 字段中;250d 长窗口仍走 `daily`)。
|
|
479
|
+
- VCP 计算 `_compute_atr_series` 用简单平均 TR(与 Wilder 公式相比差异
|
|
480
|
+
在 60d 窗口尺度内不显著);BBW 用 4σ / MA20 × 100 表达带宽占均价比例。
|
|
481
|
+
|
|
482
|
+
## [limit-up-board v0.4.0] — 2026-05-07 — 候选股市值/股价过滤 + 持久化设置
|
|
483
|
+
|
|
484
|
+
> 仅升级 limit-up-board 插件版本(0.3.2 → 0.4.0);框架版本不变。
|
|
485
|
+
> 升级方式:`deeptrade plugin upgrade <plugin-source>`,会自动应用新增的
|
|
486
|
+
> migration(`20260508_005_lub_config.sql`)。
|
|
487
|
+
|
|
488
|
+
### Added (limit-up-board plugin)
|
|
489
|
+
|
|
490
|
+
- **Step 1 候选股漏斗增加两条筛选条件**:在主板 + 涨停 join 之后、ST/停牌之前
|
|
491
|
+
按 *流通市值* 与 *当前股价* 上限再筛一层。null 在任一字段 → 过滤(保守语义)。
|
|
492
|
+
默认 `流通市值 < 100亿` + `股价 < 15元`。`bundle.market_summary` 新增
|
|
493
|
+
`candidate_filter_summary` 字段(before / after / 阈值),LLM prompt 中可见。
|
|
494
|
+
- **新子命令 `deeptrade limit-up-board settings`**:交互式编辑两个上限阈值,
|
|
495
|
+
回车保留当前值,写入新表 `lub_config`。
|
|
496
|
+
- **新子命令 `deeptrade limit-up-board settings show`**:表格展示当前生效设置,
|
|
497
|
+
source ∈ {`default`, `persisted`}。
|
|
498
|
+
- **运行时打印当前配置**:`run` / `sync` 在 Step 1 之前 emit 一条 LOG 事件
|
|
499
|
+
(`运行配置: 流通市值 < ...亿、股价 < ...元`),dashboard / 日志中可见。
|
|
500
|
+
|
|
501
|
+
### Implementation notes
|
|
502
|
+
|
|
503
|
+
- 配置存储:插件自有 `lub_config` 表(与 `lub_runs` / `lub_events` 同 Plan A
|
|
504
|
+
纯隔离层级),不复用框架级 `app_config` —— 避免 `_DOT_TO_FIELD` 白名单
|
|
505
|
+
扩张,框架保持轻量。
|
|
506
|
+
- 默认值唯一来源:`limit_up_board.config:LubConfig` 的 dataclass default;
|
|
507
|
+
SQL / CLI / 文档不重复声明。
|
|
508
|
+
|
|
509
|
+
## [limit-up-board v0.3.0] — 2026-05-07 — Phase A + Phase B 因子补齐
|
|
510
|
+
|
|
511
|
+
> 仅升级 limit-up-board 插件版本(0.2.1 → 0.3.0);框架版本不变。
|
|
512
|
+
> 升级方式:`deeptrade plugin upgrade <plugin-source>`,会自动应用本版本新增的两条
|
|
513
|
+
> migration(`20260508_001_lub_lhb_tables.sql` / `20260508_002_lub_cyq_perf.sql`)。
|
|
514
|
+
|
|
515
|
+
参考 `docs/limit-up-board-optimization-plan.md`。对照 Gemini 给出的"游资思路"因子
|
|
516
|
+
清单,分两阶段补齐:A 阶段为派生因子(不增 tushare API),B 阶段接入龙虎榜与筹码。
|
|
517
|
+
|
|
518
|
+
### Added (limit-up-board plugin)
|
|
519
|
+
|
|
520
|
+
**A1 — 派生因子(候选股层,pure compute)**
|
|
521
|
+
- `amplitude_pct`:T 日振幅 = (high − low) / pre_close × 100。
|
|
522
|
+
- `fd_amount_ratio`:封单比 = fd_amount / amount × 100,>10% 为强势封板。
|
|
523
|
+
- `ma5` / `ma10` / `ma20` + `ma_bull_aligned`:基于 prev_daily 的简单移动平均;
|
|
524
|
+
`ma_bull_aligned = (close > ma5 > ma10 > ma20)`。历史不足窗口期返回 null。
|
|
525
|
+
- `up_count_30d`:近 30 个交易日 pct_chg ≥ 9.8 的天数;不足 30 日返回 null。
|
|
526
|
+
|
|
527
|
+
**A2 — 市场情绪三件套(market_summary 层)**
|
|
528
|
+
- `limit_step_distribution_prev` + `limit_step_trend`:T 与 T-1 的连板梯队对比,
|
|
529
|
+
interpretation ∈ {`spectrum_lifting`, `spectrum_collapsing`, `stable`}。
|
|
530
|
+
- `yesterday_failure_rate`:T-1 全市场炸板率(z / (u + z)),interpretation ∈
|
|
531
|
+
{`high` ≥25%, `moderate`, `low` ≤10%}。
|
|
532
|
+
- `yesterday_winners_today`:T-1 涨停股在 T 日的连板率与平均涨幅,
|
|
533
|
+
interpretation ∈ {`strong_money_effect`, `neutral`, `weak_money_effect`}。
|
|
534
|
+
|
|
535
|
+
**B1 — 龙虎榜(top_list / top_inst → required)**
|
|
536
|
+
- 候选股层字段:`lhb_net_buy_yi`(龙虎榜净买入,亿元;正/负均如实给出)、
|
|
537
|
+
`lhb_inst_count`(机构席位 unique 数)、`lhb_famous_seats`(命中游资白名单的
|
|
538
|
+
exalter 原文数组)。
|
|
539
|
+
- `FAMOUS_SEATS_HINTS`:~15 条主流游资席位子串白名单(拉萨系、宁波系、章盟主、
|
|
540
|
+
赵老哥、厦门帮等)。匹配逻辑大小写不敏感;只给 exalter 原文,不暴露白名单标签。
|
|
541
|
+
- 新表:`lub_top_list`(key trade_date+ts_code)、`lub_top_inst`
|
|
542
|
+
(key trade_date+ts_code+exalter+side)。
|
|
543
|
+
- "未上榜 ≠ 数据缺失":candidate 不在当日 top_list 中时 `lhb_*` 为 null,但
|
|
544
|
+
`data_unavailable` 中**不会**出现 top_list/top_inst(接口本身可用)。
|
|
545
|
+
|
|
546
|
+
**B2 — 筹码(cyq_perf → required)**
|
|
547
|
+
- 候选股层字段:`cyq_winner_pct`(获利盘比例 %)、`cyq_top10_concentration`
|
|
548
|
+
(100 − (cost_95pct − cost_5pct) / weight_avg × 100,clip [0,100])、
|
|
549
|
+
`cyq_avg_cost_yuan`(weight_avg)、`cyq_close_to_avg_cost_pct`
|
|
550
|
+
((close − weight_avg) / weight_avg × 100)。
|
|
551
|
+
- 新表:`lub_cyq_perf`(key trade_date+ts_code)。
|
|
552
|
+
|
|
553
|
+
**Prompt 改动**
|
|
554
|
+
- R1_SYSTEM【分析维度】新增形态、历史基因、市场情绪三类;evidence 新增"missing_data
|
|
555
|
+
字段不得引用"硬约束。
|
|
556
|
+
- R2_SYSTEM【判断重点】追加:亏钱效应下自动下调 confidence 一档;梯队拉升期允许
|
|
557
|
+
上调 continuation_score;筹码维度三阈值(70/60/-10)+ 龙虎榜维度(未上榜 ≠ 缺失;
|
|
558
|
+
负净买入不可作为正面 evidence;不可推断游资身份)。
|
|
559
|
+
- R1 **不引入** chip / LHB 维度(控制 prompt 噪声,B 阶段仅 R2 使用)。
|
|
560
|
+
|
|
561
|
+
### Changed (BREAKING)
|
|
562
|
+
|
|
563
|
+
**Tushare 权限要求扩张**(用户必须确保 tushare 账户已开通以下权限):
|
|
564
|
+
- `top_list` / `top_inst` / `cyq_perf` 由 `permissions.tushare_apis.optional` 上移
|
|
565
|
+
到 `required`;任一缺失 → run failed。**未引入新权限分类**——"接口可用但
|
|
566
|
+
candidate 未上榜"用 null + 数据层 inner-join 自然表达。
|
|
567
|
+
|
|
568
|
+
**默认值 / 内部行为变更**
|
|
569
|
+
- `RunParams.daily_lookback` / CLI `--daily-lookback` 默认值 10 → **30**(满足
|
|
570
|
+
ma20 + up_count_30d)。
|
|
571
|
+
- `collect_round1` 新增 `prev_trade_date: str | None` 入参;3 个 runner 调用点
|
|
572
|
+
通过 `_safe_prev_trade_date(cal, T)` 计算并传入。
|
|
573
|
+
- daily 历史窗口的日历缓冲由 `lookback + 5` 改为 `lookback × 2`,确保 30 个交易
|
|
574
|
+
日 lookback 在过节窗口下仍能命中 ≥30 行真实交易日。
|
|
575
|
+
|
|
576
|
+
### Migrations
|
|
577
|
+
|
|
578
|
+
- `20260508_001_lub_lhb_tables.sql` — 创建 `lub_top_list` / `lub_top_inst`。
|
|
579
|
+
- `20260508_002_lub_cyq_perf.sql` — 创建 `lub_cyq_perf`。
|
|
580
|
+
|
|
581
|
+
### Tests
|
|
582
|
+
|
|
583
|
+
- `tests/strategies_builtin/limit_up_board/test_phase_a_factors.py`:33 个单元
|
|
584
|
+
测试,覆盖 A1/A2 全部派生函数 + 边界(不足窗口期、零分母、空 frame、阈值跳点)。
|
|
585
|
+
|
|
586
|
+
- `tests/strategies_builtin/limit_up_board/test_phase_b_factors.py`:20 个单元
|
|
587
|
+
测试,覆盖白名单匹配(大小写、去重、非字符串)、LHB rollup(空帧 / None /
|
|
588
|
+
仅 top_list)、cyq 集中度(紧/宽/截断到 0)、close_to_avg_cost_pct 边界。
|
|
589
|
+
|
|
590
|
+
## [v0.7.0] — 2026-05-01 — Stage 概念归插件 + 配置键改名
|
|
591
|
+
|
|
592
|
+
清理 v0.6 留下的 stage 硬编码技术债。Stage 名字、preset → stage tuning 表全
|
|
593
|
+
部移入插件;框架的 `LLMClient.complete_json` 不再认识 stage,由调用方直接传
|
|
594
|
+
入 `StageProfile`。配置键 `deepseek.profile` 同步重命名为 `app.profile`。
|
|
595
|
+
**Breaking change**(项目仍在 dev/iteration 期)。设计原文:DESIGN.md §10.1。
|
|
596
|
+
|
|
597
|
+
### Changed (BREAKING)
|
|
598
|
+
|
|
599
|
+
**框架瘦身 — stage 退出 LLMClient**
|
|
600
|
+
|
|
601
|
+
- 删除 `core.llm_client.KNOWN_STAGES` / `LLMUnknownStageError` /
|
|
602
|
+
`_stage_profile()` / 全局 `_CURRENT_STAGE`。
|
|
603
|
+
- `LLMClient.complete_json` 签名变化:
|
|
604
|
+
- 删除 `stage: str` 入参;framework 不再写 `llm_calls.stage` 列。
|
|
605
|
+
- 新增 `profile: StageProfile`(必填)— 调用方直接传入已解析的调参档。
|
|
606
|
+
- `LLMClient.__init__` 删除 `profiles=` 入参。
|
|
607
|
+
- `LLMManager.get_client()` 不再绑 profile。
|
|
608
|
+
- `RecordedTransport` 改为纯 FIFO:`register(response)` 不再带 stage 标签。
|
|
609
|
+
- 删除 `core.config.DS_STAGES` / `DeepSeekProfileSet` / `PROFILES_DEFAULT`
|
|
610
|
+
/ `ConfigService.get_profile()`。
|
|
611
|
+
- `StageProfile` 升格为公共契约,搬到 `deeptrade.plugins_api.llm`,由
|
|
612
|
+
`from deeptrade.plugins_api import StageProfile` 公开导出。
|
|
613
|
+
|
|
614
|
+
**配置键改名 — `deepseek.profile` → `app.profile`**
|
|
615
|
+
|
|
616
|
+
- `AppConfig.deepseek_profile` → `AppConfig.app_profile`;`_DOT_TO_FIELD`
|
|
617
|
+
同步更新。preset 仍是 `Literal["fast","balanced","quality"]`,语义全局,
|
|
618
|
+
但键名 vendor-agnostic。
|
|
619
|
+
- DB 行自动迁移:`config_migrations.migrate_legacy_deepseek_profile_key`
|
|
620
|
+
幂等地把 `deepseek.profile` 行改写为 `app.profile`,并删除旧行。
|
|
621
|
+
- **环境变量直接断代**:`DEEPTRADE_DEEPSEEK_PROFILE` 不再被识别。
|
|
622
|
+
`ConfigService.get_app_config()` 启动时若检测到旧 env 而新 env 未设,
|
|
623
|
+
抛 `RuntimeError` 退出 — 避免静默用错配置(默认会回落到 "balanced",
|
|
624
|
+
让用户以为生效但其实并没有)。请改为 `DEEPTRADE_APP_PROFILE`。
|
|
625
|
+
|
|
626
|
+
**DB schema — `llm_calls.stage` 列删除**
|
|
627
|
+
|
|
628
|
+
- 新 SQL 迁移 `20260501_002_drop_llm_calls_stage.sql` `ALTER TABLE
|
|
629
|
+
llm_calls DROP COLUMN IF EXISTS stage`(DuckDB 1.0+)。
|
|
630
|
+
- `core/migrations/core/20260427_001_init.sql` 同步去掉 `stage` 字段,
|
|
631
|
+
让 fresh DB 直接落到 v0.7 期望状态。
|
|
632
|
+
- 历史 run 的 stage 信息仍可在
|
|
633
|
+
`~/.deeptrade/reports/<run_id>/llm_calls.jsonl` 中查阅;v0.7 起新写入的
|
|
634
|
+
jsonl 行也不再含 `stage` 键。
|
|
635
|
+
|
|
636
|
+
**插件改造(两个内建插件)**
|
|
637
|
+
|
|
638
|
+
- 新增 `<plugin>/profiles.py`:本地维护 preset → stage tuning 表 +
|
|
639
|
+
`resolve_profile(preset, stage)`。
|
|
640
|
+
- `runner.py` 读取 `cfg.app_profile`(preset 字符串),传给 pipeline 函数;
|
|
641
|
+
pipeline 内部调 `resolve_profile()` 得到 `StageProfile`。
|
|
642
|
+
- `volume-anomaly` 借此改造把语义错误的 stage 名 `continuation_prediction`
|
|
643
|
+
改回 `trend_analysis`。
|
|
644
|
+
|
|
645
|
+
### Added
|
|
646
|
+
|
|
647
|
+
- `deeptrade/plugins_api/llm.py` — 公共 `StageProfile` 契约(4 字段:
|
|
648
|
+
thinking / reasoning_effort / temperature / max_output_tokens)。
|
|
649
|
+
- `config_migrations.migrate_legacy_deepseek_profile_key` + 4 条单测
|
|
650
|
+
(rename / 幂等 / fresh DB no-op / new-already-set 跳过)。
|
|
651
|
+
- `tests/core/test_config.py` 新增两条 env 行为测试(旧 env 报错、新旧并存
|
|
652
|
+
以新为准)。
|
|
653
|
+
|
|
654
|
+
### Engineering
|
|
655
|
+
|
|
656
|
+
- 136 pytest tests passing (无变更)。
|
|
657
|
+
- 偿还 v0.6 RV6-4 / RV6 §10.2 已知技术债。
|
|
658
|
+
|
|
659
|
+
## [v0.6.0] — 2026-05-01 — LLM Manager 化(多 Provider)
|
|
660
|
+
|
|
661
|
+
The LLM client is no longer DeepSeek-specific — it is now a framework-level
|
|
662
|
+
service that lets a single plugin call multiple OpenAI-compatible LLMs in
|
|
663
|
+
the same run. **Breaking change** (project remains in dev/iteration phase).
|
|
664
|
+
Full rationale: `DESIGN.md` §0.7 + §10.
|
|
665
|
+
|
|
666
|
+
### Changed (BREAKING)
|
|
667
|
+
|
|
668
|
+
**Configuration model — `deepseek.*` → `llm.*`**
|
|
669
|
+
|
|
670
|
+
- `llm.providers` (JSON dict, app_config) — `{name: {base_url, model, timeout}}`.
|
|
671
|
+
Multiple providers coexist; each plugin picks by name at call time.
|
|
672
|
+
- `llm.<name>.api_key` (secret_store) — one secret per provider. The
|
|
673
|
+
`is_secret_key()` predicate (replacing the old `SECRET_KEYS` constant)
|
|
674
|
+
matches `tushare.token` plus this dynamic prefix.
|
|
675
|
+
- `llm.audit_full_payload` (bool, app_config) — replaces
|
|
676
|
+
`deepseek.audit_full_payload`.
|
|
677
|
+
- `deepseek.profile` is **kept** as the global stage-profile name (rename
|
|
678
|
+
deferred to v0.7 per §10.1 note).
|
|
679
|
+
- The four legacy keys `deepseek.base_url` / `deepseek.model` /
|
|
680
|
+
`deepseek.timeout` / `deepseek.audit_full_payload` are removed from
|
|
681
|
+
`AppConfig`. There is **no `llm.default`** — callers must pass a name.
|
|
682
|
+
|
|
683
|
+
**Auto-migration**
|
|
684
|
+
|
|
685
|
+
- On first `apply_core_migrations()` after upgrade, legacy `deepseek.*`
|
|
686
|
+
rows are migrated into `llm.providers["deepseek"]` + the renamed secret
|
|
687
|
+
`llm.deepseek.api_key` + `llm.audit_full_payload`. Idempotent: re-runs
|
|
688
|
+
on already-migrated DBs are no-ops. Code in
|
|
689
|
+
`deeptrade.core.config_migrations.migrate_legacy_deepseek_keys`.
|
|
690
|
+
|
|
691
|
+
**LLM client / new manager**
|
|
692
|
+
|
|
693
|
+
- New `core/llm_manager.py::LLMManager` — the only path plugins should use:
|
|
694
|
+
`list_providers()`, `get_provider_info(name)`, `get_client(name, *,
|
|
695
|
+
plugin_id, run_id, reports_dir=None)`. Caches clients per
|
|
696
|
+
`(name, plugin_id, run_id)`. Documented as not thread-safe.
|
|
697
|
+
- `core/deepseek_client.py` → `core/llm_client.py`;
|
|
698
|
+
`DeepSeekClient` → `LLMClient`. `OpenAIClientTransport` keeps its name
|
|
699
|
+
(it really is the OpenAI-compatible transport).
|
|
700
|
+
- `LLMNotConfiguredError` (new) — raised by manager when a provider is
|
|
701
|
+
missing or its api_key is unset.
|
|
702
|
+
|
|
703
|
+
**CLI command surface**
|
|
704
|
+
|
|
705
|
+
- Added: `config set-llm` (interactive new / edit / delete one provider),
|
|
706
|
+
`config list-llm` (show usable providers), `config test-llm [name]`
|
|
707
|
+
(per-provider connectivity check; tests all when omitted).
|
|
708
|
+
- Removed: `config set-deepseek`, `config test`. The init-time prompt
|
|
709
|
+
("Configure deepseek now?") is now "Configure an LLM provider now?".
|
|
710
|
+
- `config show` expands `llm.providers` so each provider's `api_key` slot
|
|
711
|
+
is its own masked row.
|
|
712
|
+
|
|
713
|
+
**Plugin runtime collapse**
|
|
714
|
+
|
|
715
|
+
- `volume-anomaly` and `limit-up-board` runtimes lose their per-plugin
|
|
716
|
+
`build_llm_client(rt)`; both now declare `llms: LLMManager` and call
|
|
717
|
+
`rt.llms.get_client(provider_name, plugin_id=, run_id=, reports_dir=)`.
|
|
718
|
+
Provider selection helper `pick_llm_provider(rt)` ships in each
|
|
719
|
+
plugin's `runtime.py` (prefers `deepseek`, falls back to first
|
|
720
|
+
available); a per-plugin `default_llm` config key is deferred to v0.7.
|
|
721
|
+
|
|
722
|
+
### Added
|
|
723
|
+
|
|
724
|
+
- `LLMProviderConfig` Pydantic model (per-provider connection record).
|
|
725
|
+
- `ConfigService.set_llm_provider(name, *, base_url, model, timeout,
|
|
726
|
+
api_key=None)` and `delete_llm_provider(name)` — CRUD helpers used by
|
|
727
|
+
the CLI.
|
|
728
|
+
- `tests/core/test_llm_manager.py` — list/info/get_client + cache + missing
|
|
729
|
+
api_key/provider errors + multi-provider coexistence (11 cases).
|
|
730
|
+
- `tests/core/test_config_migrations.py` — idempotency + happy path +
|
|
731
|
+
partial-legacy-state edge case (7 cases).
|
|
732
|
+
|
|
733
|
+
### Engineering
|
|
734
|
+
|
|
735
|
+
- 136 pytest tests passing (was 130; +11 manager + 7 migration − 12 retired
|
|
736
|
+
duplicates).
|
|
737
|
+
- ruff + mypy clean on touched files.
|
|
738
|
+
- DESIGN §0.7 + §7.1 + §7.3 + §10 rewritten; PLAN §11 adds the v0.6 work
|
|
739
|
+
breakdown.
|
|
740
|
+
|
|
741
|
+
### Known design debts (deferred)
|
|
742
|
+
|
|
743
|
+
- `KNOWN_STAGES` (`strong_target_analysis / continuation_prediction /
|
|
744
|
+
final_ranking`) is still hardcoded in the framework — leaks plugin
|
|
745
|
+
semantics into `core/llm_client.py`. v0.7 will let plugins declare their
|
|
746
|
+
own stage names + per-stage profile overrides.
|
|
747
|
+
- `deepseek.profile` key name retained for backward compat within the
|
|
748
|
+
current dev cycle; rename to `llm.profile` planned for v0.7.
|
|
749
|
+
- No transport plugin type yet — Anthropic native / Gemini native cannot be
|
|
750
|
+
added without code changes. Will land as `type=llm-transport` plugins
|
|
751
|
+
when first user need surfaces.
|
|
752
|
+
|
|
753
|
+
## [v0.5.0] — 2026-04-30 — Plugin CLI dispatch + pure data isolation
|
|
754
|
+
|
|
755
|
+
Breaking-change reshape per `docs/plugin_cli_dispatch_evaluation.md`. The
|
|
756
|
+
project is in dev/iteration phase; **no backward compatibility**.
|
|
757
|
+
|
|
758
|
+
### Changed (BREAKING)
|
|
759
|
+
|
|
760
|
+
**Framework command surface — closed**
|
|
761
|
+
- Top-level CLI is now `init / config / plugin / data` ONLY. `strategy` and
|
|
762
|
+
`channel` command groups removed.
|
|
763
|
+
- Pure pass-through: any unknown first token is looked up as a `plugin_id`;
|
|
764
|
+
if installed + enabled, framework calls `Plugin.dispatch(remaining_argv)`
|
|
765
|
+
and is otherwise dumb.
|
|
766
|
+
- `deeptrade --help` no longer enumerates plugin subcommands. `--help`
|
|
767
|
+
inside a plugin is the plugin's own responsibility.
|
|
768
|
+
- `deeptrade hello` removed. Interactive main menu removed.
|
|
769
|
+
- Reserved plugin_ids: `init`, `config`, `plugin`, `data`.
|
|
770
|
+
|
|
771
|
+
**Plugin contract — minimal**
|
|
772
|
+
- `StrategyPlugin` Protocol removed. New unified `Plugin` Protocol:
|
|
773
|
+
`metadata` + `validate_static(ctx)` + `dispatch(argv) -> int`.
|
|
774
|
+
- `ChannelPlugin` extends `Plugin` and adds `push(ctx, payload)`.
|
|
775
|
+
- `StrategyContext` / `StrategyParams` / `StrategyRunner` / TUI dashboard
|
|
776
|
+
removed. Each plugin owns its own runtime + run lifecycle internally.
|
|
777
|
+
- `ChannelContext` renamed to `PluginContext` (still narrow: db + config +
|
|
778
|
+
plugin_id).
|
|
779
|
+
|
|
780
|
+
**Data isolation — Plan A (pure)**
|
|
781
|
+
- Framework owns ONLY: `app_config`, `secret_store`, `schema_migrations`,
|
|
782
|
+
`plugins`, `plugin_tables`, `plugin_schema_migrations`, `llm_calls`,
|
|
783
|
+
`tushare_sync_state`, `tushare_calls`. No business tables.
|
|
784
|
+
- Tushare-derived shared market tables (`stock_basic`, `trade_cal`, `daily`,
|
|
785
|
+
`daily_basic`, `moneyflow`) removed from core. Each strategy plugin
|
|
786
|
+
declares its own prefixed copies (e.g. `lub_stock_basic`, `va_*`).
|
|
787
|
+
- `tushare_sync_state` PK now `(plugin_id, api_name, trade_date)`. Each
|
|
788
|
+
plugin tracks its own sync state and cache; no cross-plugin sharing.
|
|
789
|
+
- `tushare_calls` and `llm_calls` add `plugin_id` column.
|
|
790
|
+
- `TushareClient.__init__` requires `plugin_id`. Framework probes use the
|
|
791
|
+
reserved `FRAMEWORK_PLUGIN_ID = "__framework__"` sentinel.
|
|
792
|
+
|
|
793
|
+
**Notification API**
|
|
794
|
+
- `core/notifier.py` exposes top-level `notify(db, payload)` and
|
|
795
|
+
`notification_session(db)`. Re-exported as `from deeptrade import
|
|
796
|
+
notify, notification_session`. NoopNotifier when no channel enabled.
|
|
797
|
+
- `strategy_runs` / `strategy_events` tables removed. Each plugin defines
|
|
798
|
+
its own `<prefix>_runs` / `<prefix>_events` if it wants run history.
|
|
799
|
+
|
|
800
|
+
### Added
|
|
801
|
+
|
|
802
|
+
- Built-in plugins reshaped to v0.2.0:
|
|
803
|
+
- `limit-up-board`: own `cli.py` + `plugin.py` + `runner.py` + `runtime.py`;
|
|
804
|
+
new migration with 10 `lub_*` tables.
|
|
805
|
+
- `volume-anomaly`: same pattern; 5 `va_*` tables; subcommands `screen`
|
|
806
|
+
/ `analyze` / `prune` / `history` / `report`.
|
|
807
|
+
- `stdout-channel`: implements new `Plugin` + `ChannelPlugin` contracts;
|
|
808
|
+
own `dispatch` for `test` / `log`.
|
|
809
|
+
- Tests: framework routing tests (`tests/cli/test_routing.py`); Plugin
|
|
810
|
+
Protocol contract tests (`tests/plugins_api/test_protocol.py`); plugin
|
|
811
|
+
install + migration isolation tests (`tests/core/test_plugin_install.py`).
|
|
812
|
+
|
|
813
|
+
### Removed
|
|
814
|
+
|
|
815
|
+
- `cli_strategy.py`, `cli_channel.py`, `tui/` package, `core/strategy_runner.py`,
|
|
816
|
+
`core/context.py`, old plugin `strategy.py` files.
|
|
817
|
+
- `textual` dependency.
|
|
818
|
+
- `_interactive_main_menu`, `hello` command.
|
|
819
|
+
|
|
820
|
+
## [v0.1.0] — 2026-04-28
|
|
821
|
+
|
|
822
|
+
First public release. Baseline implementation of DESIGN.md v0.3.1.
|
|
823
|
+
|
|
824
|
+
### Added
|
|
825
|
+
|
|
826
|
+
**Framework**
|
|
827
|
+
|
|
828
|
+
- `deeptrade init` — DuckDB layout + 11-table core schema migrations (idempotent).
|
|
829
|
+
- `deeptrade config show / set / set-tushare / set-deepseek / test` — layered config (env > db > default), keyring + plaintext fallback for secrets.
|
|
830
|
+
- `deeptrade plugin install / list / info / disable / enable / upgrade / uninstall [--purge]` — three-stage lifecycle (install **never** touches network; validate is connectivity-only; run does the strict checks).
|
|
831
|
+
- `deeptrade strategy list / run / history / report <run_id>` — Live EVA-themed dashboard (header / progress / events / analysis / footer); `--no-dashboard` for non-tty.
|
|
832
|
+
- Plugin api_version "1": `StrategyPlugin` Protocol, `StrategyContext`, `StrategyEvent` enum, Pydantic `PluginMetadata` (YAML).
|
|
833
|
+
|
|
834
|
+
**Core services**
|
|
835
|
+
|
|
836
|
+
- `Database` — single-process, single-writer DuckDB; reentrant write lock; short transactions.
|
|
837
|
+
- `SecretStore` — keyring-first, plaintext fallback with explicit warning.
|
|
838
|
+
- `TushareClient` — token-bucket rate limit, tenacity retries, 4 cache classes (static / trade_day_immutable / trade_day_mutable / hot_or_anns), `data_completeness` (final / intraday) for intraday isolation.
|
|
839
|
+
- `DeepSeekClient` — JSON-mode + Pydantic double-validate; profile triple (`fast` / `balanced` / `quality`); stage-level `max_output_tokens` (R1/R2 default 32k, final_ranking 8k); **never** passes `tools` / `tool_choice` / `functions`.
|
|
840
|
+
- `StrategyRunner` — status state machine (running → success / failed / partial_failed / cancelled); KeyboardInterrupt → cancelled; any `VALIDATION_FAILED` event flips success → partial_failed.
|
|
841
|
+
- `setup_logging()` — stderr handler + rotating file under `~/.deeptrade/logs/`.
|
|
842
|
+
|
|
843
|
+
**Built-in strategy: limit-up-board**
|
|
844
|
+
|
|
845
|
+
- Step 0 `resolve_trade_date()` — most-recent-closed trade day; `app.close_after` configurable threshold; `--allow-intraday` opt-in.
|
|
846
|
+
- Step 1 data assembly — main board filter (Q2: SSE/SZSE only), ST/suspended exclusion, three-tier `sector_strength` fallback (`limit_cpt_list` → `lu_desc_aggregation` → `industry_fallback`), normalized prompt fields (亿/万 + 2dp) while DB keeps raw.
|
|
847
|
+
- Step 2 R1 — `plan_r1_batches()` with input + output token DUAL budget (F5); `EvidenceItem.unit` mandatory.
|
|
848
|
+
- Step 4 R2 — single batch by default; auto multi-batch when input exceeds budget.
|
|
849
|
+
- Step 4.5 `final_ranking` — only triggered on multi-batch R2; `select_finalists()` keeps top + watchlist + boundary avoid samples.
|
|
850
|
+
- Step 5 reports — 5-file dump under `~/.deeptrade/reports/<run_id>/`; banner stack rules (red for partial_failed/failed/cancelled, yellow for INTRADAY MODE, both stack).
|
|
851
|
+
|
|
852
|
+
### Fixed (v0.3 review round 2 → v0.3.1)
|
|
853
|
+
|
|
854
|
+
- **F1** `limit_step` was duplicated in optional table → removed; required-only.
|
|
855
|
+
- **F2** `limit_cpt_list` `✅` mismatched optional status → unified to `optional+fallback` everywhere; `sector_strength_source` label propagated to prompts.
|
|
856
|
+
- **F3** Fast profile R2 `thinking: true` contradicted "all off" docs → flipped to `false`.
|
|
857
|
+
- **F4** `--allow-intraday` would have polluted EOD caches → added `data_completeness` column; daily-mode reader rejects intraday-cached rows; UI/report INTRADAY MODE banner.
|
|
858
|
+
- **F5** Default `max_output_tokens=8192` would truncate R1 → moved to per-stage profile (R1/R2 32k, final_ranking 8k); R1 evidence cap 8 → 4; rationale length-capped via prompt.
|
|
859
|
+
- **S1** Migrations are now the **sole** DDL source; `tables` only declares names + purge flag.
|
|
860
|
+
- **S2** `app.close_after` configurable (default 18:00); install never touches tushare.
|
|
861
|
+
- **S3** `strategy_runs.status` CHECK constraint removed (DuckDB ALTER limitation); validation moved to Pydantic layer.
|
|
862
|
+
- **S4** `row_count=0` is a legal outcome (extreme tape day); fallback predicate accepts it.
|
|
863
|
+
- **S5** `final_ranking` only ranks finalists; non-finalists keep `batch_local_rank`; both surface in `round2_predictions.json`.
|
|
864
|
+
|
|
865
|
+
### Engineering
|
|
866
|
+
|
|
867
|
+
- 163 pytest tests passing; ruff + mypy clean.
|
|
868
|
+
- 1 real concurrency bug fixed during development: `Database.transaction()` + `execute()` self-deadlock with non-reentrant `threading.Lock` → switched to `threading.RLock`.
|
|
869
|
+
- 1 pandas 2.x compat fix: `pd.read_json(json_str)` deprecated → wrap in `io.StringIO()`.
|
|
870
|
+
|
|
871
|
+
### Known design debts (planned for v0.4)
|
|
872
|
+
|
|
873
|
+
- **D1** Replace `configure(ctx) -> dict` with schema-driven `get_param_schema() -> type[BaseModel]`; CLI auto-renders questionary forms; non-interactive mode supports `--params-file`.
|
|
874
|
+
- **D2** Per-API `probes` in plugin metadata; `validate` becomes two layers (`validate_connectivity` + `validate_required_apis`).
|
|
875
|
+
|
|
876
|
+
[v0.1.0]: https://github.com/example/deeptrade/releases/tag/v0.1.0
|