第 7 篇:第三次大事故——假暴跌、另一个 AI 的报告,和一个"对账员"
早上 10 点,三条告警和一个"外援"
10 月 30 日上午,我打开手机,先看到的是前一天 16:30 的推送:
- 永久组合净值 1.4714 → 1.3805,跌 6.18%
- 全天候组合 1.4396 → 1.3528,跌 6.03%
- 永久组合回撤 16.47%,超过 15% 阈值
三个告警,同一个方向。但我盯着它们的第一反应不是"市场怎么了",而是——这两个组合里头大部分是债券和黄金,一天怎么可能跌 6%?
于是我做了一件事:把三条告警和三份备份文件丢给另一个 AI(扣子),让它独立分析;同时把这个分析贴给 WorkBuddy:
> 我(10-30 10:01):你昨天 4:30 报了三个告警,我让扣子分析了一下,你看看:(附完整报告)
这就是"双 AI 交叉验证":一个给结论,另一个带着数据库去现场验证。
另一个 AI 查到了什么
扣子的报告写得很清楚,我摘几条关键的:
> 三条告警全是误报,平台没跌,是净值算错了一次。
| 告警说的 | 实际 | 差 |
|---|---|---|
| 永久组合 1.4714 → 1.3805(-6.18%) | 1.4714 → 1.4686(-0.20%) | 虚报 6% |
| 全天候 1.4396 → 1.3528(-6.03%) | 1.4396 → 1.4341(-0.38%) | 虚报 5.7% |
| 永久回撤 16.47% | 11.1%(低于 15% 阈值) | 不该报 |
其余 16 个策略当天全部正常,最大波动是半导体网格的 -2.14%。
它把根因链也拆出来了,我按时间顺序复述:
10 月 28 日 16:30,采集端误判。 513500(标普 500ETF)当天发生 1:2 份额折算,前复权价从 5.404 变成 2.702。平台的"行情异常防护"把它当成数据错误,乘 2 还原成 5.404 存库。因为份额还是旧口径(32,744 份),市值算出来 176,949 元——是对的,当天净值 1.4715 也没错。
10 月 29 日 09:32–10:07,修了一半。 有人发现还原判断错了,把这只 ETF 的全部历史价格 ÷2 归一到 2.702 口径。备份文件名 app.db.pre_scale_20261029_0932、app.db.post_scale_20261029 就是这次操作留下的痕迹。
但 positions.holdings.units 里的份额没有同步 ×2,还是 32,744 份。
10 月 29 日 16:30,净值腰斩。 净值 = 份额 × 收盘价。32,744(旧份额)× 2.688(新价格)= 88,016 元,正确应该是 176,032 元——凭空少了一半。总资产从 1,468,603 掉到 1,380,587,净值 1.3805。再拿这个数和 1 月的高点 1.6527 比,回撤 16.47%,第三条告警也来了。
16:30–17:59,份额被改成 65,488,净值重算回 1.4686 / 1.4341。
三份备份文件的对比,我把它们摆在一起看,链条清清楚楚:
| 时点 | 513500 份额 | 总资产 |
|---|---|---|
| 09:32 pre_scale | 32,744.12 | 1,471,449.9 |
| 10:07 post_scale | 32,744.12(没改) | 1,471,449.9 |
| 17:59 备份 | 65,488.23(×2 了) | 1,468,561.4 |
这个洞,是四天前我自己问出来的
说来讽刺:这次事故的起点,是我 10 月 26 日的一个追问。
> 我(10-26 09:58):我问你个问题啊,我感觉你的价格 adj_close 和 close 完全相等,是纯价格口径,是不是没有用复权价
这条消息在记录里出现了 6 次。核心就一句:你存的价格,是"没复权"的原始价,还是"复权"过的价?
分红、拆分会改变价格,但你的资产没变。用不复权的价格算净值,等于每次分红都当成一次下跌——十年下来,收益会被系统性低估。这个不改,所有回测都不作数。
它查了,确认是纯价格口径。我问工作量,然后拍了板:
> 我(10-27 10:26):听你的,先干 P0-1 + P0-3
于是有了那次复权改造:历史价格全量刷新,adj_close 换成前复权口径。
改造本身是对的,但它给数据表带来了"两种口径可以共存"的可能。 四天后,513500 的份额折算撞上这个可能,就成了假暴跌。
这也是自动化项目里最难防的一类问题:你今天做的一个正确决定,会在四天后以另一种形式咬你一口。 不是因为它错,而是因为它只改了一半的世界。
但我没有照单全收
这份报告写得太完整了,完整到让人想直接转发。可我还是让 WorkBuddy 去服务器上逐条核对——因为报告是推理出来的,账是真在库里的。
核对结果是:根因链是对的,但两处细节错了:
1. 扣子说"全盘找不到对应脚本、bash_history 里也没有,说明是纯手工执行"——不对。那次归一其实有脚本,只是脚本本身少做了一步(没同步份额)。这比"手工操作"更值得注意:有脚本也会漏,因为脚本可以只写一半。 2. 扣子说 17:59"自动修正"——也不对。那一次是手工改回来的,系统并没有自愈。不存在"自愈"这件事,别给自己留这种幻觉。
这两处纠正看似细节,其实改变了整改方向:如果是"人手工改库",结论是"禁止手工";如果是"脚本少一步",结论是"让脚本必须原子完成"。方向不一样,补丁就不一样。
三个漏洞,三条规则
报告里提了三个隐患,我基本认同,落地成了三条规则:
规则一:价格表只允许存一种口径。 source 字段的记录显示,这只 ETF 在 10-20 到 10-28 是前复权(tencent_adj),10-29 变成了不复权(tencent)。前复权价会随分红拆分自动重算历史,不复权不会。两个口径混在一张表里,下次除权日一定再出问题。
规则二:任何价格口径的整体缩放,必须原子完成。 所谓原子,就是"要么全做,要么全不做"。一次缩放必须在同一个事务里做完三件事:
1. 缩放历史价格 2. 对应标的的持仓份额按反向因子同步调整(价格 ÷k → 份额 ×k) 3. 触发所有策略的信号重算
中间态被 16:30 的净值计算读到,就是这次这种假暴跌。
规则三:净值写库前,必须做一致性校验。 原来的顺序是"先写库 → 再比阈值 → 报警",等于把错误数据当成了事实。改成:单策略单日跌幅超过 3% 时,先看是哪个标的贡献的、该标的当天真实跌幅是多少。如果策略跌了 5%,但没有任何一个持仓标的跌超 5%,那就不是市场跌了,是你算错了——拒绝写库,报数据级告警。
我们后来把这个角色叫"对账员":它不负责赚钱,只负责在账不平的时候拒绝签字。
顺手抓到的第四个漏洞
整改过程中,WorkBuddy 主动汇报了一件事:告警入口少接了一根线——调用时缺一个参数,一旦触发会直接报错中断。
而这个入口,恰好只在"采集出异常"的那天才被调用。也就是说:平时从来不执行,一出事就崩。最需要它喊人的时候,它先哑了。
这个 bug 让我后背发凉。它意味着:过去半个月里,所有"采集异常"的日子,我可能一条告警都没收到,而系统自己也不知道。
这一篇学到的
1. 两个 AI 互相校对,是普通人能用的最高级手段。 A 给结论,我拿去让 B 带着数据现场验证,听 B 反驳,再拍板。AI 给结论,我去现场。 任何一份"看起来很完整"的分析,都值得被另一双眼睛查一次。
2. 报告写得越顺,越要核对。 扣子那份报告逻辑通顺、时间线完整、还列了整改建议——它错的两处,恰好是最容易被"顺"过去的地方(谁来修的、怎么修的)。
3. 手工修和脚本修,是两种病。 手工修的问题是会漏步骤;脚本修的问题是会漏写步骤。前者靠流程,后者靠"原子化"设计。
4. 最危险的 bug 在异常路径上。 正常路径天天跑,问题早暴露了;异常路径一年跑三次,没人测。而它偏偏只在出事时才被调用。
【你也可以】
1. 让 AI 给的任何重要结论,换个说法贴给另一个 AI:"帮我检查这份分析哪里可能错,特别是因果链。" 2. 给系统加一句"算不平就别写"——宁可今天没有数据,也不要把错的当成事实。 3. 检查你的自动化里,异常处理那段代码有没有被测过。有没有可能:它从来没成功跑过?
本系列为个人研究与搭建记录,所有数据与策略均不构成投资建议。
数据仅供参考,不构成任何投资建议。市场有风险,投资需谨慎。
其他笔记
- 开篇:我不懂代码,19 天搭了一套每天替我盯盘的系统 2026-11-03
- 德隆「三知道」投资理论完整版 2026-11-02
- 申万行业 PB 数据从哪来:从手工导出到官方 API 全自动 2026-11-02
- 动量方舟是什么:一个人如何搭一套动量轮动策略监控平台 2026-11-02
- 第 12 篇:尾声——连"怎么写这个系列",都被纠正了三轮 2026-10-30