Good Design: A Stitch In Time

Back in the summer of 2015, I interned at Belvedere and assisted with our initiative to deprecate dependencies on third party software by building new electronic order entry applications into Belvedere’s in-house proprietary software. These electronic order entry platforms are known internally as Execution Services or “gateways.” That summer I worked on the gateway for the Bats platform, which provided access to several exchanges that trade options and equities. Fast forward to December of 2017 when, as a full-time Belvedere employee, I was tasked with expanding upon the work I performed as an intern. Cboe, one of Chicago's foremost exchanges, recently acquired Bats and plans to transition all of their electronic trading systems over to the Bats platform. While this is a pretty major change, thanks to reusable code and code generators created by Belvederians that came before me, I was able to complete almost all of the coding in two days. Before we can discuss this design, it's important to understand what gateways do and the complications they face. 

On its surface, order entry is simple and straight-forward. A trader places an order and the exchange "acknowledges" that order. If the order fills or trades immediately,  the exchange will respond with an execution report. An order, if greater than a size of 1, can partial fill or full fill depending on the state of the market. For example, an order to trade 10 contracts can be filled by two orders of 5 contracts (interchangeably referred to as lots as well) or can be filled all at once by an opposing 10 lot order. Orders can also be placed with different time-in-force designations which govern the duration of the order. An IOC or Immediate-or-Cancel order will seek to trade as much quantity as possible up to the placed quantity (e.g. a 10 lot IOC order can fill up to 10 contracts) but will not rest any quantity in the market. A day order will also seek to trade as much quantity as possible that currently exists now up to the placed quantity. But, if it does not get immediately filled by the existing quantity in the market, it will rest that order on the exchange and wait for potential other parties to complete their trade until the market closes for the day. 

As a trader or a trading algorithm (referred to as a client), these market interactions are the main focus of trading strategies. They place an order for a particular asset and quantity, specify a price (or ask the exchange to complete the order at the current market price) and how long the order should be valid. However, while Belvedere trades at over 15 exchanges, each with their own protocols, supported functionality, and rules, the client does not care about any of these nuances. For example, Cboe allows firms to change the side of their open orders while most exchanges do not. In light of this variability, the Belvedere order gateway must provide a consistent interface to the client and abstract out all of the complexities and differences of each exchange’s specifications (similar to the Strategy pattern). 

To make this work, a well-defined contract was implemented between clients and gateways (see below for examples of client-gateway communication). For example, clients want to know after each trade how much has traded in total during the lifetime of the order (known as the cumulative quantity). Bats, which uses its own Binary Order Entry (BOE) protocol, sends this data back on each execution notice, but BOX, or Boston Options Exchange, uses the standard FIX protocol and does not send back the cumulative quantity back on each execution notice. To satisfy the contract, the BOX gateway must keep track of the cumulative quantity on its own and pass this data to the client. 

In order to maintain a consistent interface and perform similar actions for varying exchanges, these gateways were carefully designed to limit code duplication and ease addition of any new exchange gateways. Thankfully, both of these requirements were satisfied by maximizing code reuse. Previously, this was accomplished by utilizing inheritance, but lately there has been a transition to composition and reusable components, which limits code coupling and allows for the creation of cleaner systems that share a subset of functionality. Of course, this code also needs to be fast (we measure time to execute on the scale of nanoseconds) and reusing code allows for code to be optimized in a single place and instantaneously applied across the board. 

Additionally, existing gateways must nimbly accept updates to stay up to date when exchanges modify their protocols. Belvedere devised a domain-specific language that is easy to read and write for specifying exchange protocol details. We also built an application that reads in this language and generates code to translate between the exchange protocol (which can be thought of as a string) and C++ structs representing each message between the gateway and exchange. Oh, Bats added a new required field for all orders? All you have to do is write one line of our DSL, run the code generator, then write a single line of C++ to fill out the new field that was added to the generated message struct. Why write code when you can write code that writes your code (yo dawg...)? Unfortunately, our automated tests must still be updated manually after making changes, although this is a small task compared to re-writing everything. 

Order entry gateways perform the seemingly simple task of sending orders to an exchange, but must provide a consistent interface that abstracts out exchange-specific details. By leveraging smart code reuse and automating tedious, error-prone coding, we drastically cut the amount of time required to write new exchange gateways. 

Previous
Previous

International Women's Day