Considerations in Trading Systems Architecture

Introduction

Spending time defining the architecture of a trading system before starting development will unquestionably save a lot of time in the long run. It’s implementation will impact the system's performance, scalability, reliability, and ultimately, the profitability of the strategies.

Compromises always have to be made when selecting quality attributes of a system, so identifying and prioritizing the quality attributes of your system early on is crucial.

For example, will the system need to scale in deployment size, or be exceptionally fast with one set of tasks? A distributed microservices architecture can handle high volumes and offer flexibility, allowing you to co-locate with multiple trading venues and data sources in different geographical regions. Conversely, a monolithic design might be easier to deploy, and perform much faster at processing a set of instructions for when the strategy demands a fast reaction to market activity and new data.

Is it better to handle all of the work in a single thread, or should multithreading be implemented, taking on the overhead of complexity, context sharing and locking but with the advantage of parallel processing. Is a hybrid approach more favorable - doing the majority of the hot path work in a single thread and using dedicated threads for the cold path, writing to a backup database for example.

What happens when the exchange goes down mid-session and you need to identify possible trade discrepancies? Do you have a reliable data store and can you identify the positional risks since your last order execution acknowledgement?

This article explores some important considerations in Trading Systems Architecture, helping you to navigate some of the complexities and trade-offs involved.

Languages, Libraries, and Dependencies

You can build a trading system using any general purpose programming language, but choosing one to suit your experience level as well as the needs of the system will allow for a more frictionless development experience and at extreme levels, can affect performance. Let’s take a look at some popular programming language and address their pro’s and cons (somewhat opinionated).

One further advantage of using a language like C++ that does not rely on a runtime environment, is that debugging can be easier due to the deterministic nature of memory management. The use of a debugger can be extremely powerful in stepping through strategy simulations throughout testing and development.

Some would argue that C++ can be unsafe due to the potential for developers to mistakenly create memory leaks, but the use of smart pointers or a memory profiling tool quickly extinguish those concerns.

Quality Attributes

Quality attributes, also known as non-functional requirements, are essential characteristics that guide the implementation of software architecture. These attributes ensure that the software not only meets functional requirements but also gives priority to the requirements of the project.

Identify a handful of quality attributes that will be priorities during the development of your system, recognizing that one attribute may sometimes lead to trade-offs with others. For instance, making a system highly compatible can affect its maintainability; and making a system highly scalable could affect its performance. Here’s a closer look at some critical quality attributes in software architecture:

By giving due consideration to these attributes, developers can create software that stands the test of time and meets the evolving demands of users.

A trading system will genererally prioritize performance, reliability, fault tolerance and extensibility. Attributes like compatibility, portability, reusability are obviously desirable but usually less important to a proprietary trading system.

Architecture Pattern

Choosing an appropriate architectural pattern for a software project is a critical decision that can only be made after identifying priority quality attributes, and as such should be selected to best suit those requirements. Here’s a detailed look at several common architecture patterns, along with considerations for selecting the right one for your needs.

It’s fairly obvious that for the development of a client trading system, the monolothic and service-oriented architectures are the most typical approaches. But as the system grows in complexity, other patterns can start to look appealling. For example, a layered architecture might be appropriate when your system has an internal pricing model, or a pre-trade risk management component, that would likely be layerered between the API and strategy components. It’s important to remember that a systems architecture is evolutionary, like the software itself.

Carefully evaluate the trade-offs of each pattern to ensure your chosen architecture aligns with your project’s goals and constraints.

System Components

Identify and modularize key feature components of your system (e.g., order management, risk management, market data handling). Modular design enhances maintainability and allows for independent scaling and upgrades. Even if a monolithic architecture pattern is selected for the software, modular code design is always a good idea.

A simple strategy integrated into a trading suite like MetaTrader or ThinkOrSwim might only involve the strategy component and a pre-trade risk component; but a standalone client will have multiple components that need to interact in with each other. Identifying and grouping responsibilities of the software can help determine which components must exist in isolation and how interaction should be handled between them. For example, post-trade risk computation might run in parallel to the order management engine, but at times will need to interrupt normal operation.

Tactics

Architectural tactics are a key abstraction of software architecture, supporting the systematic design and analysis of software to satisfy quality attributes. They are fundamental strategies that address common challenges in software design and directly impact the system's overall effectiveness.

Giving consideration to tactics up front can help to plan for better performance, failure handling, and improved interaction between components, for example. Here are some things to think about when considering which tactics you might want to use in your system:

Testing

Outside of typical strategy backtesting, robust system testing is critical to ensure reliability and accuracy. A good architecture will define minimum standards for testing and coverage; and might even select a testing framework.

By considering the approach to testing early in the architectural design, tests will be implemented sooner, and issues can be identified and addressed early on in the development cycle. This early detection helps in reducing the cost and effort required to fix problems later. Planning to test up front also encourages a modular and loosely coupled design of system components and functions. This allows for individual components to be tested independently without needing to be refactored.

It goes without saying that integration tests carry the most weight in a trading system, but unit tests on algorithms, validators and arithmetic are important, and if performance is a concern, stress or benchmark tests have their place too.

Other Considerations

Precision

Accurate numerical computations are crucial in financial software. Choose data types that offer the required precision to avoid rounding errors, inaccuracies; and be sure to log whenever numerical limits are approached or reached. Ensure that floating point residuals are cleaned up correctly and reconcile with exchange values.

Logging

Effective logging provides insight into system behavior and aids in troubleshooting. Implement comprehensive logging mechanisms that capture relevant data without significantly impacting performance.

Error Handling

Proper error handling prevents unnecessary crashes, enhancing the user experience by providing informative feedback and maintaining functionality. It also aids in debugging and maintenance, allowing developers to identify and fix issues more efficiently.

Wrapping Up

It's clear that software architecture is critical to successful trading systems development. The integrity it demands ensures that systems can meet their demands and evolve with changing requirements. By investing time and effort into a well-thought-out architectural design, engineers can preempt many potential issues, leading to more efficient and effective software solutions. Ultimately, a strong software architecture underpins the technical aspects of a project and is a key factor in its long-term success.