能力子页 · 扩展与二次开发

tick-stock-panel 的扩展机制:不改核心代码能加什么

大多数开源项目的“扩展”意味着 fork,之后每次升级都要手工合并。本页讲清 tick-stock-panel 把扩展做成了两条互不干扰的链,以及它为此定下的那些硬约定。

本页回答:能加什么 / 放哪里 / 升级会不会坏依据:官方 README 关键机制、docs/secondary-development.md、AGENTS.md声明:仅写已实现能力,不写设计文档里的目标契约
后端目录自发现
前端构建时插槽
隔离跳过
删除即卸载
tick-stock-panel 的两条扩展链示意(依据官方 README 关键机制与 docs/secondary-development.md 整理的 schematic,非官方架构图)。后端按目录自发现注册,前端在构建时挂载插槽,两边都不需要修改核心文件。
Two chains

tick-stock-panel 的扩展到底是怎么加载进去的?

后端靠目录自发现,前端靠构建时插槽。两条链都不需要你碰核心文件,也都有各自的约定。

链路官方机制你需要做的事
后端把包放进 app/custom/<包名>/,启动时自动发现并注册独立路由写一个普通的 Python 包,在 setup 里把路由注册上去
前端把 src/custom/*/extension.tsx 放入,构建时自动挂载到插槽实现一个前端模块,向插槽暴露组件
隔离跳过版本不符或 setup 失败时,该扩展被隔离跳过,不影响主程序升级后发现扩展没加载,先看日志里是不是被跳过了
卸载删除整个扩展目录即整体卸载,零核心文件修改没有注册表、没有配置文件需要同步清理
这两条链共同决定了一件事:你不需要分组、不需要注册表、也不需要改主程序的任何一行。这在开源项目里是一个有意的选择:它让“你自己的代码”与“上游代码”在物理层面就分开,导致后续升级几乎不会冲突。代价是你得接受它的约定:目录位置、文件名与入口形式都是固定的,不能自己发明一套加载方式。
What can be added

tick-stock-panel 到底能扩展哪些东西?

下表列出六类拓展方式,以及它们各自应该放在哪里。其中两类完全不需要写代码。

拓展方式放在哪里适合什么需求
自定义信号(不写代码)UI 侧搭建条件,没有文件几个阈值条件的组合,改起来快
自定义策略data/strategies/custom/,文件名带 custom_ 前缀有特殊逻辑,需要历史窗口、状态依赖或自定义评分
AI 生成策略data/strategies/ai/,文件名带 ai_ 前缀想先拿一个可跑的基线,再逐行改
内置策略(贡献回上游)仓库的内置策略目录,参照现有文件实现接口你的策略通用且愿意提 PR
数据源插件参照仓库内的插件实现写成插件你有一个仓库未支持的数据源,且不想用 YAML 声明式源
数据扩展(不写代码)面板里的 HTTP 拉取 / CSV·Excel 上传 / JSON 写入你有现成数据文件或接口,想当作信号与因子的输入
Strategy file

tick-stock-panel 策略文件里到底要写什么?

一个策略文件由五个部分组成,其中有一个容易被忽略的硬约束。本节把它们摆出来,并补上目录扫描的细节。

策略文件组成作用编写时的关注点
META定义 UI 上可调的阈值与默认值把可能变的东西都放这里,而不是写死在逻辑里
basic_filter单日过滤,返回一个表达式适合“当日满足什么条件”这类逻辑
filter_history历史窗口过滤,需同时提供 LOOKBACK_DAYS只要用到回看,就必须点明回看长度
scoring评分权重,各项权重之和应为 1.0和不为 1 时排序会出现不可预期的结果
ENTRY_SIGNALS / EXIT_SIGNALS入场与出场信号它们让策略能被监控引用,也是回测与归因的依据
一个商界细节:引擎只扫描自定义策略目录的顶层 .py 文件。这意味着你可以在里面建子目录放文档、快照与证据,不会被当成策略加载;以下划线开头命名的文件也会被跳过。官方在策略迭代文档里正是利用这一点,把每轮的修改与台账保存在子目录里。它同时提供了一个「证据包」思路:每轮修改都留下改了什么、为什么改、结果如何;并且把「什么没用、为什么」的负知识列为最值得保留的部分。
Boundary

tick-stock-panel 哪些能改、哪些不能改?

边界比能力更重要:加错地方,升级时会连带着把你的修改一起覆盖。

想加的东西能不能加正确做法
一个新策略能放自定义策略目录,或向上游提 PR
一个新因子能用因子编辑器的 DSL 写,不需要改代码
一个新数据源能YAML 声明式源(六类数据集契约)或写插件
一个新页面能用前端插槽挂载,不改核心路由
一个 AI 工具能(只读前提)参照助手的扩展实现写自己的只读工具
改核心的回测口径不建议上游升级会覆盖你的修改,且会让结果不可审计
一个设计文档里写了但代码里没有的 API不能依赖官方明确要求不得据设计示例虚构尚不存在的 API
关于最后一行,这是官方在仓库里反复强调的一条纪律。二次开发文档里同时包含“已实现能力”与“目标扩展契约”,前者可以直接用,后者只是方向。它甚至把这一条写进了仓库的协作说明:不得根据设计示例虚构尚不存在的 API,也不得虚构测试与审查结果。对你的意义很直接:写扩展时不要“按文档推测”接口形状,而要去现有实现里找一个能跑的参照。
Build a extension

写一个 tick-stock-panel 扩展的正确顺序是什么?

下面七步是本站归纳的顺序:先用空扩展验证加载,再往里面填逻辑,最后用删除测试验证卸载干净。

写一个扩展的七步:从空扩展到可用

  • 先确认你要加的东西属于哪一类

    策略?因子?数据源?页面?AI 工具?四类扩展路径各自有固定的目录与命名约定,选错了就加载不上。

  • 先找官方已有的参照实现

    数据源插件参考仓库内现有插件;AI 工具参考助手的实现;前端插槽参考现有的前端扩展。照着能跑的代码写,不照设计描述写。

  • 用最小可运行版本验证加载

    先只做一个空的扩展,确认它能被发现、能注册、能在界面上看到。这一步通过了,再往里面填逻辑。

  • 再把真实逻辑填进去

    到这一步才开始写业务代码。每修改一次都重启一次,确认扩展还在正常加载。

  • 记下你用到的接口与版本

    扩展依赖的内部接口可能随版本变。记下你基于哪个版本开发,下次升级后就能快速定位。

  • 用删除测试验证卸载干净

    把整个扩展目录移走重启,界面与接口都应该恢复到没有它的样子——这是验证“零核心修改”的最直接方法。

  • 为什么要先用一个空扩展验证加载,而不是直接写功能:因为“扩展没被加载”与“扩展里的逻辑有 bug”是两类完全不同的问题,但在界面上可能表现得一模一样:都是“没效果”。先挊一个能跑的空壳,就把“加载链路是否通”与“逻辑是否正确”彻底分开了。这个习惯在写任何插件式系统时都适用,尤其是目录约定越严的系统:一个字母写错就可能让整个扩展静默失效。另一个实用建议是把扩展目录当成一个独立仓库来管理:它与主仓库在物理层面本就是分开的,上游更新时不会冲突,下次换机器也能整体搬走。
    为什么要先用一个空扩展验证加载,而不是直接写功能:因为“扩展没被加载”和“扩展里的逻辑有 bug”是两类完全不同的问题,但表现在界面上可能一模一样:都是“没效果”。先挊一个能跑的空壳,就把“加载链路是否通”与“逻辑是否正确”彻底分开了。这个习惯在写任何插件式系统时都适用,尤其是目录约定越严的系统:一个字母写错就可能让整个扩展静默失效。
    Upgrade safety

    tick-stock-panel 升级会不会把我的扩展弄坏?

    下表列出升级前后的几类风险与官方机制,并给出每一类对应的核对动作。

    升级前后的风险官方机制你该做什么
    扩展在新版本下加载失败版本不符或 setup 失败会被隔离跳过,不影响主程序不要把它当成“整站挂了”;先看日志确认是不是被跳过了
    前端插槽在新版本下未挂载插槽是构建时挂载的,前端改动后需重新构建重新构建,并检查你用到的插槽名是否变了
    上游修改了你依赖的内部接口内部接口不属于公开契约对关键扩展做一个最小自测,升级后跑一遍
    你改了核心文件才让扩展能用官方设计本意是不需要改改回去,把修改限制在你自己的扩展目录里
    想把扩展分享给其他人仓库欢迎贡献先读官方的贡献指南,再改成符合其目录约定的形式
    升级兼容的一个通用策略:把你依赖的东西限制在“最小集”。扩展越是只依赖目录约定与契约本身,升级越不容易坏;越是去调内部函数与未公开的结构,越容易在某次升级后静默失效。把你用到的接口写进一个简短的清单文件,升级后先跑一遍,就能在几分钟内知道自己的扩展还在不在。
    Directory conventions

    TSP 的目录与命名到底有哪些硬规定?

    下表把六条目录与命名约定列出来。它们看上去是细节,但写错任一条都会导致扩展不被加载。

    目录 / 文件约定它为什么要求这样
    后端自定义包目录一个包对应一个扩展,启动时被自动发现让新功能的引入不需要改任何入口文件
    前端扩展目录每个目录一个入口模块,构建时被挂到插槽前端打包阶段就把扩展编进去,运行时不需要动态加载
    自定义策略目录文件名带 custom_、放在顶层、以 .py 结尾引擎只扫顶层,子目录里可以安心放文档与快照
    AI 生成策略目录文件名带 ai_ 前缀,与人工策略分开你随时能区分哪些是自己写的、哪些是生成的
    下划线开头的文件被引擎跳过可以把临时脚本、旧版本备份放在同一目录不被加载
    证据与台账子目录不被作为策略加载每轮修改的记录与快照可以与策略放在一起,但不干扰运行
    这张表背后是同一个思路:用位置与命名代替注册表。传统做法需要一个配置文件告诉系统“加载哪些扩展”,而 tick-stock-panel 把这个信息放在目录与文件名里。好处是没有需要同步维护的清单;代价是你必须遵守它的约定,自己发明一套命名规则是不会被加载的。这也是很多人写完扩展发现“没生效”的第一原因。
    Why nothing loaded

    TSP 扩展没生效时,应该怎么排查?

    下表列出六类最常见现象与处理顺序:先排“有没有被加载”,再排“加载后行不行”。

    现象最常见的原因处理顺序
    后端扩展没被发现包没放在约定的自定义目录,或目录下缺入口文件先对照目录约定表 → 再看启动日志有无跳过记录
    前端插槽上没出现新页面入口模块名不符合约定,或没重新构建确认文件名与位置 → 重新构建 → 清缓存再看
    策略列表里多了不该有的项把临时脚本或旧版本备份放进了顶层把非策略文件移进子目录,或改成下划线开头命名
    扩展在本机好使、到别人机器就没了依赖了本地路径、环境变量或未提交的文件把扩展当独立仓库管理,并检查所有路径都是相对路径
    升级后扩展失效但主程序正常扩展被隔离跳过看日志的跳过原因 → 对照升级说明 → 改适配而不是改核心
    不知道自己改没改到核心文件没有检查过修改范围用一次删除测试验证:移走扩展目录后界面与接口应完全恢复
    一句话总结这一页:扩展写在自己的目录里,升级与卸载都不需要动核心代码。但这份自由的前提是守约定:位置、命名与入口形式都不能自己发明。守住这一点,你的扩展就能长期跟着上游走;守不住,就会退化成一个每次升级都要重新合并的 fork。
    排查顺序比结论重要:先排“有没有被加载”,再排“加载后行不行”。很多人一上来就去调扩展里的逻辑,结果花了很久才发现根本没加载。而且因为官方把加载失败设计成了“隔离跳过”而不是报错,这类问题不会自己冒出来,你必须主动去看日志。这也是“隔离跳过”这个设计的代价:它保住了主程序,但把发现问题的责任交给了你。
    FAQ

    tick-stock-panel 扩展与二次开发常见问题

    以下答案基于官方仓库与文档的核验结果;涉及版本、数据源口径与许可条款的内容一律以官方仓库与官方文档为准。
    tick-stock-panel 扩展真的不用改核心代码吗?

    官方的设计目标就是这个:后端把包放进自定义目录就会被自动发现并注册路由,前端把模块放进自定义目录就会在构建时挂到插槽,两边都不需要注册表。删除整个目录就是卸载,也没有残留配置需要清理。它甚至把自己的 AI 助手当作参考实现——就是为了证明这套机制能支撑一个完整功能。换句话说,你可以把它当成一个“可以自己写功能的客户端”,而不是一个只能用现成功能的封闭产品。

    为什么文件名要带前缀?

    因为引擎靠命名与目录区分策略来源。自定义策略带 custom_ 前缀,AI 生成的带 ai_ 前缀,两者分别落在不同子目录。这不是命名风格问题,而是为了让系统在不读取任何配置的前提下就能判断一个文件是什么、从哪来、能不能被跳过。另外下划线开头的文件会被跳过,别用它命名策略。

    我能用自己的数据源插件吗?

    可以。仓库里有现成的插件实现可供参照,你按同样的方式写一个就行。不过先判断你的需求属于哪一类:如果你只是想接一个 HTTP 接口,YAML 声明式数据源可能就够了,不必写代码;只有当接口形态特殊、YAML 表达不了时,才值得上插件。另外记住:深度盘口目前没有自定义数据集契约,那一块接不了。

    写完扩展怎么验证它真的生效了?

    用一个最小可运行版本先验证加载:空扩展能被发现、能注册、界面上能看到。这一步通过再填逻辑。最后还要做一次删除测试:把整个目录移走重启,界面与接口应该恢复到没有它的样子。如果删掉后还有残留行为,说明你不小心改到了核心文件。

    上游升级后扩展失效了怎么办?

    先确认它是不是被隔离跳过了。官方的行为是:版本不符或 setup 失败时,该扩展会被跳过而不影响主程序。所以“扩展没上线”与“主程序坏了”是两件事,不要把前者当成后者。定位方法是看启动日志里是不是有跳过记录,再对照你开发时用的版本。

    能不能把扩展发布出去给别人用?

    可以。仓库欢迎贡献,也提供了贡献指南与协作说明。不过官方在协作说明里给了两条铁纪律:一是区分“已实现能力”与“目标契约”,不得根据设计示例虚构尚不存在的 API;二是不得虚构测试或审查结果,以实际验证结果作为完成标准。实际操作上,建议先把扩展当成一个独立项目维护,等它稳定后再考虑合并到上游;这样即使提交被请求修改,你自己的扩展也不会因此停止工作。

    启用扩展后启动变慢了怎么办?

    先判断慢在哪一段:如果是启动时才慢,大概率是扩展在初始化阶段做了重操作(比如拉远程数据、扫大目录);如果是运行时才慢,则多半是在请求路径上。两者的修法完全不同:前者把重操作改成懒加载,后者去看是否每次请求都在重算。另外一个常见原因是扩展在每次请求里重复初始化,而不是只在启动时初始化一次。把状态放到正确的生命周期里,是写扩展时最容易忽略的一件事。

    扩展看完了,下一步该做什么?

    先回到部署页把环境跑起来,再按能力路由页把数据源配好;也可以直接回到概览重新选一条路线。