本文来自微信公众号:程序人生(ID:coder_life) ,作者:马超,责编:伍杏玲,头图来自IC photo
4月21日凌晨,WTI 5月原油期货结算价跌至负数的-37.63美元/桶,令人意外的是,这只“黑天鹅,竟波及众多来自国内的中行原油宝产品客户,不但造成原油宝投资者血本无归,还倒欠银行资金的境地。从目前中行的回应来看,要求原油宝客户补回缺口资金,下面笔者就来带大家从IT的角度来解读一下此事件。
无杠杆的原油宝,怎么会倒贴钱?
从原油宝的说明上来看,该产品属于无杠杆产品,那么用户的钱是怎么亏的呢?
原油宝的穿透损失出现得很神奇,我们看到WTI的最小价格变动为1美分,也就是当价格为1美分时,假如客户认为今后价格将反弹,他就可以用100美元本金,买入1万手的多单,多单的含义是客户以现价买入原油,然后以交割价格卖出。假如油价从1美分反弹到1美元,交割价格就是1美元,那么就会直接给客户带来1万美元的收益;而假如油价跌到0,也就是将100美元的本金赔进去,这看起来还真是挺完美的。
但是万万没想到的是油价不仅没有反弹,还跌成了负值,这时客户按照交割价-37.63美元卖出1万桶原油,就需要倒贴买家37.63万美元才行,造成原油宝这种无杠杆产品,产生超出本金损失的罪魁祸首,是函数输入范围超出预期。
千年虫、负油价祸出同门?
提起“千年虫”问题可能很多90后的程序员没什么印象,不过对于笔者来说,记忆犹新。
千年虫问题起源于60年代,由于当时计算机存储器的成本很高,计算字长也不够,如果用四位数字表示年份,就要多占用内存空间,因此当时最流行的企业级开发语言COBOL以及现在很多数据库的原型SABRE,都为节省存储空间,默认使用两位数字表示年份。虽然后来计算机字长变长,内存价格降低,但在使用两位数字来表示年份的做法,却由于惯性力量而被沿袭下来,直到21世纪即将来临的1997年,业界才开始拉起了“千年虫”警钟,并很快引起了全球关注。
千年虫的影响堪称是世纪Bug的级别,人们一度怀疑千年虫是否会导致核武的误发射问题。冈比亚成为全球受千年虫影响最为严重的国家,除不少地方电力供应中断外,其海空交通、金融和政府服务也经常中断。著名的7-11便利连锁店的计算机系统也曾遭到类似千年虫的计算机漏洞的袭击,把2001年当成1901年,导致许多使用信用卡的用户无法正常刷卡。
从本质上讲,千年虫与负油价都是输入参数越界的问题,“千年虫”使用两位空间计数的话,99加1就变成0了,而“负油价”则意味着卖东西需要倒贴钱,针对这种负油价的情况,交易软件的行为的确无法预测,所以这一切基本可以回归到一个IT的基本问题,当一个8位的整型到了其上限127的情况下再加1,输出到底应该是什么?
平衡:系统设计的艺术
如果奇点的情况不可预知,那么系统就只能在几种目标下寻求平衡了,这样的平衡术成为了系统的制约条件,比如分布式系统的CAP原则,货币系统受到蒙代尔三角的制约。
在本文“千年虫”、“负油价”的讨论范围内,这种平衡术就要求系统设计者在健壮性、容错性与连续性之间做出平衡。如果一味强调健壮性,那么一旦系统发生错误,就很可能让错误扩大,酿成更大危机;如果单纯追求容错性,那么经常带病上岗的系统,健壮性又不会太强。
比如对于负油价的例子,我们看到绝大多数与WTI连接的系统,在合法性检查时,都会拒绝发送价格为负值的请求,正是这个原因造成原油宝无法正常平仓,不过这个锅不能完全由程序员来背,因为不能确定发送负油价请求到交易所,会产生何种后果。因此这时到底选择容错性还是健壮性其实也未为可知。
再举一个电竞的例子,在王者荣耀的比赛中到底是选择以孙膑为核心的容错体系,还是选择以后羿为核心的硬刚体系,就是在做这样的平衡。以健壮性为第一目标的体系,特别容易因为C位的倒地而猝死;容错性为核心的体系则容易被对手在前期就积累优势,从而不断被滚雪球,最终慢性死亡。所谓一代版本一代强,最终的版本之子,往往游戏的设计者都不能预测,所以也可以看到现在基本主流的即时战略和MOBA类游戏厂商都会有体验服,以提前获取游戏平衡性调整的资料,仅靠纸上谈兵几乎无法预测实际情况。
所以复杂系统只能做平衡,十全十美的策略并不存在。
取舍:系统的应对之道
虽然从某种程度上讲千年虫与负油价都是边界的问题,但是千年虫对于其跨越1999年的行为是可以提前预期的,而负油价却不行。一个属于已知行为的异常,一个属于不可预期的异常,因此要应对危机,首先要了解异常与错误的类型。
已知异常:最具代表的例子就是千年虫了,一般是由于系统设计时忽略的问题或者其它已知问题引起的,也被称作检查性异常。比如我们现在乘坐飞机火车都需要查验身份证件,对付这类异常都需要有对应的预案或者处理流程。对于系统整体来讲,服务不应该因此类异常中断。
未知错误:这种情况一般是由系统设计时无法预期的边界而产生的,系统设计人员无法将系统运行时所遇情况考虑完整,未知错误出现后到底是让程序继续运行,还是崩溃蓝屏退出,其实是健壮性与容错性之间的抉择。
重要错误:如堆栈溢出,磁盘IO错误等等,遇到此类错误到底是立即蓝屏或者崩溃以防事态扩大。但是未知错误与重要错误往往边界不清,处理逻辑也很麻烦。
复杂系统的异常处理原则就是做好检查性异常的处理流程,不过这其中的处理方案其实非常难以取舍。只能提示尽量使用Java中Finally的机制,以便保存好错误现场情况后续复盘,出现不可预期的问题时尽快提示人工介入兜底。
本文来自微信公众号:程序人生(ID:coder_life) ,作者:马超,责编:伍杏玲
网址: 负油价甩锅程序员? http://m.xishuta.com/newsview21996.html