mirror of
https://github.com/shy3130/tick-stock-panel.git
synced 2026-11-02 10:26:13 +00:00
feat(import): 统一导入备份入口并支持后端扩展包
集中处理策略与定义、配置备份、数据源和页面插件导入,保留旧入口跳转。新增可信后端及前后端完整包的预检、暂存、重启生效、失败回退和版本管理。 验证:扩展、资源、备份与页面定向 pytest 通过,前端 41 项测试及生产构建通过,并完成桌面、窄屏和明暗主题联调。 Signed-off-by: shy3130 <shy3130@qq.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# 统一导入与外部后端扩展包
|
||||
|
||||
入口是「设置 → 导入与备份」(`/settings?tab=import`)。策略、因子、信号页的小盒子,以及数据源的「安装插件」都会进入同一页面。旧配置备份和页面插件链接继续重定向到这里。
|
||||
|
||||
## 导入与管理
|
||||
|
||||
选择或拖入文件后,先自动识别、预览,再确认写入:
|
||||
|
||||
- 策略 `.py`、信号/因子 `.json` 和资源 ZIP 可混合多选;复用原有依赖闭包、冲突比较、批次回滚和缓存刷新。
|
||||
- `tick-config-backup` 配置备份按类别恢复。每次新预览都默认不选择密钥和访问凭证;需要迁移 API Key 时明确勾选。
|
||||
- 根目录有 `plugin.yaml` 的数据源包继续使用 [Provider 安装契约](external-data-source-packages.md)。安装后在「已安装扩展」配置 Key、验证并选择数据能力;不会自动切换行情来源。
|
||||
- `.js/.mjs` 页面产物、单个 `.tsx` 或本项目已有源码页面继续使用 [Page Pack SDK](page-sdk.md)。确认信任和同名替换,通过浏览器加载校验后刷新生效。源码编译仍受现有部署环境限制。
|
||||
- 根目录有 `extension.yaml` 的 ZIP 使用下述后端扩展契约,可同时包含配套前端页面。安装仅暂存,重启主程序后生效。
|
||||
|
||||
「创建配置备份」和「批量导出资源」保留原有范围,不包含后台扩展的安装文件。行情数据、K 线和回测缓存不包含在配置备份中。外部数据源与后端扩展请单独保留原 ZIP。
|
||||
|
||||
## 后端 / 前后端完整包
|
||||
|
||||
ZIP 根目录直接放清单,不额外套目录。每包一个后端模块,可选一个自包含页面 ES 模块:
|
||||
|
||||
```text
|
||||
research.zip
|
||||
├── extension.yaml
|
||||
├── backend/
|
||||
│ ├── __init__.py
|
||||
│ └── extension.py
|
||||
└── frontend/
|
||||
└── extension.mjs
|
||||
```
|
||||
|
||||
```yaml
|
||||
format: tick-extension-package
|
||||
format_version: 1
|
||||
id: example.research
|
||||
display_name: 研究观察工作台
|
||||
version: 1.0.0
|
||||
extension_api_version: 1
|
||||
author: 作者声明
|
||||
description: 自定义研究服务与配套页面
|
||||
backend: backend.extension
|
||||
frontend: frontend/extension.mjs # 纯后端扩展省略此项
|
||||
requires_app: '>=0.3.25'
|
||||
requires_python: '>=3.11'
|
||||
platforms: [any]
|
||||
requires: [] # 仅检查已有依赖,不运行 pip/npm
|
||||
```
|
||||
|
||||
`id` 为小写字母开头的字母、数字、下划线,可用点或连字符分段,最长 80 字符;`official.*`、`core.*` 保留。版本如 `1.0.0`;相同版本不同内容拒绝覆盖。更新必须提高版本,降级使用回退操作。
|
||||
|
||||
后端模块沿用 `app.extensions` 的真实契约:
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter
|
||||
from app.extensions import BACKEND_EXTENSION_API_VERSION
|
||||
|
||||
EXTENSION_ID = "example.research"
|
||||
EXTENSION_API_VERSION = BACKEND_EXTENSION_API_VERSION
|
||||
|
||||
def setup(registrar):
|
||||
router = APIRouter(prefix="/api/extensions/example.research")
|
||||
|
||||
@router.get("/status")
|
||||
def status():
|
||||
return {"ready": True}
|
||||
|
||||
registrar.include_router(router)
|
||||
|
||||
def startup(context):
|
||||
# 可选;核心仓库准备完成后运行。
|
||||
# context 提供 api_version、data_dir、只读 repository 协议。
|
||||
pass
|
||||
```
|
||||
|
||||
`setup` / `startup` 是同步函数。可注册 HTTP 路由和 `NotificationFormatter`,不支持替换核心引擎。外置接口必须位于 `/api/extensions/<包id>/`,不能覆盖核心或其他扩展,不接受 WebSocket 路由。初始化使用 `startup(context)`,不要依赖路由器自身的 lifespan。
|
||||
|
||||
配套页面使用 Page Pack SDK v1,`default.id` 与包 `id` 相同。用户确认信任后,浏览器先加载候选页面检查导出契约,成功才提交安装;预览阶段不执行 JS 或 Python。页面路由最终由宿主注册表检查,启动时发生冲突会显示在「已安装扩展」,需修正包后重新安装。
|
||||
|
||||
## 生效与失败隔离
|
||||
|
||||
安装内容位于 `data/extensions/<id>/versions/<version>/`,状态原子写入 `state.json`。限 20 MiB 压缩包、80 MiB 解压总量、1000 条目;拒绝越界路径、重复/大小写冲突、符号链接、原生库、wheel 和安装脚本。不写入程序源码目录。
|
||||
|
||||
启动时验证文件摘要、兼容性、ID、接口命名空间和注册冲突,再运行启动钩子。成功后才发布路由、通知格式化器和配套页面。新后台失败时保留原版本,新页面不会先切换;单个扩展错误不阻断主程序或其他扩展。
|
||||
|
||||
启停、回退、更新和卸载在下一次主程序启动时应用;浏览器刷新不能替代后台重启。停用中回退不会自动启用。旧页面可能保留在已打开标签中,后台重启完成后需刷新浏览器同步。
|
||||
|
||||
失败隔离覆盖已提交的路由、策略注册与安装状态;可信代码自行产生的文件、网络等副作用无法自动撤销。扩展与主程序同权限,不是沙箱。管理使用面板登录会话,不向开放 API Token 授权;无密码时仅接受本机同源管理。
|
||||
|
||||
首版面向单进程部署;多 worker 应全部停止后统一维护和重启,不提供跨 worker 在线切换。升级后仍需满足包的兼容范围。
|
||||
|
||||
## API
|
||||
|
||||
| 请求 | 行为 |
|
||||
| --- | --- |
|
||||
| `POST /api/import-center/inspect` | 多文件 `files`,只读识别并委托既有预检 |
|
||||
| `GET /api/extension-packages` | 安装、运行、待生效版本与错误 |
|
||||
| `POST /api/extension-packages/install` | `file/digest/revision/trusted=true`,重新校验后暂存 |
|
||||
| `POST /api/extension-packages/frontend` | 信任后读取候选页面供浏览器校验,不发布 |
|
||||
| `POST /api/extension-packages/{id}/manage` | `enable/disable/rollback/cancel-update/remove` |
|
||||
| `POST /api/import-center/page` | 页面上传/编译,增加摘要、信任和同名替换确认 |
|
||||
| `POST /api/import-center/project` | 信任后编译现有项目页面,保留同名确认 |
|
||||
|
||||
配套页面复用 `/api/ext-pages` 清单与分发,标记 `managed_by`,只在对应后台版本就绪时提供。启停和卸载通过整包管理,不能拆开修改。
|
||||
@@ -1,13 +1,13 @@
|
||||
# 外部数据源插件安装
|
||||
|
||||
外部安装是数据源插件机制的新入口:开发者发布 ZIP,用户在「设置 → 数据源 → 安装插件」安装,随后点击新卡片填写 API Key、验证并选择接管的数据能力。无需为每个源改前端或重新发布主程序。
|
||||
外部安装是数据源插件机制的新入口:开发者发布 ZIP,用户在「设置 → 导入与备份」安装(原数据源页「安装插件」会跳转到此),随后点击新卡片填写 API Key、验证并选择接管的数据能力。无需为每个源改前端或重新发布主程序。
|
||||
|
||||
## 使用流程
|
||||
|
||||
1. 点击数据源区域右上角的小盒子「安装插件」,选择或拖入 ZIP。
|
||||
1. 打开「设置 → 导入与备份」,选择或拖入 ZIP;数据源页的小盒子入口仍可使用。
|
||||
2. 查看名称、作者声明、版本、能力和兼容检查结果。预览不会执行包内 Python。
|
||||
3. 确认信任来源后安装。Python 插件与后端同权限,可读取数据和凭据;这不是沙箱。
|
||||
4. 安装后自动选中新卡片。需要 Key 时显示「待配置 API Key」,有效 Key 才保存到现有 `user_data/secrets.json`;运行时初始化失败恢复原凭据和实例。
|
||||
4. 安装完成后点击「查看已安装扩展」,在数据源详情中继续配置。需要 Key 时显示「待配置 API Key」,有效 Key 才保存到现有 `user_data/secrets.json`;运行时初始化失败恢复原凭据和实例。
|
||||
5. 在详情区「选择启用能力」勾选需要接管的能力,确认当前源到新源的变化。仅修改勾选项;安装、配置 Key 都不会自动切换路由。
|
||||
6. 插件提供 `test_dataset` 时可以逐项试拉;未提供时显示「未检测」。验证 Key 不代表全部接口都有权限,试拉可能消耗供应商额度。
|
||||
|
||||
@@ -74,7 +74,7 @@ requires: [] # 现有宿主发行包版本约束,例如 'htt
|
||||
|
||||
- 数据源加载器负责外置目录发现、包入口解析、统一注册和受控实例释放;原有服务继续通过 `get_provider` / `provider_has_dataset` 取数。
|
||||
- 安装管理通过 `app/custom/source_packages` 的后端扩展注册,不修改主应用路由入口。
|
||||
- 前端管理位于 `src/custom/source-packages`,使用两个正式插槽:`settings.data-sources.toolbar`(`refresh`、`selectSource`)和 `settings.data-sources.detail-actions`(`plugin`、`refresh`)。这些是本次新增的已实现契约。
|
||||
- 统一页面复用 `src/custom/source-packages` 的管理与能力选择组件。数据源页通过两个正式插槽跳转到统一入口:`settings.data-sources.toolbar`(`refresh`、`selectSource`)和 `settings.data-sources.detail-actions`(`plugin`、`refresh`)。这些是本次新增的已实现契约。
|
||||
- 数据源列表、Key、试拉、路由 API 和查询缓存复用原实现,不建立另一套数据服务。
|
||||
|
||||
新增管理端点:
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
# 页面插件开发指南 (Page Pack SDK v1)
|
||||
|
||||
页面插件是**运行时可挂载的页面产物**:把页面打包成单文件 ES 模块(`extension.js`),在「设置 → 页面插件」上传,刷新后即成为完整页面——自动注册路由、侧栏入口,可在菜单设置里排序/隐藏。本地 dev、生产构建、桌面端三种运行形态一致生效,宿主无需重新构建。
|
||||
页面插件是**运行时可挂载的页面产物**:把页面打包成单文件 ES 模块(`extension.js`),在「设置 → 导入与备份」上传,刷新后即成为完整页面——自动注册路由、侧栏入口,可在菜单设置里排序/隐藏。本地 dev、生产构建、桌面端三种运行形态一致生效,宿主无需重新构建。
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -9,7 +9,7 @@ cp -r frontend/page-pack-template my-pack && cd my-pack
|
||||
pnpm install && pnpm build # → dist/extension.js
|
||||
```
|
||||
|
||||
打开宿主「设置 → 页面插件」→ 填 ID(字母/数字/下划线)→ 选择 `dist/extension.js` → 上传并校验 → 刷新页面。
|
||||
打开宿主「设置 → 导入与备份」→ 选择 `dist/extension.js` → 预览页面标识(字母/数字/下划线)、确认来源及同名替换 → 导入并校验 → 刷新页面。
|
||||
|
||||
## 产物契约 (apiVersion 1)
|
||||
|
||||
@@ -70,3 +70,5 @@ export default extension
|
||||
| 页面插件(运行时产物) | 免重建挂载、独立分发 | 本文档 |
|
||||
| 源码级扩展 `frontend/src/custom/<ns>/extension.tsx` | 随宿主一起构建维护、可用全部核心模块 | `docs/secondary-development.md` §3 |
|
||||
| 声明式分析页面(零代码) | 扩展数据 + 模板化列表/分组页 | 设置 → 扩展页面 |
|
||||
|
||||
配套后台扩展可使用 [前后端完整包](extension-packages.md);单独上传 JS/TSX 只导入前端页面。
|
||||
|
||||
@@ -20,12 +20,13 @@
|
||||
- 扩展数据与声明式分析页面:适合不需要自定义 React 交互的页面。
|
||||
- 前端源码扩展注册:`frontend/src/custom/<namespace>/extension.tsx`,支持静态页面、导航和已开放插槽。
|
||||
- 后端源码扩展注册:`backend/app/custom/<module>.py`,支持 FastAPI 路由、启动钩子和通知格式化器。
|
||||
- 外部后端扩展包及配套页面安装:`data/extensions/<id>/versions/<version>/`,复用现有注册契约,静态预览、重启生效、失败保留旧版本。入口为「设置 → 导入与备份」,见 [统一导入与外部后端扩展包](extension-packages.md)。
|
||||
- 当前前端插槽:`layout.navigation.extra`、`stock-preview.footer`、`watchlist.toolbar`。
|
||||
- 当前后端继承点:`NotificationFormatter`。
|
||||
- 自定义仓位策略(`PositionSizingPolicy`,已实现):`data/user_data/position_policies/*.py`,在「设置 → 定义 → 仓位策略」创建(表单 META + 只写一个 `def compute(ctx)` 函数,模板起步)。契约见 `app/backtest/position_sizing.py`:ctx 只含当日及以前信息(候选评分/近 60 日收益/已平仓快照/组合状态/参数),输出 weights + budget_scale,引擎侧单仓上限/现金/敞口钳制不变。保存/备份导入均过「AST 安全校验 → 结构解析 → 合成冒烟」三关;回测在「买入权重 → 自定义策略」选用,引用失效 400 拒绝、运行期异常当日回退等权并计入统计。
|
||||
- 因子平台与策略线的既有桥接(已实现,二开时直接复用、勿重复实现):因子库一键生成单因子排名策略(`GenerateFactorStrategyDialog` → `custom_factor_*`);策略触发器引用因子条件信号(`AddFactorSignalDialog`,`csg_f_*`);自定义信号 AI 提示词含因子分组(`custom_signals_ai.py`);策略回测因子归因(`strategy.py` 的 `factor_attribution`,覆盖 `meta.scoring` 非空的策略)。
|
||||
- 旧策略包格式与接口(兼容保留):按依赖闭包打包策略 `.py` + 引用的自定义信号/因子定义 + 用户调参(叠加策略含子策略)。`app/services/strategy_bundle.py` 只保留兼容入口,实际实现统一位于 `app/services/resource_bundle.py`;新旧接口共用依赖闭包、ZIP 安全校验、打包与资源预检,旧导入复用 `config_backup.apply_archive` 的校验/逐条写盘/冒烟管线,缺失依赖在旧预览中只作预警。包内仅允许策略/调参/信号/因子四类,其他路径 400 拒绝。API:`GET /api/strategies/{id}/bundle`、`POST /api/strategies/bundle/inspect|apply`;当前页面入口使用下述统一资源传输。
|
||||
- 统一资源传输(已实现):策略、因子、信号页各使用一个 `ResourceTransferButton` 图标,在当前页打开 `ResourceTransferDialog`;`kind` 限定主资源列表类型,依赖统一展示并自动携带。多文件识别后优先选择同类型原始项,没有此类型原始项时允许从混合包提取该类型定义;确认冲突后只提交所选项及其依赖。`StrategyExportButton` 与弹窗共用 `downloadResourceBundle`,单个策略和批量导出均下载 ZIP,包含所需因子、信号与已保存调参。处理期间禁用关闭,关闭后重开重置临时状态。服务层 `app/services/resource_bundle.py` 统一收集多资源依赖闭包,复用备份路径/原子写与定义编译;新包格式 `tick-resource-bundle` v1,兼容旧策略包。预览通过 `registry.isolated_registry()` 在请求上下文副本内检查同包因子,不向实时线程泄漏定义;新导入链路预检后整批提交,写入/注册/重载失败回滚,区别于旧备份接口的逐条容错语义。API 位于 `/api/definitions/resources`,详见 [策略文档](strategy.md#资源导入与批量导出)。
|
||||
- 统一资源传输(已实现):策略、因子、信号页各使用一个 `ResourceTransferButton` 图标,进入「设置 → 导入与备份」;`kind` 透传资源选择上下文,依赖统一展示并自动携带,批量导出复用 `ResourceTransferDialog`。多文件识别后优先选择同类型原始项,没有此类型原始项时允许从混合包提取该类型定义;确认冲突后只提交所选项及其依赖。`StrategyExportButton` 与弹窗共用 `downloadResourceBundle`,单个策略和批量导出均下载 ZIP,包含所需因子、信号与已保存调参。处理期间禁用关闭,关闭后重开重置临时状态。服务层 `app/services/resource_bundle.py` 统一收集多资源依赖闭包,复用备份路径/原子写与定义编译;新包格式 `tick-resource-bundle` v1,兼容旧策略包。预览通过 `registry.isolated_registry()` 在请求上下文副本内检查同包因子,不向实时线程泄漏定义;新导入链路预检后整批提交,写入/注册/重载失败回滚,区别于旧备份接口的逐条容错语义。API 位于 `/api/definitions/resources`,详见 [策略文档](strategy.md#资源导入与批量导出)。
|
||||
- 模拟盘「绑定即评估」(已实现,`app/strategy/paper_watch.py`):账户绑定策略(自动跟单规则 `match_kind=strategy`)即自动进入实时评估循环——引擎内存态注入合成规则(`paper_watch:{strategy_id}`,不落盘、不进监控页),事件照常喂 `paper_auto.on_rule_events`。同步幂等且与真监控规则去重(同策略已有启用中的监控规则则不注入,避免重复事件);`set_rules` 是全量覆盖,故监控规则任何保存后都会重注入。合成规则的信号是沙盒信号:只驱动模拟盘下单,不进 SSE 告警/系统通知/webhook/留痕(`quote_service` 按 `is_paper_watch_event` 分流)。
|
||||
|
||||
尚未实现、只能在真实需求出现后增加的能力:
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@
|
||||
|
||||
### 资源导入与批量导出
|
||||
|
||||
策略、因子和信号页顶部各有一个**小盒子图标**,在当前页打开对应的导入导出弹窗;策略页入口位于右侧基础配置按钮左边。弹窗主列表只展示当前页的资源类型,关联依赖在右侧预览并自动携带;没有独立资源中心或页面跳转。
|
||||
策略、因子和信号页顶部各有一个**小盒子图标**,进入「设置 → 导入与备份」并保留资源类型上下文;策略页入口位于右侧基础配置按钮左边。统一页展示资源与关联依赖,自动携带所需依赖;批量导出使用同页的资源导出入口。
|
||||
|
||||
单个策略的下载入口位于策略配置对话框顶部,导出的 ZIP 包包含策略源码、已保存调参、相关自定义因子和信号(及其递归依赖);未保存修改须先保存后再导出。页面顶部的小盒子弹窗内可批量导出多项策略、因子或信号,完成后关闭窗口继续使用当前页面。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user