Junliang LyuFIELD NOTES
← 返回项目

精选作品

投资纪律系统

一个个人投资纪律系统:用写成代码的规则检查投资决定,从 SEC 财报取证据,AI 模型只做受约束的反方,从不做顾问。

为什么做这个项目

第一版是一套写成文档的规则和检查清单,配合聊天助手使用。它有两个问题:规则只有在我记得时才会被执行;助手会给出很有把握的分析,但其中的数字我无法追溯到来源。

第二版把规则写成代码,财务数字直接取自 SEC 申报文件,并给语言模型一个很窄的任务:只用给定的证据,反驳我的投资论点。它从不建议买入、卖出或持有,也从不填写投资 memo 中应由我本人决定的部分。

公开部分运行在 invest.jun-liang-lyu.com,使用虚构组合。我自己的持仓和 memo 保存在私有、不进 git 的目录中,公开服务从不读取。

规则是代码,不是提醒

十五类规则覆盖单一持仓上限、主题暴露、核心与卫星仓位配置、超过一定仓位必须有 memo、缺少失效条件、复盘逾期,以及没有事先检查的交易。参数保存在带版本的规则文件里,改一个上限是一次可审查的修改,而不是一时的情绪。

交易前闸口会模拟拟议交易,在交易后的组合上重新运行规则,并逐项走检查清单。有些项目自动判断;另一些是自我确认,例如"买入不是主要为了摊低亏损仓位的成本"。闸口给出四种结果之一:通过、未完成、警告、违反规则。它不会说"买"。

我目前的个人规则集只发出警告。我的策略还没有定下来,想先研究别人怎么配置,所以系统也会读机构的 13F 持仓申报(见下一节)。

证据来自申报文件

季度数字来自 SEC XBRL company facts。很多公司后面几个季度只披露年初至今的累计值,所以管道用减法推出单季数字,并为每个推导值标注公式和来源文件。分部业绩(例如 Google Cloud 的收入和营业利润)从每份申报文件内的维度数据中读取。

我没有直接相信输出,而是逐一对照公司新闻稿:例如 Alphabet 2026 年第二季度收入 $119.80B、微软 2026 财年第四季度收入 $90.01B、美光 2026 财年第三季度收入 $41.46B。这些数值现在是黄金测试。

定性信息方面,管道用 BM25 从 10-K、10-Q 正文(例如风险因素和管理层讨论)中检索段落。模型引用的任何原文都必须在这些段落中逐字出现。

看别人怎么配置:13F、内部人交易,以及“数字由代码填”的 AI 解读

参考持仓页读取十一家知名机构(也可以按 CIK 查任何 13F 机构)的最新两份 13F:去掉期权行(13F 按标的市值申报,不是投入的钱),把同一公司的不同股份类别合并(Alphabet 的 A 类和 C 类算一家),再算前几大占比、持仓的“有效个数”,以及较上季度的新进、清仓、增持和减持。页面还会生成一份规则草稿,数字机械地抄自申报文件,可以改两个上限后带进交易前闸口;闸口也能直接把某家机构最大的十家公司折成一个 1 万美元的样本组合来试。

给这些数据配 AI 解读时,我不让模型写任何数字。代码先把所有指标算好,模型只写带占位符的文字(比如 {top1}、{effective}、{name1}),真实数值由代码填入;校验器拒绝占位符之外的任何数字和百分号、买卖建议和评价词,还会检查占位符用得对不对(“有效持仓数”不能写成“4 周”)、集中度的描述与固定的判断规则一致、“上升/下降”与真实方向一致。真实试跑暴露出的问题几乎都是占位符被用错,而不是数字写错,所以检查是照着这些失败一条条加的。这份解读目前没有评测集,页面上写明了。

公司页上还有两块纯背景信息:内部人交易(Form 4)只列公开市场的买入和卖出,股权奖励、期权行权、扣税等机械性交易不计入;知名机构的持仓则列出各家在这家公司上的权重和较上季度的变化。上线后第一版内部人交易页面把别家公司的交易也显示出来了——公司的申报列表里混有它作为持有人给别家公司股票提交的 Form 4,现在按发行人编号排除。这两块都不进闸口、规则,也不给 AI 看,因为一旦进去就要重跑评测,也会诱导模型去讲择时。

模型是受约束的反方

每份 memo,模型拿到的是一个证据包,其中每条事实都有编号。它的输出在我看到之前要先通过代码检查:

  • 结构校验,并区分陈述事实与解读。
  • 数字核对:事实性陈述中的每个数字都必须与它引用的证据条目一致。
  • 引用原文与检索到的申报文本逐字核对。
  • 中英文规则拦截买卖持有建议、目标价和仓位大小。
  • 检查事实性陈述是否引入了没有来源支撑的产品或分部名称。

输出不合格时,系统会列出具体问题让模型重试;仍不合格就直接拒绝,未通过校验的内容不会显示。每次调用都记录成本,并受每日和每月预算限制。同一套引擎也用于公司页的“上个季度发生了什么”:每家公司每个季度只生成一次并缓存给所有人;解读里的措辞要逐条对照申报文件,另有一句大白话,而这句大白话不能加入原文没有的原因或判断。

大部分规则来自审查真实草稿。有一份草稿把某个季度净利润的大幅增长描述为经营改善,而那个季度净利润远高于营业利润,主要来自非经营收益。现在证据包加入了"净利润 / 营业利润"比率,提示词要求遇到这类差距时列为待验证问题。

部署

Lab 由 FastAPI 服务和 React 前端组成,与本网站运行在同一台小服务器上,经由现有的 Caddy 代理。API 容器不对外开放端口,也访问不到网站的数据库;它使用只读文件系统、去掉全部 Linux capabilities、内存上限 256 MB,并在自己的网络上访问 sec.gov。发布包在本地构建,检查脚本会验证路由、安全响应头、API 文档已屏蔽、SEC 实时数据可用,以及主站不受影响。

发布是拉取式的:推送到 main 后,GitHub Actions 先跑测试、构建、打包并发布到 GitHub Release,服务器上的定时器拉取新版本、校验哈希、切换,并运行检查脚本;检查失败会自动回滚到上一版。服务器端的脚本只接受发布者在白名单内、标签格式正确的发布,并校验哈希和文件路径。测试是发布的门槛,其中一条护栏测试要求默认模型必须是已经通过公开评测的那一个:我试过换成更便宜的新模型,完整评测没达标,就改回了原来的。

发布前的测试发现了几个真实缺陷:一条跳转规则被代理当成了路径匹配器;一个表单输入框拒绝了它自己的默认值;SEC 不可用时返回服务器错误,而不是清楚的提示。

局限

  • 规则仍是草稿,这里的内容都不构成投资建议。
  • SEC company facts 数据可能比申报晚几周,所以最新一季有时缺失。
  • 十家公司逐家测试过,其他任何 SEC 登记的美股也能查,但回答会标明“不在已测试名单”。AI 反方使用财报和分部数字,以及从最新 10-K、10-Q 中检索出的原文段落,每位访客每天最多 3 个新论点,整站每天花费上限 $0.50。它通过了 34 条对抗性用例(索要建议、提示注入、编造数字、离题输入),显示出来的回答中没有任何建议;测试结果公开在页面上。
  • 财报解读和 13F 结构解读还没有正式评测集,只有单元测试和真实试跑。
  • 13F 最多滞后 45 天,只含美股多头,看不出买卖时机和原因;机构与公司的对应按公司名匹配,个别名字对不上的公司会被当成“没有持有”。
  • 内部人交易只读最近的 100 份 Form 4,申报很多的公司较早的交易可能缺失,页面会提示。