master 发布的文章

Furthermore, when working with financial data, we can bring practitioners' knowledge of markets and financial data to bear on our validation procedures. We know that since markets are competitive, factors decay over time; signals that may have worked well in the past may no longer work well by the current time. For this reason, we should generally test and validate on the most recent data possible, as testing on the recent past could be considered the most demanding test. 此外,在处理财务数据时,我们可以使从业人员对市场和财务数据的了解可用于我们的验证程序。我们知道,由于市场竞争激烈,因此因素会随着时间而衰减。过去可能效果良好的信号可能在当前时间不再有效。因此,我们通常应该对最新数据进行测试和验证,因为对最近历史的测试可能被认为是最苛刻的测试。

It's possible that the design of the model may cause it to perform better or worse in different market regimes; so the most recent time period may not be in a market regime in which the model would perform well. But generally, we still prefer to use most recent data to test if the model would work in the time most similar to the present. In practice, of course, before investing a lot of money in a strategy, we would allow time to elapse without changing the model, and test its performance with this true out-of-sample data: what's known as "paper trading". 模型的设计可能会导致它在不同的市场体制下表现更好或更差。因此,最近的时间段可能不在该模型可以正常运行的市场体制中。但总的来说,我们仍然倾向于使用最新数据来测试该模型在与当前时间最相似的时间内是否可以正常工作。当然,实际上,在实践中,在为策略投入大量资金之前,我们会花些时间而不更改模型,并使用此真实的样本外数据(即所谓的“纸面交易”)测试其性能。

In summary, most common practice is to keep a block of data from the most recent time period as your test set. 总之,最常见的做法是将最近一段时间内的数据作为测试集

Then, the data are split into train, valid and test sets according to the following schematic: 然后,根据下图将数据分为训练集,验证集和测试集: train-valid-test-time-2.png

When working with data that are indexed by asset and day, it's important not to split data for the same day, but for different assets, among sets. This would manifest as a subtle form of lookahead bias. For example, say data from Coca-Cola and Pepsi for the same day ended up in different sets. Since they are very similar companies, one might expect their share price trends to be correlated. If the model were trained on data from one company, and then validated on data from the other company, it might "learn" about a price movement that affects both companies, and therefore have artificially inflated performance on the validation set. 当使用按资产和日期索引的数据时,重要的是不要在同一天中将同一种资产的数据分到一组,而是将不同资产的数据分到一组内。 这将表现为超前偏差的微妙形式(译注:某种程度上像是利用了未来数据)。 例如,说来自可口可乐和百事可乐的同一天的数据以不同的集合结束。 由于它们是非常相似的公司,因此人们可能希望它们的股价趋势相互关联。 如果模型是根据一个公司的数据进行训练的,然后根据另一公司的数据进行验证的,则它可能会“了解”会影响两家公司的价格变动,因此会人为地夸大验证集上的绩效。


Source/来源: AI for Trading, Udacity

Methods for choosing training, testing and validation sets for time-series data work a little differently than the methods described so far. The main reasons we cannot use the previously described methods exactly as described are, 选择时间序列数据的训练,测试和验证集的方法与到目前为止描述的方法稍有不同。我们无法完全按照上述说明使用前述方法的主要原因是:

We want our validation and testing procedure to mimic the way our model would work in production. In production, it's impossible to train on data from the future. Accordingly, training on data that occurred later in time than the validation or test data is problematic. 我们希望我们的验证和测试过程能够模仿我们的模型在生产中的工作方式。在生产中,不可能训练未来的数据。因此,对在时间上晚于验证或测试数据的数据进行训练是有问题的。 Time series data can have the property that data from later times are dependent on data from earlier times. Therefore, leaving out an observation does not remove all the associated information due to correlations with other observations. 时间序列数据可以具有以下特性:来自较晚时间的数据取决于来自较早时间的数据(译注:自相关性?)。因此,由于与其他观察值的相关性,省略观察值不会删除所有关联的信息。 How do we modify cross validation procedures to treat time-series data? A common method is to divide the data in the following manner: 我们如何修改交叉验证程序以处理时间序列数据?一种常见的方法是按以下方式划分数据: time-series-validation-2.png

This way, each training set consists only of observations that occurred prior to the observations that form the validation set. Likewise, both the training and validation sets consist only of observations that occurred prior to the observations that form the test set. Thus, no future observations can be used in constructing the forecast. 这样,每个训练集仅包含在形成验证集的观察之前发生的观察。同样,训练集和验证集都仅包含在形成测试集的观察之前发生的观察。因此,未来的观察不能用于构建预测。


Source/来源: AI for Trading, UdaCity

温故而知新。

框架

HFT-Infra.png 上图是我眼中的最简交易系统,它分为几个部分:

  • 网关(Gateway): 负责和交易所/经纪人(远端)打交道。一方面接收行情数据(报价更新,订单更新等),另一方面把交易指令传给远端.
  • 定价引擎(Pricing Engine): 接入的行情数据需要转换成策略需要的报价。对于衍生品来说,它的输出应当至少包含衍生品的理论价格。定价引擎可能还有额外的输入,比如对于期权定价而言,,一般会使用类Black-Scholes模型定价,有了标的物(underlying)的市场数据之后,它还需要波动率(volatility)、利率等参数的输入以及一些合规、风控相关的参数。
  • 策略(Strategy): 交易策略接收定价引擎的信息,同时他也接受策略目标instrument的实时行情。不同的策略给出不同的执行建议,然后把这些执行建议传给后续模块。
  • 风控&执行复用(Compliance & Execution Mux): 一个交易系统(很)可能同时执行多个策略。不同策略发出的执行建议可能互相重合、冲突或者产生执行时的合规风险。这个模块负责处理所有的执行请求,把合格的请求发给Gateway执行。这边用Mux相对形象一些,暂时还没想到更好的词。

需要说明的是,上面这些仅仅是极其基础的组件。如果把交易系统作为一个模块看待,它其实会有许多附加组件,例如高频交易员往往会根据经验值调整一些参数,这些参数会反映在策略执行、风控、定价模块中,于此就会产生一些和人交互的UI组件。

优化: 软件、硬件、程序语言...

不同于一众互联网公司的系统,高频交易系统对于低延迟的渴求相当急切,对它来说高并发往往是可以忽略的。重要的是一个报价更新如何最快到达系统,一个订单(order)如何在最快的时间内到达交易所。目前能见到的国外交易公司,主要用C++编写交易系统,当然也有很厉害的公司用自己定制的JVM跑Java交易。对于延迟不是非常敏感的市场,也有使用Python作为开发语言的。当然这些仅限于软件层面,事实上很多交易公司已经用上了FPGA用来进一步加速执行速度,甚至使用FPGA来执行一些简单但是对延迟极其敏感的交易策略。 以C++为例,代码编写上将会大量使用模板(template)替代虚函数。一般应用场景上虚函数调用的开销可能是不值一提的,但是如果算到了纳秒(或CPU tick)级别,虚函数的开销也会令人头疼。也许有人会问,那接口继承怎么不用虚函数弄呢?有兴趣的可以去看一下CRTP设计模式。

Gateway

网关系统的职责是负责对外联系,一般分为行情数据(Market Data)和订单执行(Order Execution)两个部分。交易所一般会提供机房租赁,衍生品交易公司会采用co-location策略把交易主机放在租的机房里,确保和交易所的Matching Engine距离最近。所谓Matching Engine就是交易所自己用来匹配订单(order)的中央机器(其实有很多个机器)。 就行情数据而言,绝大多数交易所是用UDP组播的方式下发数据的。收到组播数据后,需要把订单报价更新的信息尽快处理并上发。就程序逻辑优化而言,第一是要尽最大可能减少内存拷贝(memory copy)。第二是要钻研(并且实验!)交易所给的数据格式,避免去读取不需要的字段。其次某些交易所的市场数据可能还是通过ASCII字符下放的,这边就要尽量优化string to int的延迟。

Market Data

对于某些交易Gateway实现,也会包含price book/order book的更新。行情数据可以细分为不同的频道(channel),一些会播放price book update, 另一些则是order book update. 后者往往包含更多的交易信息,这些交易信息与trade update合并关联之后,就能给pricing module提供实时更新。对于price book/ order book,写代码的小哥往往会对他们各自抽象各自的数据结构,这些数据结构需要保证book update的响应尽量快。在book得到更新后,上层系统(strategy, pricing module)对它的需求是不同的,有些需要full depth of book,有些可能只需要几个book level或是只要TOB(top of book),针对这些不同的需求,系统也会给出不同的优化。也有系统把book update的具体操作另外归类为几个特殊事件直接传给系统上层、以便做出最快响应。这些事件可能包括巨大的价格变动(此时很可能需要撤单)。 另一部分个重要部分是重启、恢复机制。由于UDP传输的不稳定性(尽管已经是co-location了),交易所一般提供通信恢复的专门channel播放恢复数据。在网络中断或者系统故障后,需要通过数据恢复机制重建各种交易工具(instrument)的最新信息。

Order Execution

再来讲讲发单的模块。系统给发单模块的指令已经是经过各种决策后的,一般不需要再进行过滤。一般而言,这部分是通过TCP传输的。为了尽快发单,一般采用数据结构预填充的方法,即把交易信息用数据结构表示,里面固定的字段预先填好。比如C++实现时,Order Execution模块会把各种发单请求写成类成员,在OE class构造时预先构造好。对于OE而言,一般情况下并发并不是主要考虑的因素,因为大部分交易所是允许单个消息包含多个订单请求的(bulk orders or bulk delete orders), 往往把交易请求拆单并发发出的总体耗时比用单个请求发出的更不划算。但这部分仍然需要通过做实验观察得出结论,对于部分的交易所,将一个大单拆成几小份可能有一半左右能抢到,但是如果做成一个大单可能真的是Fill(10%) or kill(90%)了。对于一些需要并发才能优化的交易所,也许会需要使用无锁队列(如MPMC Queue)进行优化。 socket连接?有人可能会问socket怎么搞。其实这种时候用整个系统用busy loop, blocking的方式或许比费劲心事async来得更有效。当然对整个trading engine而言,它应当是多线程、适当异步的,或许Proactor模式更适合一些。所谓多线程,在通信不是很繁忙的desk上,整个gateway系统可以是一个线程,在比较繁忙的desk上,把market data和order execution拆成两个线程做是一种解法。

关于优化,再说一句

不只是应用层的软件,在内核层也需要对性能进行优化。一个简单方法是购买专用的网络硬件,然后利用驱动(等)把网络传输的操作从内核态操作转移到用户态进行。学过操作系统的同学应该知道,在内核态和用户态中间反复切换是有成本的。 此外,一般用于高频交易的主机往往要求CPU主频够快,对核心数要求并不特别高。为了主频够快,一些公司甚至会购买定制优化的超频主机来使用,通过将线程直接分别绑定在CPU core上,然后isolate cpu,再考虑NUMA node,继而用realtime scheduler实现各种黑优化。

交易策略 Trading Strategy

一个交易系统启动之后,往往要运行多个交易策略。单个的交易策略主要由行情变动驱动,打个比方——如果市场的价格发生大幅度变化,那么可能某个期权的报价(Quoting)策略就要立马做很多撤单操作了。一些策略同样也会对成交(Trade)做出相应。 除了直接处理行情信息外,策略也会处理由行情间接引发的其他因子(factor)的更新变化。这些因子的更新频率可能不如实时行情那么频繁,但是也会通过其他的消息途径输入。另外,可能有一些人工设定的策略参数发生了临时调整,这些信息也会输入进来。 如果要对行情变化做出响应,在一个多线程的交易系统中,可能会用到一些消息队列。行情更新的信息会被主线程推到消息队列里,每个策略跑在单独的线程(这个线程可能会独占某个CPU核心)中,不停地做循环(Busy loop)去拿队列里的可用更新数据,做出相应的响应,然后再继续下一次轮询... 将策略的实现与多线程细节隔离。交易策略的执行可能会用到多线程,但是较好的实践方式是将多线程的业务逻辑和策略的主逻辑进行隔离,一个策略需要尽可能在在自身逻辑上保持单线程。如果策略逻辑本身需要费心多线程的话(例如和其他策略频繁地交流),它的性能将变得比较差。 策略的输出——基本上是一堆指令(command),其中绝大多数是交易指令。这些交易指令基本上会被堆到一些队列容器里,然后——等等——得交给交易限制检查(limit checker)模块先做一番检查。不同策略发出的交易指令可能会有冲突或者重叠,所以如果能安排一个Order Execution Manager的模块来快速处理这些交易指令,可能是明智之举。经过筛查的交易指令最终将会被发回到网关(Gateway)模块,然后发往交易所。一般来说,一个策略不是很关心它生成的单条指令最终的执行结果,这是因为系统发出去的订单结果,包括市场对它的反应,肯定会第一时间反馈到行情更新上。当然Order Execution Manager可能会对发单执行情况做一些统计并作为信息输入的一部分回馈给某个策略。

性能评价

  • Hit rate,中文可能叫命中率。当你觉得市场上的行情报价有钱可赚的时候——打个比方,有人在以很便宜的价格卖某种期权——你就需要发出一个订单去吃掉对方的单。在你接收到这种利好行情的时候,别家也会接受到同样的信息,所以很有可能你有非常多的对手都想要吃掉这个人的便宜货。这时候hit rate就是衡量 实际成交的订单数(或者具体到多少手)/发出的订单数. 一般而言hit rate越高系统的性能越好,但是这个值跟你算的理论价格也有关系——如果你一直算偏了fair price,那么没人跟你抢单,hit rate也会出奇高;
  • Tick to Trade/Quote,即从接收到行情数据开始,直至发生交易时的时间间隔。一个性能强劲的系统,它的TTT/TTQ是非常低的。目前使用FPGA收发的策略,TTT在纳秒级

书目推荐

  • 《Effective C++》如果对C++了解不深,最好先读这本;
  • 《Effective Modern C++》跟上面一本是相同的作者,讲C++11新特性的种种;
  • 《C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond》C++模板元编程
  • 《C++ Templates: The Complete Guide》这本有侯捷的译本叫《C++ Template 全览》,英文读起来有点吃力
  • 《Flash Boys: A Wall Street Revolt》当小说看就行,讲高频交易的一些故事的
  • 《Options, Futures and Other Derivatives》中文名《期权、期货及其他衍生产品》偏教材的入门书籍,看完这个再看下一本
  • 《Option Volatility & Pricing: Advanced Trading Strategies and Techniques》作者: Sheldon Natenberg 经典绿皮书