Simple Yet Effective Architecture Patterns for Algorithmic Trading

In this thread, I’ll show you how to develop trading software that is easy to maintain and update, while making it simple to collect statistics and track bugs.

1/ Events-Driven Architecture

At the core of this architecture are EVENTS. Every significant action, such as receiving data or processing results, will generate events. This approach allows:

  • Clear tracking of system activities
  • Standardized interaction between system components
  • Easy addition of new event handlers

Example of an event:

class BalanceUpdate(Event): def __init__(self, timestamp, symbol, amount) -> None: self.type = 'BALANCEUPDATE' self.timestamp = timestamp self.symbol = symbol self.amount = float(amount)
Enter fullscreen mode
Exit fullscreen mode

In our trading system, we need balance updates via websocket or REST API. Other updates include price changes, order info, and internal system events. Each module handles a specific function and generates events like BalanceUpdate.

2/ Data Storage

It’s most efficient to store all data in a single repository. This includes price updates, balances, order data, signals, etc. Here’s the workflow: The data module requests from a remote server, processes the response, and generates an event. Event handlers then save these data to the repository or trigger other handlers.

class Balances: def __init__(self): self.data = {} self.total = 0 self.usd_values = {} def update(self, event): self.data[event.symbol] = event.amount def read(self): return self.data
Enter fullscreen mode
Exit fullscreen mode

Storing data in memory is the simplest and fastest method, suitable for most trading algorithms that don’t require permanent data storage. For more complex needs, use Redis for an in-memory storage interface, especially when accessing data from other microservices.

3/ Configuration

Separating settings into a config file is good practice for trading software and any other system. This centralizes settings, making changes easy to manage.

Example config:

[general]
username = my_user_name [trade]
markets = ['BTC/USDT', 'ETH/USDT']
Enter fullscreen mode
Exit fullscreen mode

Read config with configparser:

def read_config(self): config_object = ConfigParser() config_object.read('config.ini') settings = {sect: dict(config_object.items(sect)) for sect in config_object.sections()} settings['trade']['markets'] = eval(settings['trade']['markets']) #string to dict
 return settings
Enter fullscreen mode
Exit fullscreen mode

4/ Event Processing Loop

Consider this the main file coordinating all system elements:

  • Read settings from config
  • Initialize system modules with settings
  • Start the event processing loop
  • Launch tasks like balance updates every minute or continuous websocket listening

Example balance update event handling:

if event.type == 'BALANCEUPDATE': print('BALANCE UPDATE') self.data_storage.balances.update(event) self.data_storage.calc_usd_value()
Enter fullscreen mode
Exit fullscreen mode

In summary, we’ve covered several components of a trading system without delving into specific strategies: events, data storage, config, and event loop. These practices are essential for developing any trading algorithm. In the next article, I’ll discuss strategy modules, portfolio management, risk management, and order execution.

All the code will be open-source and available upon the beta release of the service, developed in collaboration with aspis.finance —a DeFi platform that makes trader-investor relationships transparent through smart contracts.

Got questions? Reach out on TG @JungleSven.


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top