开源项目 Vibe-Trading 仓库结构导读:改一处功能从哪进去
本文基于 Vibe-Trading 仓库 commit 3a752d5(2026-08-04)梳理,该项目仍在高频迭代,具体行为以仓库 https://github.com/HKUDS/Vibe-Trading 最新代码与文档为准。
这个仓库最值得先看的不是代码,是根目录那份 AGENT_CONTRIBUTOR_GUIDE.md——它用五行字把整个仓库的入口切好了,还额外点名了一组「即使改动看起来很小也算安全关键」的目录。 你如果直接从 agent/src/ 一头扎进去,面对二十多个模块目录会花掉半小时才找到方向;先读那五行,五分钟就能定位。
先说清楚这里的 Vibe-Trading 指的是 HKUDS 放出的那个开源仓库,是一个专有项目名,跟中文语境里「凭感觉交易」那种说法没有关系。它的定位在 pyproject.toml 的 description 里写得很直白:带回测能力的自然语言金融研究 AI agent。
站内已经写过 opencode 的仓库结构、hermes 的仓库结构和 pi 的仓库结构,那三篇拆的都是通用编码 / 终端 Agent 的骨架;本篇拆的是另一种形态——一个把领域工作流沉淀成技能库与因子库的 Agent 工程,看点在「内容资产怎么组织」和「危险操作怎么圈起来」,跟前三篇正好互补。
先立一句边界:本文只讨论这个仓库的工程实现,不评价任何策略或因子的优劣,也不涉及投资建议。历史表现不代表未来。
一、顶层五块地盘,各自的边界在哪
AGENT_CONTRIBUTOR_GUIDE.md 的 Repository Shape 一节把仓库形状说完了:后端与可发布包在 agent/,前端在 frontend/,公开 wiki 内容在 wiki/ 且有独立的 GitHub Actions 检查,MCP 入口是 vibe-trading-mcp / agent/mcp_server.py,CLI 入口是 vibe-trading / agent/cli/。
这几句话看着简单,落到数量上差别很大。全仓 2030 个受版本控制的文件里,agent/ 占 1805 个,frontend/ 只有 155 个。也就是说这是一个后端极重、前端偏薄的仓库,你要改的大部分东西都在 agent/ 里面。
wiki/ 是第三块,它不是文档目录而是一个能独立部署的静态站,里面有 docs、tutorials、alpha-library、research-lab、home、locales 这些子目录,还带 wrangler.toml、_headers、_redirects。仓库的 .github/workflows/ 下确实有专门的 wiki.yml 与 wiki-deploy.yml,跟 test.yml 分开。指南也要求 wiki 的改动就留在 wiki/ 下,别把生成的资产和本地草稿混进公开文档。
第四块是根目录的治理文件:CONTRIBUTING.md(DCO 与因子 PR 审查清单)、AGENT_CONTRIBUTOR_GUIDE.md(AI 辅助贡献者的安全约定)、SECURITY.md、NOTICE(第三方来源与许可声明)、LICENSE(MIT,Copyright 2026 Vibe-Trading Contributors)。第五块是构建与 CI 周边:Dockerfile、docker-compose.yml、requirements-lock.txt,以及 tools/ci_grep_gates.sh 这类仓库级 CI 助手。
| 组成部分 | 它负责什么 | 对应仓库位置 | 你什么时候会碰到它 |
|---|---|---|---|
| 后端与可发布包 | agent 核心、API、CLI、回测 | agent/ | 绝大多数功能改动 |
| CLI 入口 | 交互式 TUI 与子命令 | agent/cli/(脚本名 vibe-trading) | 加子命令、改终端交互 |
| HTTP 服务 | FastAPI 服务与已拆分的路由模块 | agent/api_server.py + agent/src/api/ | 加接口、改鉴权 |
| MCP 服务 | 把工具暴露给别的 Agent 宿主 | agent/mcp_server.py(脚本名 vibe-trading-mcp) | 接入外部 Agent 客户端 |
| 技能库 | 88 个技能目录,每个一份 SKILL.md | agent/src/skills/ | 加一份方法论文档 |
| 工具层 | agent 可调用的工具实现 | agent/src/tools/(72 个文件) | 加一个可被调用的能力 |
| 因子库 | 五个 zoo 子目录的公式重实现 | agent/src/factors/zoo/ | 加或改因子(要过两道门) |
| 回测层 | 引擎、数据加载器、组合优化器 | agent/backtest/ | 加市场、加数据源 |
| 多智能体编制 | 运行时 + 30 份预设 yaml | agent/src/swarm/、agent/src/swarm/presets/ | 加一支团队编制 |
| 实盘安全层 | mandate、order gate、halt、audit | agent/src/live/ | 安全关键区,改前先看指南 |
| 券商连接器 | 12 家连接器子目录 | agent/src/trading/connectors/ | 安全关键区,改前先看指南 |
| IM 渠道 | 16 个具体渠道实现文件 | agent/src/channels/ | 接新的聊天平台 |
| 前端 | 页面、组件、状态管理 | frontend/src/ | 改 Web UI |
| 公开 wiki | 独立部署的静态站 | wiki/ | 改公开文档 |
| 治理与许可 | 贡献规则、AI 贡献约定、第三方来源 | 根目录若干 md 与 NOTICE | 提 PR 之前 |
二、后端内部:先看打包配置,再找目录
agent/src/ 下有 23 个模块目录,外加 market_data.py、preflight.py、ui_services.py 三个顶层文件。目录名基本自解释:agent/(ReAct 循环与上下文)、api/、channels/、config/、core/、factors/、goal/、hypotheses/、live/、memory/、openbb_bridge/、providers/、scheduled_research/、security/、session/、shadow_account/、skills/、strategy_store/、swarm/、tools/、trading/、utils/ 等。
但真正决定你怎么写 import 的是 pyproject.toml。它把 package-dir 设成 {"" = "agent"},packages.find 只 include src*、backtest*、cli*,另外把 api_server、mcp_server 作为 py-modules 单独列出。[project.scripts] 里 vibe-trading = "cli:main"、vibe-trading-mcp = "mcp_server:main"。
这条配置有两个直接后果。第一,包内的导入路径是 src.xxx 而不是 agent.src.xxx——你在因子文件里写 from src.factors.base import ... 才是对的,写全路径会在打包安装后崩掉。第二,agent/tests/、agent/scripts/ 不在 include 列表里,pip 装到本地的那份和你 clone 下来的那份并不是同一套文件,排查用户报的路径问题时别拿仓库结构直接套。
package-data 那段同样有信息量:它显式列了 skills/**/*.md、skills/**/*.yaml、skills/**/*.json、skills/**/*.py、providers/*.json、swarm/presets/*.yaml、shadow_account/templates/*.j2 与 .html / .css,以及 src.factors 下的 zoo/**/*.yaml、zoo/**/*.md、zoo/**/NOTICE。换句话说,这些非 Python 资源是发布物的一部分。你新增一类资源文件时,如果它不落在这些通配符里,本地跑得好好的,pip 安装的用户那边就是空的。
agent/src/api/ 里已经能看到路由被切成 alpha_routes.py、auth_routes.py、channels_routes.py、live_routes.py、runs_routes.py、scheduled_routes.py、sessions_routes.py、settings_routes.py、swarm_routes.py、system_routes.py、uploads_routes.py、qveris_routes.py,另有 security.py、state.py、models.py、helpers.py。加接口时进对应的 routes 文件,而不是回头去堆 api_server.py。
三、技能库与因子库:两套「内容资产」,组织方式完全不同
这是 Vibe-Trading 跟通用编码 Agent 最不一样的地方——它有两大块以内容为主体的资产,而且规矩不一样。
技能库在 agent/src/skills/,88 个目录、共 404 个文件。每个技能一个目录,目录里放 SKILL.md;比如 shadow-account/ 下就只有一份 SKILL.md,而目录总数和文件总数对不上,说明部分技能带了附加文件。加载逻辑在 agent/src/agent/skills.py,frontmatter 解析被抽成了 agent/src/agent/frontmatter.py 共用。技能名从 candlestick、elliott-wave 到 regulatory-knowledge、research-discipline 都有,覆盖面很宽。这套「一个目录一份 Markdown」的形态跟别的框架差异不小,可以对照 技能机制的三体对比 来看。
因子库在 agent/src/factors/,482 个文件,zoo/ 下五个子目录:academic/、alpha101/、fundamental/、gtja191/、qlib158/。周边是 base.py(算子层)、registry.py(元数据加载与惰性计算)、bench_runner.py 与 bench_runner_strict.py、compare_runner.py、factor_analysis_core.py、cli_handlers.py。
这里必须把来源说清楚,因为仓库自己说得很清楚:根目录 NOTICE 声明 qlib158 打包的是 Microsoft Qlib 的特征定义,走 Apache 2.0 许可,上游 NOTICE 与归属见 agent/src/factors/zoo/qlib158/NOTICE 与 LICENSE.md;alpha101 的公式来自 Kakushadze (2015) 的 “101 Formulaic Alphas”(arXiv:1601.00991);gtja191 来自国泰君安 2014 年的 191 短周期交易 alpha 因子研报;academic 对应 Fama-French 五因子、Carhart 动量、Hou-Xue-Zhang q-factor 等。NOTICE 里明确写着:论文与研报的正文、表格、图表都没有复制进这个仓库,只把数学公式当作事实性内容重新实现。所以这几个因子库不是「项目自研」,是公开公式的工程化重实现;能不能商用要看各 zoo 子目录下 LICENSE.md 的原文,本文不提供法律意见。
CONTRIBUTING.md 里那份 Alpha PR 审查清单,把加一个因子的全部硬要求列成了勾选框:过 agent/tests/factors/test_alpha_purity.py 的纯度门(AST 扫描只允许 pandas、numpy、scipy.*、src.factors.base、__future__、typing、math、dataclasses,并拒绝 os、subprocess、socket、urllib、requests、httpx、pathlib、Path、eval、exec、compile、__import__、裸 open,以及第二参数以 "__" 开头的 getattr);过 test_lookahead.py 的前视门(不允许负向 shift,delta(df, d) 的 d 必须大于等于 1);__alpha_meta__ 必须带齐 id、theme、formula_latex、columns_required、universe、frequency、decay_horizon、min_warmup_bars;compute(panel) 要返回与 panel["close"] 同形状的 DataFrame,warmup 与缺数据位置保留 NaN,不得出现正负无穷;formula_latex 要和代码真正算的东西一致;每个 zoo 的 LICENSE.md 要更新来源引用,并且要用「公式是数学事实」的理由陈述,不许套用美国那套抗辩措辞;来自 Apache-2.0 上游的文件必须带 # Adapted from <repo>@<commit-sha>:<path> (Apache-2.0). Copyright (c) <holder>. 这种形式的头。
顺带一个容易被忽略的细节:pyproject.toml 的 ruff per-file-ignores 对 agent/src/factors/zoo/**/*.py 关掉了 F401,注释解释了原因——zoo 文件为了对齐论文公式会整份 import src.factors.base 的接口,未用导入是噪音;但 F841(未用局部变量)是故意留着的,用来抓真实的公式书写错误。
四、指南划出的安全关键区
AGENT_CONTRIBUTOR_GUIDE.md 直接点名:券商连接器、mandate、order gate、halt、审计账本这几处逻辑属于安全关键,哪怕改动看起来很小也一样。在仓库里能对上号的位置很清楚:agent/src/trading/connectors/ 下 12 家连接器子目录(alpaca、binance、dhan、futu、ibkr、longbridge、mt5、okx、robinhood、shoonya、tiger、trading212,README 也自述 12 家),以及 agent/src/live/ 下的 mandate/、order_guard.py、sdk_order_gate.py、halt.py、audit.py、daily_count.py、enforcement.py、registry.py 和 runtime/。
指南另外列了一组「跑之前要拿到维护者或操作者明确批准」的高风险动作:下单 / 撤单 / 审批 / 平仓等影响券商订单的命令;授权券商、OAuth、MCP、交易所、支付、钱包、云账号;把真实凭据写进 agent/.env、~/.vibe-trading/、token 缓存或外部服务;启动外部可达的 API / MCP / SSE / webhook / dashboard 服务;部署 wiki、发包、触发 release、改 CI secrets;改写历史、强推共享分支、删备份、删除持久化的 run 或 memory 数据。并且有一句没有条件的禁令:例行 PR 验证过程中,永远不要跑实盘交易、支付、钱包、合约或券商写操作。
安全规则那节还有几条落到代码约束上的:超出 loopback 的 API 或 Web 部署必须使用 API_AUTH_KEY;外部 MCP server 属于 operator-trust 面,除非某条代码路径显式记录并测试了这个 opt-in,否则不允许调用方注入 MCP 的 command、URL、环境变量或 allowlist;券商连接器的写操作必须保持 mandate 门控、感知 kill switch、失败时关闭、并写审计日志;优先用脱敏 fixture 而不是真实金融数据;secret 要当作访问权而不是文本看待,一旦出现就打码、停止复述、并建议轮换。
README 的 Security defaults 一节能跟这些对上:非本地客户端访问敏感接口需要 API_AUTH_KEY;shell 类工具只在交互式本地 CLI 打开,HTTP/SSE API 与 MCP 的所有传输方式(包括 stdio)默认关闭,要显式设 VIBE_TRADING_ENABLE_SHELL_TOOLS=1 或给 vibe-trading-mcp 传 --enable-shell-tools。还有一条 README 没写、要到配置加载层才看得到的:session 级注入 mcpServers 需要服务端先设 ALLOW_SESSION_MCP_SERVERS=1,否则这个字段在加载配置前就被剥掉,行为落在 agent/src/config/loader.py,开关本身在 agent/src/config/env_schema.py 里登记。这套「默认关、显式开、传输类型不隐式授权」的思路,跟 最小权限设计 讲的是同一件事。
指南给的验证命令也是分场景的:一般 Python 改动跑 pytest --ignore=agent/tests/e2e_backtest --ignore=agent/tests/test_e2e_harness_v2.py --tb=short -q;涉及实盘或订单安全就跑 test_sdk_order_gate.py、test_mandate_enforcement.py、test_killswitch_blocks_orders.py、test_readonly_default.py 这四个文件;因子相关跑 agent/tests/factors/ 下的纯度门与前视门;前端改动是 cd frontend && npm ci && npm run build。它还要求:全量太贵的时候用最窄的匹配命令,但要说清楚哪些没跑。
五、边界与代价
这套结构不是没有取舍,几点要先认下来。
第一,agent/src/ 这一层是按运行时职责切的,不是按资产类别切的——没有「股票模块」「期货模块」这种顶层目录。资产类别的差异要下沉一层,在 agent/backtest/engines/ 里才看得见按市场分的文件(china_a.py、china_futures.py、crypto.py、forex.py、global_equity.py、options_portfolio.py 这类),数据侧则是 agent/backtest/loaders/ 下一堆按数据源命名的加载器。这个切法的代价是:想加一个新市场,引擎与加载器两侧都要动,而且它们分属两套抽象(engines/base.py 与 loaders/base.py),不是抄一个现成目录改改名就能收工。
第二,后端重、前端薄。1805 比 155 的文件数摆在那,前端是 React 加 Vite 的一层壳,frontend/src/pages/ 下按 Home、Agent、AlphaZoo、RunDetail、Compare、Correlation、Settings、Reports、Runtime、Scheduled 这些页面组织,状态放在 frontend/src/stores/,依赖里用的是 Zustand。想在前端做深度定制,要预期后端契约变化会反复推着你改。
第三,那两道因子门管的是代码卫生,不是研究质量。纯度门保证因子文件不联网、不读写文件系统、不动态执行代码;前视门保证不用未来数据。它们都不判断一个因子是否有用,也不该被当成质量背书。
第四,指南只覆盖「怎么安全地改这个仓库」,不覆盖「你的用法是否合规」。README 的 Disclaimer 写得很直接:它是研究与交易软件,不是投资建议,不持有资金,不运行交易场所;通过你显式授权的券商通道交易只发生在你设定的限额内并且随时可以中止;这项券商交易能力是实验性的,并未在真实券商账户上验证过。
第五,凡是涉及实盘下单、券商连接、资金授权和凭据保管的部分,代价必须说清楚:凭据一旦写进 agent/.env 或 ~/.vibe-trading/ 就多出一个暴露面,本机被入侵等于账户被入侵;下错的单在真实市场里不可撤销,回滚代码不等于回滚成交;程序化交易在不同司法辖区的合规义务不一样。能不能这么用,一律以你所在司法辖区的监管要求与券商协议为准。
六、上手与避坑清单
import 路径写成 agent.src.factors.base。 会踩是因为你按仓库目录直觉来写;package-dir 是 agent,安装后顶层包就是 src。避法:照 CONTRIBUTING.md 的快速上手写 from src.factors.base import ...,并且纯度门本来就只放行 src.factors.base。
新增技能、预设或模板只加了文件。 会踩是因为本地是源码运行,什么都能读到。避法:新增任何非 .py 资源,先回 pyproject.toml 的 [tool.setuptools.package-data] 确认它落在某个通配符里,否则 pip 安装的用户拿不到。
在因子文件里顺手 import 了 os 或用 open 读了个 csv。 会踩是因为写数据处理代码时这几个是肌肉记忆。避法:动手前先跑一遍 pytest agent/tests/factors/test_alpha_purity.py -q,把允许清单当作约束条件而不是事后检查。
用了 delta(df, 0) 或负向 shift。 会踩是因为对齐论文公式时符号容易写反。避法:改完立刻跑 test_lookahead.py,它就是为这类错误准备的。
提交忘了 -s。 会踩是因为很多人的日常仓库不要求 DCO。避法:git commit -s;已经攒了一串未签名的提交,用 git rebase --signoff <base-branch> 重签后强推自己的 PR 分支——这跟指南禁止的「强推共享分支」不是一回事,别搞混。
顺手跑全量 pytest 把端到端回测拉起来了。 会踩是因为默认 testpaths 指向 agent/tests。避法:用指南给的那组 --ignore 组合;改的是实盘或订单相关就直接跑那四个专项测试文件。
把本地 .env、token 缓存、券商导出、运行产物或私人 notebook 提交进去了。 会踩通常是因为 git add -A。避法:指南明确禁止这类文件进仓库,除非是显式脱敏过的 fixture;而且真凭据一旦泄露,正确处理是打码 + 轮换,不是 revert 一下就当没发生。
为了验证 PR 启动了对外可达的服务,或者跑了真实券商写操作。 会踩是因为想「跑通了才敢提」。避法:这两类都在高风险清单里,需要显式批准;例行验证用脱敏 fixture 和只读路径。
改了 zoo 文件,看到 F401 被关就以为可以随便留未用变量。 会踩是因为只看了 ignore 的第一项。避法:per-file-ignores 的注释写得很清楚,F841 是故意保留的,留下的未用局部变量往往就是公式漏了一步。
把 wiki 改动和代码改动塞进同一个 PR。 会踩是因为顺手。避法:wiki/ 有自己的 Actions 检查,分开提能让审查面小很多;文档更新的触发条件在指南的 Documentation Rules 里也列清楚了——面向用户的 CLI / Web / MCP / connector / provider / 安全边界变化要更新 README,贡献流程变化更新 CONTRIBUTING.md,SECURITY.md 只在漏洞报告策略变化时才动。
收束:三个文件的阅读顺序
真要动手,按这个顺序读三份文件就够开工了:先 AGENT_CONTRIBUTOR_GUIDE.md,拿到入口地图、安全关键区和分场景的验证命令;再 pyproject.toml,搞清楚包边界、脚本入口、打包资源和 lint 例外;最后按你要改的方向,进 agent/src/ 对应的那个目录,或者 CONTRIBUTING.md 的因子清单。
提交之前自查四条:改动是否触到了连接器 / mandate / order gate / halt / 审计这五处;跑了哪些测试、哪些明确没跑并在 PR 里说明了;有没有把本地凭据或运行产物带进去;如果动了实盘或订单行为,PR 里有没有写清安全边界和回滚或停机路径。指南对回滚也有交代:纯文档改动一般 git revert 就够,触及订单门、mandate、OAuth、provider、MCP、settings 或外部网络行为的代码改动需要配一个针对性的回归测试并写明 revert 路径,合并后发现安全回归时优先做小 revert 或失败即关闭的热修,而不是大范围重构。
最后重申本文的立场:以上全部是对一个开源仓库工程实现的描述,不构成任何投资建议;涉及能不能这么用的判断,以你所在司法辖区的监管要求与券商协议为准。
本篇属于一个把开源个人交易 Agent 项目 Vibe-Trading逐层拆开讲的系列,整体地图见 Vibe-Trading 是什么:HKUDS 这个开源交易 Agent 项目的工程全景与边界;沿着这条线往下,还可以看 拆解开源交易 Agent Vibe-Trading:工具、技能与风控三层 和 Vibe-Trading 源码:Agent 主循环与 runner 的分工。