Useful Design Patterns for Algorithmic Trading
Posted on Fri 28 March 2025 in Software Architecture
In this post, I want to walk through some of the software design patterns that I've found useful when building algorithmic trading systems. Many of these are implemented in my open-source Python package pattern_kit, a library of reusable design patterns aimed at making codebases more modular and maintainable, these patterns might help you structure things in a way that's easier to maintain and extend.
This isn't an exhaustive list of patterns, just the ones I personally use and that have proven helpful in real-world setups. The examples will be Pythonic and focused on practical use in a trading context.
Abstract Factory Pattern
The Abstract Factory pattern lets you define an interface for a family of related objects, and then implement that interface in multiple concrete classes. In trading, this is super useful when you want to swap implementations without rewriting logic downstream.
For example:
class AbstractBroker:
async def place_order(self, order): ...
async def cancel_order(self, order_id): ...
class CoinbaseBroker(AbstractBroker):
async def place_order(self, order):
# Coinbase API logic here
pass
class IBKRBroker(AbstractBroker):
async def place_order(self, order):
# IBKR API logic here
pass
Now your trading logic can rely on the AbstractBroker
interface and remain unaware of the specific broker implementation.
Service Locator
This is a pattern that helps decouple components in your system. Instead of passing everything around manually, you register shared services in a central registry and retrieve them when needed.
from pattern_kit import ServiceLocator
ServiceLocator.register("Broker", CoinbaseBroker())
# Later, in some strategy or component
broker = ServiceLocator["Broker"]
await broker.place_order(order)
This avoids threading a million constructor arguments through your app and makes components easier to isolate and test.
Config-Based Instantiation
Sometimes you'll want to define your dependencies in a config file (like YAML) and instantiate them dynamically. This is super handy when you want to support multiple environments (backtest, sim, live) or switch components without touching code.
Example config:
broker:
class: CoinbaseBroker
args:
api_key: "xxx"
secret: "yyy"
You can then use a simple factory function to create instances based on this config:
def build_from_config(cfg):
cls = globals()[cfg['class']]
return cls(**cfg.get('args', {}))
broker = build_from_config(config['broker'])
While not a classic design pattern, this approach complements the factory and service locator ideas well, enabling flexible, config-driven architecture.
Event Emitter
This one's super useful for decoupling logic. Instead of hardcoding callbacks or procedural logic, you emit events and let interested listeners subscribe.
from pattern_kit import EventEmitter
emitter = EventEmitter()
async def on_order_filled(event):
print("Got order filled event", event)
emitter.on("ORDER_FILLED", on_order_filled)
# Somewhere else in your app
emitter.emit("ORDER_FILLED", OrderFilledEvent(...))
This pattern is a clean way to structure complex workflows without coupling components too tightly.
Closing thoughts
These patterns aren't just academic - they make a big difference when you're building a real system with lots of moving parts. Trading systems often need to run in multiple modes (backtest, sim, live), use swappable components (different brokers, data providers), and handle tons of dynamic events. These patterns give you structure without getting in the way.
I'll probably write about more patterns later, but this is a solid starting point.