source: claude-code · 512K+ lines TypeScript

Claude Code 源码
学习构建交易机器人

CC 本质上是一个感知 → 决策 → 执行 → 观察的循环系统。 交易机器人的架构需求高度相似。从源码中提炼 12 个可复用的架构模式。

12 架构模式
1:1 CC ↔ 交易映射
01

核心循环架构

The Agent Loop → Trading Loop

源码洞察

CC 的核心循环不是简单的 while-true。它是一个 async generator,每次迭代构造完整的不可变 State 对象传给下一轮。更关键的是:它定义了 7 个显式退出点,每个都有明确的 reason 枚举(max_turns、token_budget、stop_hooks、prompt_too_long 等)。循环还通过 transition 字段记录"为什么要继续"——这不是日志,而是状态机的一部分。
CC 循环结构
  1. 构建上下文(多级 compact)
  2. 调用模型 API(streaming)
  3. 解析响应,提取 tool_use
  4. 执行 tools(并发或串行)
  5. 收集结果,更新消息历史
  6. 检查 7 个终止条件
  7. 生成 next State,循环
交易循环结构
  1. 采集市场数据
  2. 生成交易信号
  3. 风控管道检查
  4. 执行交易
  5. 更新持仓状态
  6. 检查终止条件
  7. 生成 next State,循环

设计原则:把整个循环的状态打包成一个对象,每轮循环生成一个新的。好处是随时可以拍快照——出了问题直接回看"当时仓位多少、信号是什么、为什么没止损",不用从日志里拼凑。每次循环还要记录"为什么继续":是因为收到了新信号?还是在做风控再平衡?复盘时这比"这一轮做了什么"有用得多。

02

工具系统

Tool Registry + Dispatch

源码洞察

CC 把 40+ 种操作统一为一个 Tool 接口,每个 tool 必须声明三个属性:isConcurrencySafe()isReadOnly()isDestructive()。这些不是文档标注——它们直接决定运行时行为:并发调度器根据 isConcurrencySafe 自动分区,权限系统根据 isDestructive 触发额外确认。每个 tool 还带 inputSchema(Zod),在调用前强制校验参数。

设计原则:每个操作自带"能否并发""是否只读""有没有破坏性"三个标签。系统根据标签自动决定怎么执行——查余额自动并发跑,下单自动排队串行,平仓自动触发二次确认。不用到处写 if-else 判断"这个操作要不要等上一个完成"。加一个新操作只需要填三个标签,调度、权限、监控全自动适配。

03

并发工具执行

Tool Orchestration

源码洞察

CC 不是简单地"只读并发、写入串行"。它用 partitionToolCalls() 把一组调用按连续的 isConcurrencySafe 分区成批次。并发批次中的 state 修改会被排队,等整个批次结束后才统一应用;串行批次中的 state 修改则立即生效,影响后续工具。并发批次中如果一个工具出错,会通过 siblingAbortController 级联取消所有兄弟——不是静默忽略。

设计原则:同时查 5 个交易对的行情,结果回来后不能每个立刻改 state——否则第 3 个回来时看到的 state 已经被第 1、2 个改过了,数据不一致。正确做法:5 个结果全收齐,一次性合并更新。但下单不一样:先检查余额 → 再下单 → 再确认成交,每一步必须立刻看到上一步的结果。另外,5 个并发查询中如果一个交易所断了,其余 4 个也应该立刻取消,不要出现"查了一半"的状态。

04

重试与错误恢复

Error Classification + Graduated Retry

源码洞察

CC 的重试不是通用的"失败就重试"。它先把错误分类,然后走不同的决策分支:限流遵守 retry-after;连续 3 次 529 触发模型降级(Opus → Sonnet);后台任务的限流错误直接抛出不重试(避免放大风暴);Prompt Too Long 不重试而是触发 compact。还有一个"持久模式":无人值守时无限重试,但把长等待拆成 30 秒的心跳 chunk,防止 host 判定进程 idle。

设计原则:不是所有错误都该重试。网络抖动?等一下重试。交易所限流?遵守它告诉你的等待时间。连续被限流 3 次?别重试了,切到备用交易所。余额不足?重试 100 次也没用,直接报错。定时任务的限流错误更不能重试——10 个定时任务同时重试,只会让限流更严重。先搞清楚是什么错误,再决定怎么处理。

CC 没有面对但交易必须解决的问题:幂等性

CC 的重试天然幂等(再问一次 LLM 不产生副作用)。但下单重试可能导致重复成交。必须使用 clientOrderId 确保幂等,并在重试前先查询上一次请求是否已成功。
05

状态管理

Immutable Snapshots + Pure Updates

源码洞察

CC 的 state store 是极简的三件套:getStatesetState((prev) => next)subscribe。子 agent 拿到同一个 store 引用但 setState 被替换成 no-op——子 agent 可以读全局状态,但不能写。唯一的例外是 setAppStateForTasks,它绕过 no-op 直达 root store,因为任务注册必须全局可见。这种"默认只读 + 白名单可写"很精巧。

设计原则:趋势策略想看当前持仓?可以看。想直接下单?不行——必须把下单请求提交到统一通道,通道会自动过风控。这样不管你跑多少个策略,所有订单都必经同一个风控管道,不存在"策略 A 绕过检查直接下了一笔"的可能。状态也永远不会被某个策略的 bug 搞乱,因为没人能直接改它。

06

权限与风控系统

Multi-layer Decision Pipeline

源码洞察

CC 的权限检查不是单一 if-else,而是多层管道:先查配置规则(5 层优先级:policy > workspace > project > local > user),再跑 hooks,再跑 classifier,最后才弹交互式确认。另一个精巧的设计是 denial tracking:连续 5 次被拒或 1 分钟内 10 次被拒,系统自动切换模式——因为连续被拒通常意味着系统行为异常,需要人介入。
1
硬性规则:单笔最大金额、总敞口上限
不可覆盖
2
仓位检查:新订单是否突破敞口限额
有优先级
3
频率异常检测(连续被拦截 → 策略可能故障)
熔断触发
4
回撤检查:日亏损是否超限
全局熔断
5
大额订单 → 人工确认
通过

设计原则:风控不是一个函数,是一条流水线。订单先过"金额上限",再过"仓位检查",再过"频率检查",再过"回撤检查",每一关都能拦。更妙的是:如果一个策略连续 5 次被风控拦截,系统不会傻傻地继续拦——它会判定"这个策略本身可能出了问题",自动暂停策略并告警。这不是在检查某笔交易,而是在检查系统行为是否正常。

07

上下文压缩

Multi-tier Data Lifecycle

源码洞察

CC 的 context 压缩有 5 级策略:Snip(删最老的)→ Microcompact(缓存计算结果替换原始数据)→ Context Collapse(投影视图)→ Autocompact(LLM 生成摘要)→ Reactive Compact(紧急截断)。压缩后还会重新注入最重要的 5 个文件,预算上限 50K tokens。
CC 层级 交易机器人映射 触发时机
Snip丢弃 N 天前的 tick 数据每轮循环
Microcompact原始 K 线 → 预计算 MA/RSI数据窗口滑动时
Autocompact交易记录 → 统计摘要定期 / token 超限
Reactive紧急丢弃非活跃交易对数据内存压力
恢复关键数据始终保留当前持仓 + 活跃订单每次压缩后

设计原则:历史数据越来越多怎么办?不要纠结"这条能不能删",直接大刀砍掉旧数据,然后把最重要的东西(当前持仓、活跃订单)完整地补回来。听起来粗暴,但比小心翼翼地挑选"哪些可以删"简单得多,而且关键数据永远是完整的。CC 里每次压缩后都会把最重要的 5 个文件重新注入,预算 50K tokens——先砍后补,简单可靠。

08

流式处理

Streaming Execution

源码洞察

CC 的 StreamingToolExecutor 不等响应结束就开始执行——一旦解析出完整的 tool_use block 就立即调度。并发安全的工具可以与尚未完成的流式响应并行执行。如果流中断,已调度但未完成的工具会被标记为 tombstone(墓碑),不是简单丢弃,而是保留标记让下一轮循环知道"这里有未完成的操作"。

设计原则:行情数据来一条处理一条,不用等"这一秒的数据都到齐了"再算。但断线时要小心:WebSocket 断了,你刚提交的订单到底成交了没有?CC 的做法是给未完成的操作打一个"墓碑"标记——重连后系统看到墓碑,就知道"这里有个操作状态未知,先查清楚再继续",而不是当作没发生过。

09

插件与集成

Dynamic Tool Discovery

源码洞察

CC 的 MCP 集成不是静态配置。它通过 list_tools() 在运行时动态发现外部工具,转换成统一的内部 Tool 格式后注入 registry。工具的元数据(readOnlyHintdestructiveHint)直接映射为运行时行为标记。LRU 缓存限制最多 20 个 server 连接。认证失败缓存 15 分钟避免重复 OAuth。会话过期时自动清除缓存并重试(最多 1 次)。

设计原则:加一个新交易所不应该改策略代码。每个交易所是一个"插件",启动时自动注册自己支持的操作(下单、撤单、查余额…),策略只管调用统一接口。Binance 挂了?切到 Bybit,策略代码一行不改。连接数也要管——CC 限制最多 20 个连接,用 LRU 淘汰最久没用的;认证失败缓存 15 分钟,避免反复触发 OAuth 登录流程。

10

Hook 系统

Lifecycle Interception

源码洞察

CC 的 hooks 不只是事件回调。它们是 async generator,可以 yield 多个中间结果。Hook 的返回值直接决定工具是否执行:exit 0 = 继续,exit 1 = 阻止,exit 2 = 阻塞等待(hook 可以挂起整个执行流等外部条件满足)。hooks 还支持 asyncRewake 模式——后台执行完成后通过任务通知系统唤醒主流程。
pre_order

风控检查、滑点保护

post_order

日志、通知、状态更新

order_filled

更新持仓、触发止盈止损

order_rejected

告警、策略自动调整

position_closed

计算 PnL、更新统计

exchange_disconnect

紧急风控、tombstone 标记

设计原则:普通的事件系统只能"通知"——订单提交了,发个消息。CC 的 hook 能"拦截":下单前跑 pre_order hook,hook 说"不行,滑点超限",这笔单直接不下。甚至可以"挂起":hook 说"等一下,我去查外部风控系统",整个流程暂停等结果回来再继续。这比"先下了再说,出了问题再撤"安全得多。

11

子 Agent 模式

Multi-Strategy Isolation

源码洞察

CC 的子 agent 是递归调用同一个 query() 函数——不是另起进程。子 agent 继承父 agent 的 state store 引用,但 setState 被替换成 no-op(只读)。唯一的例外是任务注册,它有一条专用通道直达 root store。子 agent 的 fork 模式更精巧:所有 fork 子 agent 共享字节级一致的 API 请求前缀,确保 prompt cache 可复用。

多策略并行架构:

主循环 ── 全局状态管理 + 风控守卫

  ├── 子策略:BTC 趋势跟踪(独立循环、只读全局状态)

  ├── 子策略:ETH 均值回归

  ├── 子策略:跨交易所套利

  └── 风控守卫:全局监控(唯一有写权限的子进程)

设计原则:跑 5 个策略,每个策略都能看到全局持仓和行情,但没有一个能直接下单。想下单?提交到统一通道,过风控后才执行。BTC 趋势策略出 bug 了?直接关掉,ETH 策略和套利策略完全不受影响,全局状态也不会被搞乱。CC 里子 agent 的 setState 就是被换成了空函数——代码层面就不可能"偷偷"改全局状态。

12

可观测性

Structured Telemetry

源码洞察

CC 的遥测不是撒网式 console.log。它在精确的决策点记录结构化事件:每次 auto compact 记录压缩前后的 token 数和消息数;每次 retry 记录 attempt/delay/error/provider;每次模型降级记录原始模型和 fallback 模型。事件包含 queryChainIddepth 用于关联主 agent 和子 agent 的调用链。

设计原则:不要在"发了一个请求"的地方打日志,要在"做了一个决定"的地方打。"切换到备用交易所"比"发了一个 HTTP 请求"有用一万倍。每条日志带一个 traceId,把整条链路串起来——出了问题可以追溯:这笔亏损的单是哪个策略、在什么行情下、经过哪几层风控、最终在哪个交易所成交的。没有 traceId,5 个策略并行跑的时候,日志就是一锅粥。

总结映射表

CC 架构模式 设计原则
Query Loop状态打包成对象,每轮记录"为什么继续"
Tool Interface三个标签自动决定:并发跑、排队跑、还是要确认
Batch Partitioning并发结果收齐后一次性合并,串行每步立即生效
withRetry先分类再处理:重试、降级、报错、还是先做别的
Immutable Store策略能看不能改,想下单必须走统一通道过风控
Permission Pipeline流水线逐层过,连续被拦 = 策略可能出故障
5-tier Compaction大刀砍旧数据,然后把持仓和活跃订单补回来
Streaming + Tombstone来一条处理一条,断线的操作打墓碑标记
Dynamic Discovery交易所是插件,加新所不改策略代码
Lifecycle Hooks不只通知,能拦截、能挂起等外部条件
Sub-agents5 个策略并行跑,关掉一个不影响其余
Chain Telemetry在"做决定"的地方打日志,traceId 串起全链路