设计最高效的软件

设计最高效的软件

程序员在编写软件来完成某些特定的任务时,通常会在写代码之前通过一些标准去选择软件背后的算法。在高频交易领域,最聪明的程序员都会把效率当做选择算法的最根本标准,并且会不断地花时间去优化算法的效率。算法越高效,处理任务所需要的时间就越短。毕竟我们都知道,在高频交易中,时间就是不折不扣的金钱。

算法,指的是解决某种问题的方法。它并不是一组指令的集合,指令是一种具体有形的东西。而算法则比它要更加抽象,是指令背后的思想。对于任何一个给定的问题, 一般来说都会有很多种可能的算法能够解决它。我们举个例子,假设我们想在电话簿里找到某个人的电话号码,我们都知道电话簿里的名字是按姓氏的字母顺序排列的。想得到一个人的号码,我们必须首先找到这个人的名字,而我们有很多不同的办法来做到这一点。

比如我们想要找到Sara Gruen 的电话号码。一种办法是从电话簿的第一页开始,一页页地翻, 直到我们看到" Gruen Sara" 这个名字,虽然这种方法可以保证让我们一定找到这个名字, 但它实在是太慢了,很容易看出它是一种时间利用率很低的方法。另一种我们很容易想到的办法是根据姓氏的第一个字母,猜测以这个字母开头的名单在电话簿里的大致位置,然后根据翻开页码上名字的开头字母确定该往前还是往后翻。我们大概会从电话簿靠中间的地方翻起,假设我们翻开电话簿后看到的名字是"Mortenson Greg" ,我们知道, "Gruen " 这个名字按字母排序时在"Mortenson" 的前面,所以我们向前翻一些,假设我们又看到一个名字"Eggers Dave" ,因为" Gruen " 按字母排序在" Eggers" 之后,所以我们再向后翻, 看到另一个名字" Lamott Anne , 再往前翻。不断重复这样的做法,直到我们找到"Gruen Sara" 这个名字,看看她的电话号码是多少, 再打电话给她。计算机科学家把这种方法命名为二分搜索,这种查找的方法很明显比我们前面说的那种顺序搜索效率要高得多。

事实上,高频交易系统的开发者经常需要在一系列的排序和查找算法中进行选择,这些算法的种类很多,算法之间的差异主要在于时间效率、空间效率(占用的内存)以及准确度上。但除了这些排序和查找算法,他们还经常需要设计一些专门针对高频交易应用的高效算法。举个例子,假设一个程序员需要写程序来根据某个代码的标的物。( 比如SPY ) 的价格变动来修正期权报价。但是市场上代码为SPY 的期权合约有接近2000 种, 其到期日、执行价格和看涨看跌类型都不一样,并且每一种都有独立的委托单簿,作为一个期权做市商,很可能需要维护2000 个申买价和2000 个申卖价。只要标的ETF 的价格发生变化,即使只有一两美分,做市商就必须以最快的速度更新4000 个报价。这必须通过向交易所发送一长串的报价更新请求来完成,每一个更新将精确地替换掉原来的报价。

程序员在处理这个问题时,不仅仅是选出能将这些更新请求以最高的时间效率发送出去的算法就够了, 他还需要考虑发送这几千个更新请求的顺序: 是从到期时间最短、执行价格最低的合约开始,按这两个维度顺序递增的方式发送请求直到所有合约的报价都得到更新呢?还是从交易最活跃的合约开始呢? 又或者从处于最深的实值状态的合约(也就是执行价格最低的看涨期权和执行价格最高的看跌期权,它们的delta 最大,这意味着期权价格对标的物价格变动最敏感, 所以在标的物价格变动时,这些期权的价格风险最大)开始呢?还是应该选择一些另外的算法呢?在这个问题上,完美的答案是不存在的, 但那些富有经验和创造性的程序员会通过一些分析和实验,找到一种看起来很不错的报价更新流程。

一个软件程序的效率不仅仅取决于程序员所选择的算法以及他所编写的程序的质量,编写软件的程序语言也会影响到程序效率。程序语言是一系列与英语比较类似的代码(编写软件也称为写代码),程序员需要按照严格的语义和语法规则使用各种指令和关键词向计算机输入代码,代码通过编译和链接最终被转换成二进制形式( 0 和1 )的代码,它是计算机本身能够理解的最终指令形式。在高频交易软件领域,主流的编程语言有三种。C++ 语言是C 语言的面向对象扩展版本,它继承了C 语言"接近硬件"的特性,因此具有很高的效率。使用C++,程序员可以编写出非常定制化的代码,这样的代码能够最好地满足软件对效率的终极需求。除了C++, C# 和Java 是另外两种流行的编程序言,它们也都具有面向对象的特性,虽然灵活性不如C++ ,但是使用它们编程更加容易。

在其他因素相同的情况下,程序员用C# 或者Java 编写程序一般来说会比用C++ 更快, 并且程序也更不容易出错。但是使用C# 或者Java 编写高频交易软件有两个值得注意的缺陷。第一,正如我们提到过的,相对这两种语言, C++让程序员能够编写出更加定制化的指令,包括指定特定的内存区域(使用指针,甚至指针的指针), 甚至是操纵内存中的某些特定字节位置,而C++ 和Java 没有提供这种让代码更"接近机器"的能力。这并不一定是件坏事,这个特性也使C++ 程序出现漏洞的可能性要远远大于C# 和Java 程序,但它毕竟限制了程序员编写程序的定制性。第二个缺陷主要是关于内存管理的,说得更具体一些,是关于在程序运行完毕之后的内存释放,通常这件麻烦的事情称为垃圾回收。

使用C#和Java 编程的一个吸引人的地方在于它们能够替程序员完成"垃圾回收"的任务,当程序发现一些内存不会再使用时,它就会自动将其清空,为其他任务腾出空间。而使用C++ 编程时,程序员需要向己编写一些专用的代码来控制内存的使用和释放,如果不这么做,程序就会给一部分被使用的内存区域进行锁定,被锁定之后,这些内存即使在程序已经运行完成之后也不能被其他程序使用(这样的漏洞称为内存泄漏)。但自动的垃圾清理机制的问题在于,你基本上无法控制这些垃圾清理过程,而它们有可能在一些非常不合时宜的时候发生。举个例子,可能你的电子眼刚刚监控到了一个可能带来丰厚盈利的交易机会,而系统也正准备发出一张IOC委托单将它拿下, 但它却突然发现自己因为正在清理垃圾而处于等待模式。这种不可控因素有可能决定你是否能够拿到一个交易机会。因为这样的一些原因,在构建最为高效和可控的高频交易系统组件时, C++ 可以说是最好的编程语言。不过C# 和Java 仍然是很流行的,而且也的确更适合用来构建高频交易系统中那些对时间效率要求不那么苛刻的组件(例如GUI apps 和报告生成器等)。

事实上,你对于操作系统的选择也会对软件的效率和可控性有影响。大多数高频交易系统都运行在标准的Linux 或者Windows 操作系统上。现在的操作系统要处理的任务,已经远远不只是运行几个特定的程序, 实际上它有一大堆麻烦而琐碎的任务需要处理,而大多数程序员甚至不知道这些任务的存在,这最终会导致即使写出最为高效的C++ 程序,也不能保证计算机能够像你期待的那样完成任务。操作系统的处理能力需要在各种目的和需求之间进行分配,并且这是不受你控制的。的确有一些操作系统能够让你事先知道计算机将在什么时候处理你的任务,它们被称为"实时操作系统" (其中的大多数都是Linux 的变种) ,在对效率和可控性有极高需求时, 某些最苛刻、最激进的高频交易公司的确会考虑使用它们。

高频交易系统背后的代码量是非常庞大的,一个高频交易系统可能包含数十万甚至数百万条代码(在使用C++ 编程的系统中,代码量更大,而使用C# 或者Java 编程的系统中的代码量会少一些)。

每一行代码都可能对系统的质量优劣产生影响。同样,任何一行代码都是某种算法中的一步,而高频交易系统背后可能有数万个算法,其中每一个算法的选择都会对系统效率高低产生影响。毫无疑问,高频交易系统的开发者需要工作很长时间才能完成它(当然,他们也领着很高的薪水)。