我亲手关掉自己安全开关的那一天

在我第一个实盘交易机器人的历史记录里,藏着一次我并不引以为豪的提交。它只有两个词。在系统正在运行的过程中,为了让一个问题消失,我伸手进入那个本应保护账户的安全机制,写下了:if False

它确实”起了作用”,就像关掉一个烟雾报警器”起作用”那样。报警声停了。报警器原本要警戒的那个东西,并没有停。

那个不肯停止触发的守卫

早期,我有一件事做对了:我给实盘机器人设了一个每日亏损上限。如果账户在单日内跌得太多,系统就应该停止交易并等我处理。这是最基本的一种断路器,纸面上它正是别人告诉初学者要去建立的那种纪律。

问题在于,它在实际上什么都没出错的时候也不停地触发。每次机器人把现金投入一个仓位,守卫看到现金余额下降,就把它读成了一笔亏损。于是它尽职尽责地、大声而持续地工作着,叫停了一个并未经历它所声称的亏损情况的系统。一个每隔几分钟就毫无缘由地响一次的警报,不是一项安全功能;它是噪音。而噪音,最终是会被消音的。

两个词的热修复

于是我把它消音了。在会话进行到一半、机器人还在运行的时候,我禁用了那个每日亏损检查——if False——然后把它发布了出去。叫停停止了。机器人又能自由交易了。有那么几分钟,感觉像是一个修复。

它不是一个修复。它是我在整个项目里做过的最危险的决定之一,而它危险在哪里,值得说得精确一些。

我实际做了什么

守卫之所以触发,是因为它被喂了错误的信息——它把”花现金买仓位”和”亏钱”搞混了。那是一个真实的 bug,而它有一个真实的修复办法:教会守卫区分”买入”和”亏损”的不同。

我没有那样做。我把守卫关掉了。我把一个测量问题当成了一个守卫问题来对待,然后通过移除守卫来”解决”它。这在工程上等同于:因为烟雾探测器一直在嘀嘀叫,就把它的电池抠出来。嘀嘀声停了,保护也停了。

一个被禁用的守卫是一笔会复利的债务

接下来这部分,当时本应比实际上更让我害怕。在热修复之前,我的问题仅仅是恼人:一个叫停我太频繁的守卫。在热修复之后,我的问题变成了无形且无界的——一个实盘机器人,交易着真金白银,却完全没有一个能用的亏损上限。

那些误报有一个我看得见的代价:被打断的交易。而被禁用的守卫有一个我看不见的代价——直到某一天,一笔真正的亏损也许会到来,并发现没有任何东西挡在它面前。我用一个看得见、能挺过去的烦扰,换来了一个看不见、无上限的风险,而我这么做,只是为了让一盏红灯更快变绿。

这就是禁用一个保护措施的诱惑:代价被推迟了。眼下不会立刻发生任何坏事。账单如果来,会在晚些时候一次性地全部到来——在最糟糕的那一刻,而那一刻恰恰正是守卫存在的意义所在。

正确的做法:修好守卫,而不是把它消音

正确的回应是那个无聊的回应。这个守卫的初衷是合理的,但它的测量与记账输入坏了。所以你去修输入:把喂给它的各种信号分开,让它只对真正的亏损做出反应,而不是对现金流入仓位这一寻常动作做出反应。一旦守卫能把真正的亏损和正常的买入区分开,它就不再狼来了——而当那些真正要紧的亏损到来时,它还在那儿,完好无损。

我从中得到的那条规则简单得几乎让人难堪,而我现在把它当作近乎绝对的准则:你不会在一个安全机制被关掉的情况下继续交易。如果一个守卫错误地触发,那就在你更改或禁用它之前,先诊断它的输入和它自身的逻辑——并且在你恢复运行之前先修好或替换它,而不是在毫无保护的状态下运行。

守卫的全部意义,就在于你不在场的那一刻

这件事对一个自动化系统的意义,要远大于对几乎任何你手动操作的东西,这是有原因的。一个实盘机器人会在你睡觉时运行,在你上班时运行,在你几乎忘了它存在时运行。它的安全机制其实并不是为你正盯着的那些时刻准备的——那时你几乎用不上它们。它们是为你不在的那些时刻准备的:凌晨四点的剧烈波动,只在节假日才触发的 bug,在你离开桌前的那九十秒里展开的连锁崩塌。

当你为了越过今天的一个烦扰而禁用一个守卫,你正是在用那些时刻做抵押借贷。你在赌那个无人看守的时段会是平静的一个。市场不会按公平的条件给你这个赌注。

我把那次两个词的提交记在心里,当作一种警示标签。if False 是世界上最容易的修复,而它常常根本不是一个修复——它是一种把一个你看得见的问题,转换成一场你看不见的灾难的方式。

——没有信号,没有收益,并非投资建议。