【免费下载链接】LangAlpha
Claude Code for Financial Market
LangAlpha 是一个面向金融市场的 AI Agent(定位是 "Claude Code for Financial Market"),它能连接 Robinhood、IBKR、moomoo 等券商做实盘交易。它最硬核的设计,就是订单治理路径:一次审批,只执行一次——用户点一次"批准",这笔单最多只会发给券商一次,重试、重放、并发都改变不了这个事实。
为什么"一次审批只执行一次"是AI交易的生死线?
想象 AI 帮你下单买入 100 股 NVDA:
- 你点了一次"批准"
- 请求发到一半,网络抖了一下 / 服务重启了一次
- 重试逻辑把同一笔单又发了一次 →你实际买了 200 股
对 AI 实盘交易来说,这是最危险的故障:重试本是提高可用性的常规手段,但在"花钱"的调用上,重试等于加倍支出。LangAlpha 的解法不是"小心点写重试",而是把审批变成一张一次性的执行凭证,用数据库行来记账,让任何重放都无处落脚。
核心账本表由迁移脚本 migrations/versions/043_order_attempts.py 创建,它的设计注释把规则写得很直白:
审批过去授权的是"图的前进";这张表让它改为授权一次调用、一组参数的一次执行:行在询问用户之前写入,按 id 裁决,执行时恰好被消费一次,最后以券商的回答收尾。
订单账本:每笔单都有唯一"身份证"
系统为每一次"AI 想下单"的调用先写一行order_attempts记录,唯一键是(user_id, message_id, tool_call_id)——用户、产生这条调用的消息 id、工具调用 id 三者共同命名"这一笔调用"。为什么不能只用调用 id?因为某些模型供应商会按对话位置编号,换一条会话就可能重复;加上消息 id 后,真正被识别的"重复"只剩下那一种:审批后恢复运行时的合法重入。
这一行记录还故意不带外键:删掉聊天线程不能删掉"有人拿真金白银下过单"的证据(见 src/server/database/order_attempts.py)。
审批五步流水线:每一步都只进一次
订单状态在 src/server/services/brokerage_orders/models.py 中定义了完整生命周期。关键路径是:
proposed(提案)→ approved(已批)→ submitting(提交中)→ submitted/filled/…(终态)
第 1 步:先记账,再问人
治理中间件 order_governance.py 在把审批卡片弹给用户之前,就先把 attempt 行写进数据库。这解决了"位置式回答"的经典缺陷:用户的"同意"不是回答"流程可以继续",而是针对某个attempt_id的裁决,写回时按 id 匹配,重放一张旧卡片改不动已经裁决过的行。
第 2 步:不回答 = 拒绝
decide_attempt()用带守卫的UPDATE ... WHERE status = 'proposed'写裁决——重复点击、迟到响应、并发 worker,只有第一个能赢。更关键的是缺省即拒绝:审批响应里没有针对这笔 attempt 的裁决,代码明确把它当"未获批准"处理并拒绝发送,而不是把沉默当成许可。
第 3 步:consume-once,一次性执行授权
consume_attempt()(order_attempts.py#L172-L201)是整个设计的题眼:
- 把
approved改成submitting的这条 UPDATE 带WHERE status = 'approved'守卫; - 同一个 attempt 被并发命中 N 次(重试、重放帧、第二个 worker),恰好一个能读到
approved并拿到执行授权,其余全部落空、返回拒绝; - 拿到授权的调用同时领到一枚短命的执行 token(默认 120 秒过期,见 execution_token.py),它签名绑定了 attempt、工具、参数哈希,无法挪用到另一笔单。
第 4 步:出口中继的最后一道闸门
真正发往券商的请求要穿过 egress relay(relay.py)。它在放行前做三件"只信证据、不信口头"的事:
- 从请求体重新计算参数哈希,与账本行的
args_sha256比对; - 验证执行 token 的 MAC 签名;
- 要求行状态恰好是
submitting。
之后claim_dispatch()再把dispatched_at打上一枚只打一次的戳:同一签名帧的第二个副本在 token 有效期内到达,会发现 claim 已被占用而拒发。也就是说,"审批消费"和"帧出站"是两层独立的一次性闸门。
第 5 步:用券商的回答收尾
complete_attempt()只在状态仍是submitting时写入券商回执(订单号、成交数量、均价、费用)。成交数量用NUMERIC而非浮点存储——从线路进来的数字是精确十进制文本,二进制舍入会让系统报出和券商不同的成交价。迟到的第二份回答会被直接丢弃,不会覆盖第一份。
审批开关中途变化,怎么办?
迁移 migrations/versions/042_order_approval_modes.py 把"是否要审批"从单一布尔改成按模式(live / staged / 模拟盘)的开关组。系统还处理了两个刁钻的竞态:
- 连接变了:同一券商行背后换了登录账号,所有尚未出站的旧审批被
refused并附上原因"连接已变化,请重新提案",绝不让新账号替旧账号的钱做主; - 开关打开了:用户把"免审批"改回"需审批"时,那些已自动批准但还没发出去的 attempt 会在 refuse_unasked_attempts() 中统一拒绝,并借助 advisory lock 与
consume_attempt互斥,保证没有一笔单能溜过审查。
兜底:发出去了但没回音?
如果请求越过 relay 后回答丢失了(worker 挂掉、超时),行会停在submitting或working。对账作业 src/server/services/orders/reconcile.py 每分钟扫一次仍在流动的行,通过同一个 relay 只读券商自己的订单列表来对账——它只调用只读工具,永不自己下单、改单或撤单。带dispatched_at戳而没回音的行会被标为unknown("可能已到券商"),直到券商列表给出定论。
代码结构速查
| 职责 | 位置 |
|---|---|
| 订单账本与一次性消费 | src/server/database/order_attempts.py |
| 治理中间件(提案/裁决/消费) | src/ptc_agent/agent/middleware/order_governance.py |
| 执行 token 签发与验证 | src/server/services/egress/execution_token.py |
| 出口中继与出站 claim | src/server/services/egress/relay.py |
| 账本端口实现 | src/server/services/egress/order_ledger.py |
| 状态与订单模型 | src/server/services/brokerage_orders/models.py |
| 对账作业 | src/server/services/orders/reconcile.py |
| 账本建表 / 审批模式迁移 | migrations/versions/ |
小结
LangAlpha 回答"AI 实盘交易如何安全"的方式很朴素:不信任任何一次内存判断,而是把"一次审批 = 一次执行"落成数据库里一条不可复用的行,用带状态守卫的 UPDATE 做互斥,用签名 token 和出站 claim 双闸门防止重放,再用对账作业兜住所有"没有回音"的订单。审批不是开关,而是一张一次性的门票——这也是它敢让 AI 拿着真钱跑实盘的底气所在。
【免费下载链接】LangAlpha
Claude Code for Financial Market
相关推荐
LangAlpha子代理团队实战:如何让多个AI分析师并行研究一只股票
LangAlpha子代理团队实战:如何让多个AI分析师并行研究一只股票 LangAlpha 是一款开源的 AI 金融研究助手,它内置了子代理(Subagents
himalaya 凭据命令每次运行只执行一次:SecretResolver 与 CommandConfig 的落地解析
himalaya 凭据命令每次运行只执行一次:SecretResolver 与 CommandConfig 的落地解析 本篇技术指南聚焦 himalaya 邮件
CLIhimalaya 凭据命令单次解析:SecretResolver 如何让 account check 只解锁一次 pass/gpg
himalaya 凭据命令单次解析:SecretResolver 如何让 account check 只解锁一次 pass/gpg 导读 himalaya 的
CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考