- 金融科技
- 后端
- 前端
【免费下载链接】binance-trading-bot
Automated Binance trading bot with pluggable strategies, historical backtesting, and a live dashboard
本文基于仓库根目录的 GEMINI.md(编码 Agent 工程章程)撰写,系统讲解 binance-trading-bot 的核心架构原则、不可变不变量(invariants)、技术栈、仓库布局、常用命令、Lint 机制、质量门禁与应拒绝的反模式。该仓库是一个面向单一运营者(single-operator)、多账户(multi-account)、多配置档案(multi-profile)、可靠性优先、移动端优先的 Binance 自动化交易平台。阅读本文后,你将掌握该项目的设计边界(哪些代码可以改、哪些改动方式被禁止)、插件如何接入、账户数据访问规则、迁移文件的变更纪律,以及 CI 强制执行的完整质量门禁清单,从而能在该仓库中正确、安全地贡献代码。
一、项目定位:一个人的多账户交易平台
从工程章程开篇即可确认项目的形态:binance-trading-bot是一个单运营者(single-operator)平台——一位运营者拥有 N 个一级 Binance 账户,每个账户等于「一对 API Key + 一个环境 + 一条 user-data 流」;每个账户下可以运行 N 个策略档案(profiles),共享该账户的钱包。
关键点在于「策略即插件(Strategy is a plugin)」:
- 第一个插件是trailing-trade(移动止盈/移动止损策略),其事实源(source of truth)位于 packages/strategy/trailing-trade/;
- 插件契约(contract)位于 packages/strategy/core/ 与 docs/architecture/extensibility.md。
项目目前处于Greenfield(全新建设,尚未部署)阶段,因此没有向后兼容的历史包袱,鼓励选择正确、可扩展的设计而非最小 diff,并且允许自由重构——但必须在动手前声明这是一次大型重构。
二、核心不可变不变量(Core Invariants):改代码前必须守住的红线
章程列出的四条不变量是整个项目架构的锚点,任何改动都不得违反。
2.1 不变量一:可扩展性(Extensibility)——插件永不侵入宿主
- 策略是
packages/strategy-core背后的插件;通知提供者是@app/notify的./providers/*子路径导出。 apps/api/apps/worker禁止导入任何具体的strategy-*包,也禁止在注册表引导(registry bootstrap)之外导入@app/notify/providers/*下的具体 provider。apps/web可以导入策略包以获取类型化事件负载(typed event payloads)。- 新增一个策略 = 新增一个包 + 注册表条目;新增一个通知器 = 新增一个 provider 模块 +
buildNotifyRegistry条目。永远不需要改动apps/api/apps/worker的代码。
源码印证:注册表的实现位于 packages/strategy/registry/src/index.ts,它是「已注册策略插件集合」的唯一事实源——apps/api与apps/worker在启动时各自调用buildStrategyRegistry(),从而保证两个进程持有完全一致的注册内容。当前注册了三个插件:trailingTrade、momentum、rebalance(详见 packages/strategy/registry/src/index.ts)。
同理,通知器注册表 packages/notify/src/registry.ts 的buildNotifyRegistry()在 boot 时注册slackProvider、telegramProvider、webhookProvider三个 provider(实现在 packages/notify/src/providers/ 下),同样保证两个进程的describeAll()一致。API 侧消费策略描述符的统一出口是 apps/api/src/strategies/registry.ts 的createApiStrategyRegistry——它直接复用 core 的AnyStrategy,只额外增加面向 SPA 的describeAll()序列化(把 zod config schema 转成 JSON Schema),刻意不做第二个平行的 API 侧插件接口,以避免「新增契约字段需要四处锁步修改」的坏味道。
2.2 不变量二:可靠性(Reliability)——崩溃即恢复
- Crash-only(仅崩溃):进程随时可以崩溃,重启后靠幂等恢复。
- 幂等作业(Idempotent jobs)。
- 按账户的 Binance 速率隔离(Per-account Binance-rate isolation)。
- 不允许静默失败(No silent failures)。
实现侧证据:worker 的单副本串行执行依赖 apps/worker/src/lib/chain-by-key.ts 的createChainByKey()——以Map<string, Promise>为链,同 key 的并发调用永不重叠,不同 key 并行执行。这正是「无分布式锁的单副本保证」的核心:BullMQ 并发 25 可能同时弹出多个相同(profileId, symbol)的作业,chainByKey在进程内将它们串行化。
订单幂等还依赖确定性clientOrderId的碰撞合并(见不变量与反模式部分),其长度校验与哈希原语集中在 packages/strategy/core/src/client-order-id.ts:BINANCE_CLIENT_ORDER_ID_MAX = 36,assertClientOrderId在超长时大声抛出而非让交易所静默拒绝;djb2Hex是唯一的订单 id 碰撞压缩原语,且刻意不用crypto(策略包禁止导入 crypto)。
2.3 不变量三:易用与响应式(Approachable & responsive)
面向单人运营者而非金融专业人士:
- 使用通俗语言(plain language);交易术语首次出现时必须附带术语解释(gloss);绝不假设读者具备屏幕之外的背景知识。
- 移动端优先:每个视图都必须在 375×667 下可用,再向上扩展。
2.4 不变量四:账户级作用域——按「行」隔离而非按「约定」隔离
这是本项目数据模型最核心的设计,分四层展开:
模型(Model):一个运营者(users)→ 多个accounts→ 多个profiles。accounts.owner_id → users.id;每个账户持有一对 Binance Key、一个binance_mode、一条 user-data 流;profiles.account_id把每个档案绑定到其账户,共享该账户的 Key/钱包/流。
品牌化两级作用域(Branded two-tier scope):AccountScope{db, operatorId, accountId}由scopeAccount铸造,ProfileScope{db, operatorId, accountId, profileId}由scopeProfile铸造——用一条查询证明整条所有权链。UserId与AccountId是互不相同的品牌化类型(branded types),把二者放错位置会导致编译错误。
源码印证:作用域与品牌的完整实现位于 packages/db/src/repo/_scoped.ts。品牌是模块私有的unique symbol(accountScopeBrand/profileScopeBrand),不导出,因此消费者无法从外部合成一个带品牌的值——手写字面量不可赋值,强转也会因 symbol 键不可命名而失败。scopeProfile通过一次accounts ⋈ profiles内连接完成operator owns account, account owns profile的链条校验,任何越权都会抛出AccountNotOwnedError/ProfileNotOwnedError(由 API 映射为 404 而非 500)。另外 packages/db/src/repo/_scoped.ts 明确要求:scope 严禁序列化——JSON.stringify会丢弃 symbol 键属性,所以 scope 只能作为每请求的缓存令牌,绝不能放进 BullMQ 负载或任何线上格式。
访问规则(Access rule):应用代码只能通过profileRepo(...)/profileRepoFromScope(scope)(或账户级表面的accountRepo(...)/accountRepoFromScope,来自@app/db)访问账户级数据;禁止手工传递(db, operatorId, …)。每个账户级 repo 函数第一个参数必须是AccountScope/ProfileScope——所有权在编译期恰好证明一次。扁平的repo.<x>仅保留给运营者/全局函数(如repo.users.*、repo.accounts.create/listForOwner、留存清理)。
路由(Routes):API 走/api/accounts/:accountId/...,Web 走/accounts/$accountId/...。
执行(Enforcement):由 packages/db/tests/repo/ast-check.test.ts 加跨账户集成测试强制,不使用数据库侧 RLS。详见 docs/architecture/account-isolation.md 与 docs/architecture/database.md。
跨符号状态(Strategy state):策略状态在存储与tick()中都是按(profile, symbol)切片的——每次调用每个符号一片。跨符号状态必须走按档案的 KV 存储:通过set-kv/delete-kv决策写入策略自有的命名空间键,再通过TickInput.profileKv读回合并快照(用capabilities.needsProfileKv显式选择加入)。KV 写入对兄弟符号在后续 tick可见,绝不在同一 tick 内可见。对应源码见 packages/strategy/core/src/decision.ts 的set-kv/delete-kv决策变体与 packages/strategy/core/src/contract.ts 中TickInput.profileKv与Capabilities.needsProfileKv的完整注释。
三、技术栈(Stack)与仓库布局
3.1 技术栈一览
章程明确指出:版本以package.json/ lockfile 为唯一事实源,下面只列名称。根 package.json 确认包管理器为bun@1.4.2,引擎要求bun >= 1.4。
| 领域 | 选型 |
|---|---|
| 运行时 | Bun |
| 单仓管理 | Turborepo |
| CI | GitHub Actions + GitLab 均调用scripts/ci/*.sh |
| 后端 | Hono |
| 前端 | Vite + React + TanStack Router/Query + Tailwind + shadcn/ui(PWA) |
| 图表 | lightweight-charts(金融类)、Recharts(非金融类) |
| 数据库 | Postgres + TimescaleDB,ORM 为Drizzle,迁移为手写 SQL + 校验和追踪运行器 |
| 缓存/队列 | Redis + BullMQ(ioredis,maxRetriesPerRequest: null) |
| 认证 | Better Auth——无 email/SMTP、无 2FA(单一主账户),见 docs/architecture/auth.md |
| 静态加密 | 无——Binance Key 与通知器密钥明文存储,依靠运营者侧 Binance IP 白名单缓解,威胁模型见 docs/architecture/auth.md |
| 金额 | decimal.js:所有价格/数量/金额/余额/盈亏端到端使用Decimal;number仅用于计数器/索引/毫秒时间戳;快照把Decimal序列化为字符串,策略在边界处恢复 |
| 通知 | 可插拔@app/notifyprovider(首个为 Slack) |
| 技术指标 | 进程内 packages/indicators/rating(vendored MIT);worker 的technicals-computecron 本地计算评级,无外部扫描器 |
| 容器 | 单镜像多阶段构建(apps/server/Dockerfile),ROLE=all\|api\|worker\|study选择行为;apps/web仅构建(api 同源托管 SPA);伸缩拆分用docker-compose.scale.yml |
| Worker | 当前单副本(WORKER_REPLICAS=1),无分布式锁;单次执行 = BullMQjobId合并 + 进程内chainByKey+ 幂等clientOrderId+ 版本感知的symbol_statesCAS。弹性池横向扩展的管线已合并但休眠 |
其中WORKER_REPLICAS的语义在 packages/core/src/env/catalogue.ts 有完整描述:compose 专用、默认1,注释明确写着「Keep it at 1. Multi-replica scale-out is merged but dormant.」——这是章程「单副本」说法的权威佐证。部署侧的伸缩入口在 deploy/compose/docker-compose.scale.yml(replicas: ${WORKER_REPLICAS:-1})。
3.2 仓库布局(Repo layout)
apps/ api/ web/ worker/ server/ # server = ROLE-selectable entrypoint (api + worker) packages/ strategy/ core/ trailing-trade/ # core = contract + Executor; TT = first plugin indicators/ # pure, Decimal-typed candle/window math notify/ # @app/notify: contract + registry + providers/{slack,telegram,webhook} binance/ db/ contracts/ config/ core/ # shared runtime utils; subpath exports (./env, …) deploy/compose/ scripts/ci/注意一个容易踩坑的细节:packages/strategy/*的目录分组只是文件系统层面的,npm 包名保持扁平(@app/strategy-trailing-trade);@app/notify是单个包,provider 通过子路径导出。
四、必备命令(Required commands)
以下命令全部来自章程的表格,并已在根 package.json 的 scripts 中逐条核实:
| 命令 | 用途 |
|---|---|
bun install | 安装 workspace 依赖 |
bun run dev | 通过 turbo 同时启动 api+web+worker |
bun run lint | oxlint + 不变量门禁 + prettier,必须保持干净 |
bun run typecheck | 全包tsc --noEmit -b,必须保持干净 |
bun run test | Vitest;各 workspace 覆盖率阈值由COVERAGE_POLICY分配完整套件覆盖率流水线强制执行 |
bun run test:e2e | Playwright e2e |
bun run test:worker-integration | apps/worker集成测试流水线(可在笔记本上运行);TESTCONTAINERS=1时启动一次性 Postgres + Redis,因此需要可达的 Docker daemon,无 daemon 时打印提示而跳过 |
bun run db:migrate | 应用手写 SQL 迁移(校验和追踪运行器) |
bun run db:generate | 不使用——仓库手写NNNN_*.sql |
bun run setup | 首次引导:由.env.example生成.env+ 迁移 |
bun run docs:install | uv sync --group docs;在任何其他docs:*命令前执行一次 |
bun run docs:build | docs:gen --check+uv run mkdocs build --strict——文档门禁 |
docker compose up | 本地栈:postgres、redis、一个ROLE=all应用 |
文档构建环境陷阱(重要):docs 在uv 托管的 Python 环境(pyproject.toml 的[dependency-groups] docs)中构建,绝不使用 PATH 上碰巧存在的mkdocs。裸跑mkdocs build --strict会撞上无关的解释器并报The "<plugin>" plugin is not installed——这个报错意味着环境不对,而不是配置坏了,所以手工安装报错点名的插件是错误的修复方式。正确路径是走bun run docs:build/docs:serve(二者都经uv run调用 mkdocs);先用bun run docs:install(即uv sync --group docs)创建该环境。
五、Lint 机制:oxlint + 不变量 shell 门禁
oxlint 是唯一的 lint 路径(.oxlintrc.json),以全仓单次遍历的方式运行(这样import/no-cycle能看到完整依赖图),启用correctness+typescript/import/oxc/react插件,并靠overrides表达仓库不变量:
- 策略/指标纯度:
no-restricted-globals/-imports; - decimal.js 边界:禁止在策略/指标代码里越过金额类型边界。
react插件开启是为了react/no-unstable-nested-components(在另一个组件 render 体内声明的组件每次渲染都会重挂其子树,在 WebKit 上会钳住scrollTop);开启该插件同时会武装全部reactcorrectness规则,因此react/exhaustive-deps被显式off(待处理其既有违规)。
oxlint 无法表达的不变量由scripts/ci/*.sh门禁承担,从 scripts/ci/lint.sh 驱动。章程点名的主要门禁及其职责:
| 门禁脚本 | 守护的不变量 |
|---|---|
no-plugin-leak | 不变量 1:宿主不得导入具体策略/通知 provider |
no-arbitrary-color-token | 前端不得使用任意颜色 token |
no-phantom-env-var | 环境变量必须有声明 |
no-invalid-mermaid | Mermaid 图表必须合法 |
no-undeclared-workspace-import | workspace 导入必须声明 |
no-wider-metrics-sink | 只有 apps/worker/src/metrics/catalog.ts 可以声明指标 sink;第二个name: string类型的 sink 能通过类型检查却会逃逸指标目录 |
no-unreviewed-tofixed | apps/web/src/** 下每个直接.toFixed(与固定两位maximumFractionDigits站点都必须登记在 scripts/ci/tofixed-inventory.json,附模式身份与事实性的值类型理由——硬舍入正是「亚美分报价余额渲染成 0.00」的根因 |
no-dropped-lint-rule | 断言解析后的 oxlint 配置仍武装着承载不变量的规则 |
no-backfilled-migration | 新NNNN_*.sql必须取下一个未用编号:运行器按文件名顺序应用,插在游标之下的文件会在每个已迁移数据库上最后执行 |
no-mutated-applied-migration | 已发布的NNNN_*.sql不可变;只有固定摘要清单能发现会卡死已部署库的漂移 |
no-bun-version-skew | 九个 Bun 版本钉点必须一致;Renovate 不能可靠覆盖它们,且门禁的读取计数是精确的,某个钉点不再匹配会大声失败而不是悄悄收窄检查 |
no-blind-walk | 每个从树遍历读取结论的门禁都必须使用 scripts/ci/lib/walk.mjs,其collectOrExit拒绝未声明锚点的根,并携带GUARD_ROOT覆盖缝隙供自测使用 |
「只收窄的遍历仍会返回数百个文件,因此门禁打印的计数只有被证明仍能到达规则守护的模块、且其停止点已被观察触发时才是证据」——这是no-blind-walk存在的理由。完整细节见 docs/contributing/coding-rules.md。
六、质量门禁(CI 必须强制执行的 9 条)
章程把 CI 质量门禁明确列为 9 条,逐条如下:
- Lint 干净。
- Typecheck 干净。
- 行为变化必须更新文档——每条声明都必须可追溯到源头,而非记忆。机器强制:
bun run docs:build(先docs:gen --check生成配置表与环境变量参考,再uv run mkdocs build --strict)、no-invalid-mermaid.sh、no-stale-migration-doc.sh;叙事准确性仍是评审门禁,见 docs/contributing/coding-rules.md。 - 各 workspace 完整套件覆盖率阈值达标(packages/config/vitest/coverage-policy.js)。
- 策略 golden fixture 回放 diff = 0。
- Playwright 流水线全绿:
browser-bootstrap证明四个配置的浏览器项目都能启动,并报告每个被跳过的应用执行及其原因(不声称关键路径应用覆盖);app-e2e针对回环 Binance fixture 启动密封的 ROLE=all 栈,在全部四个项目中跑 P0 旅程,任何跳过与任何 fixture 未应答的 Binance 流量都视为失败。 bun run compose:build对每个应用成功。- 迁移纪律(详细条款,见下)。
- JSDoc 质量是评审门禁而非 lint 门禁:每个导出或非平凡函数必须携带块注释,每个参数一条
@param、有返回值时一条@returns;标签上方散文承载 WHY,每个标签解释该值意味着什么——禁止名称复述(如@param symbol - The symbol)、禁止空块。当名称是误名或易误读时,@param行是读者唯一被告知的地方,即使类型看似自明也必须写。
6.1 迁移纪律:编号永不复用、永不回填
第 8 条门禁是迁移文件的操作规程,值得单独展开。迁移由手写NNNN_*.sql组成,按名称顺序由校验和追踪运行器应用(实现位于 packages/db/src/migrate.ts,账本表为_app_migrations),packages/db套件对真实 Postgres 运行migrate()。已发布的迁移不可变,破坏它的两种方式失败方式不同:
- 编辑(改注释也算)会改变其摘要,使所有已迁移数据库卡死在该文件,后续任何迁移都无法修复;
- 重命名或删除对运行器不可见(运行器按名称键控):文件会作为新迁移重新应用,并静默遗留旧账本行。
所以:变更已发布迁移的唯一正确方式是写一个新迁移。no-mutated-applied-migration强制执行这一点(新库套件无法看到这两种情况),而 packages/db/tests/migrate-immutability.test.ts 把两种行为都钉死为测试——注意它每个用例使用独立数据库,因为账本即被测状态,共享会让先前用例的行决定后续用例的结果。编号是同一契约的另一半:新迁移取下一个未用编号,绝不取空洞、字母后缀或低于0001的编号,因为名称顺序即应用顺序——no-backfilled-migration从目录清单强制执行,这是唯一能区分回填与全新树的 oracle。
运行器为何自研(而非 drizzle-kit migrate):需要 drizzle-kit 无法表达的扩展、角色与 TimescaleDB hypertable/保留期 DDL;每个迁移是应用一次的纯 SQL,已应用校验和记录在_app_migrations;文件内幂等(if not exists、do $$ … end$$)可在环境损坏时干净重跑;并采用单一 session advisory lock 串行化并发迁移器(这是迁移期的数据库锁,不是运行时协调锁,不违反单副本无分布式锁不变量)。
6.2 注释排版规则:禁止硬换行(Do not hard-wrap comments)
一个段落一行,一个@param/@returns标签一行,无论多长——JSDoc 块与//注释一视同仁。这正是 prettier.config.mjs 对 Markdown 已生效的proseWrap: 'never'规则的同一理由:硬换行的散文会让每次后续编辑变成多行重排,diff 因此会隐藏真正被改的句子。没有任何机制强制这一点(printWidth只管代码,prettier 从不重排注释正文),所以它是评审门禁;已有被换行的注释在相关代码被改动前保持原样。换行服务于意义而非宽度:项目符号列表、- x:表格、ASCII 图保留其换行。
七、必须拒绝的反模式(Anti-patterns to refuse)
章程给出了一份明确的「红线清单」,任何 Agent 在编码时遇到都应拒绝:
- NotifyProvider 单一事实源:provider 形状定义在
@app/notify;apps/api消费notifyProviders.describeAll(),绝不维护自己的 provider 接口。 - 无分布式锁:禁止
redlock、intents:Redis 集合、软余额预留或等价物(scripts/ci/no-locks.sh 会失败,并扫描packages/binance)。允许的共享 Redis 原语只有速率限制/可见性,不是锁(无 owner、无 release/refund):消费即衰减的请求权重桶,以及自过期SET NX PX的通知器间隙节流。 - 磁盘上无明文 API Key(Postgres 例外之外),且只允许在 docs/architecture/auth.md 的缓解措施下(单租户、运营者侧 Binance IP 白名单)。任何新存储面(备份、副本、导出、多租户)必须先有静态加密。
LIVE_DEMO响应中无秘密:公共演示注入了唯一运营者 id,所以每条不在requireNotDemo之后的路径都可匿名访问。凭证等价物——Binance API Key、通知器webhook URL、bot token、auth header、AI-provider key——绝不能序列化进任何响应体。在投影边界剥离它(api-key →last4,ai-provider →has*布尔,notifiers 在secretFields中声明秘密),使泄漏在构造上不可能。requireNotDemo是黑名单/纵深防御:新开的秘密读取或凭证写入路由默认暴露——必须加入拒绝清单,并加入 apps/api/tests/routes/live-demo-guard.test.ts 的完备性断言。- 无投机抽象:特性开关、可配置性或「为了将来」的间接层,必须有关联具体消费者的已跟踪 issue。
- 金额数学 =
Decimal:在packages/{strategy-*,indicators,contracts}下游禁止number(IEEE-754);decimal.js 导入边界由 lint 强制(oxlintno-restricted-imports)。 Decision联合保持通用:noop/place-order/cancel-order/replace-order/emit-event/set-kv/delete-kv(源码见 packages/strategy/core/src/decision.ts)。通用交易所请求形状(如replace-order)属于联合;策略特有的副作用走带命名空间键的emit-event,而不是新增策略特有变体;跨符号耦合走set-kv/delete-kv。移除一个变体要连带移除其 worker 侧端口——不留闲置缝隙。- 策略与指标保持纯净:
packages/strategy-*/src/**与packages/indicators/src/**内禁止ioredis/pg/drizzle-orm/fs/net/http/crypto/Date/Math.random;tick()内禁止 I/O(worker 注入Clock与RNG);decimal.js允许。 - 注释写为什么,不写是什么:代码内禁止 Spec / Issue / Phase /
@see引用;提交与 PR 负责承载这些;CI 拒绝。 - 测试位于
<pkg>/__tests__/,绝不放在src/旁;CI 拒绝。 - 新的共享运行时工具进入
@app/core的新子路径,而不是新包——除非该工具具有清晰领域边界(策略、通知、binance、db、contracts)。
八、策略插件的纵深:Decision与Strategy契约速览
章程反复提到Decision联合、tick()与插件契约,这里结合核心源码给出契约级速览,帮助你理解「插件为何能无缝接入」:
OrderIntent携带reason(策略自有的命名空间字符串,落入orders.intent)、clientOrderId、可选meta(策略自有元数据,原样持久化到orders.metajsonb,核心 schema 不命名任何策略概念)、可选overrideActionId、可选deferrable(标记「改进性而非必要性」订单:账户订单预算无余量时执行器跳过而非等待,避免占用 per-(profile, symbol) 链锁延误下一个 tick 的退出检查;退出、入场、首次挂止损失单绝不设此标记)。见 packages/strategy/core/src/decision.ts。replace-order是一个 cancelReplace 原子语义:一次POST /api/v3/order/cancelReplace以STOP_ON_FAILURE取消挂单并放置继任者,取消与下单之间绝无「两者皆无或两者并存」的窗口。见 packages/strategy/core/src/decision.ts。Strategy接口(packages/strategy/core/src/contract.ts)要求configSchema/overrideConfigSchema必须是 zodobjectschema(API 会序列化为 JSON Schema 供 SPA 生成配置表单);events事件图让emit-event的每个发射点在编译期同时校验eventType与payload;position(PositionStateAdapter)让 worker 的 boot 调和器、fill-adopter 在不导入插件具体状态 schema 的前提下收敛持仓(不变量 1 的又一体现);attributeOrder用确定性重算回答「这个clientOrderId是不是本档案本符号下的」——这是 API 收养孤儿订单而无需运营者猜测的唯一依据,null是硬拒绝而非「随便挑一个」;previewLevels投影「配置会如何行动」的价格/层级行,供运营者交易前预览与漂移门禁共用。- 纯函数即安全:
lintConfig、checkOrderFeasibility、protectiveStopBandSettings、previewLevels、extractAudit、requiredWindow、mergeConcurrent等均要求纯、确定、无 I/O——tick()同样如此,时钟与随机数由 worker 注入。
九、给编码 Agent 的实战清单
综合以上,在 binance-trading-bot 中做任何改动前,请把这份清单作为第一道自查:
- 不变量先行:改动是否触碰四条不变量之一?策略/通知的新增是否遵循「新包 + 注册表条目」而非改宿主?参考 packages/strategy/registry/src/index.ts 与 packages/notify/src/registry.ts 的引导模式。
- 账户数据只走 scope:任何账户级访问都必须先
scopeAccount/scopeProfile铸造品牌化 scope,再交给 scoped repo 函数;不要手工拼接(db, operatorId, …)。 - 金额一律
Decimal:策略、指标、contracts 下游禁止number参与金额计算。 - 迁移三问:是手写
NNNN_*.sql吗?编号是下一个未用数字吗?已发布的迁移一个字节都没动吗?(改注释也算动。) - 秘密不外泄:新路由若读秘密或写凭证,默认暴露——必须加入
requireNotDemo拒绝清单与 live-demo-guard 完备性断言。 - 文档同步:行为变化必须更新 docs,
bun run docs:build必须通过。 - 注释写 WHY:一个段落一行、一个 tag 一行,禁止硬换行;代码内不写 Spec/Issue/Phase 引用。
- 测试放
__tests__/:绝不与src/并列;否则 CI 拒绝。
这份章程的价值在于把「正确性优先、可扩展、可靠、单运营者友好」从口号变成了可机器强制的工程约束——lint 门禁、类型系统(品牌化 scope)、shell 门禁与测试共同把误用变成编译错误或 CI 失败,而不是运行时事故。
- 金融科技
- 后端
- 前端
【免费下载链接】binance-trading-bot
Automated Binance trading bot with pluggable strategies, historical backtesting, and a live dashboard
相关推荐
5分钟上手res-downloader:跨平台资源嗅探与代理下载完整指南
5分钟上手res downloader:跨平台资源嗅探与代理下载完整指南 res downloader 是一款基于 Go 与 Wails 开发的桌面应用,通过本
金融科技后端前端Binance-volatility-trading-bot 使用教程
Binance volatility trading bot 使用教程 1. 项目的目录结构及介绍 Binance volatility trading bot
金融科技区块链Jamstack ECommerce与Stripe支付集成:安全交易处理的完整实现
Jamstack ECommerce与Stripe支付集成:安全交易处理的完整实现 Jamstack ECommerce是一个基于Next.js和React构建
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考