Joe Archondis
July 6, 2026 · 10 min read
Fintech & Algo Trading
Algorithmic Trading System Architecture: A Complete Breakdown
At Enigma Securities, I built an HFT platform from scratch. Market data connector, OMS, risk engine, execution layer — the full stack, no existing infrastructure to inherit. The latency target was sub-15ms tick-to-order. We hit it. This is the architecture that got there, and the design decisions that determined whether we hit that number or missed it by an order of magnitude.
The Five Core Components
Every algo trading system has the same five layers. The complexity inside each layer varies from simple to very complex depending on your strategy and latency requirements, but the structure holds from a basic VWAP executor all the way to a full HFT stack.
- Market data feed handler
- Strategy engine (signal generation and position logic)
- Order Management System (OMS)
- Risk engine
- Execution Management System (EMS)
Skip any one of these and you either can't trade or you'll lose money in ways that won't be visible until something breaks badly. The connections between components matter as much as the components themselves. A fast strategy engine running on a slow market data feed still executes late.
Market Data Infrastructure
Market data is where latency begins. Every component downstream inherits the timestamp error from this layer. A slow feed handler means your strategy runs on stale prices and your orders arrive behind the market.
The first design decision: raw versus normalized market data. Raw feed processing in C++ via a binary protocol — ITCH for Nasdaq, SBE for CME derivatives — is fastest. You parse directly from the network socket, build your own order book from the raw message stream, no abstraction layer between the wire and your state. Normalized data through a vendor like Refinitiv or middleware like 29West is slower but far more practical when you're running multi-venue.
At Enigma, we ran raw feeds for primary execution venues and normalized data for reference price calculation. Parsing ITCH in C++ with a lock-free ring buffer added roughly 3-4 microseconds from NIC interrupt to book update. A normalized feed over UDP multicast added 50-200 microseconds depending on vendor and network path. That gap is the cost of abstraction.
Clock synchronization is non-negotiable. Without PTP (Precision Time Protocol) hardware timestamping across your servers, your latency measurements are wrong and your event logs can't be trusted. A few hundred dollars of network hardware saves hours of debugging mysterious latency spikes.
Handle feed drops. Exchanges have gaps, connectivity blips, and occasional full reconnects. Your feed handler needs sequence number tracking, gap fill requests, and a clean state reset procedure. A strategy running on a stale book during a feed outage will trade on garbage — no exception.
Order Management System Design
The OMS is the state machine at the center of everything. Every order has a lifecycle: new, pending, acknowledged, partially filled, filled or cancelled. The OMS tracks that lifecycle and is the single source of truth for open orders and positions.
The core data structure is an in-memory order map: order ID to order state object. Fast lookup matters — fill confirmations, partial fills, and cancel acknowledgments arrive in rapid succession during active trading. In C++, an unordered_map with a custom slab allocator handles this cleanly. In Python at moderate throughput, a plain dict is fine.
Position tracking belongs in the OMS. It maintains running net position per instrument, per account, updated on every fill event. The risk engine reads from these positions for pre-trade checks. Never allow two components to independently track position state. That's how you end up with a real position of +100 contracts and a risk view of +50.
Persistence requires care. Writing to disk on the critical path adds latency. The production pattern: in-memory is the source of truth during live trading, every state change is appended to a write-ahead log (WAL) asynchronously on a separate thread, and recovery on restart reads the WAL to rebuild state. SQLite with WAL mode works for this at moderate scale. Kafka works at higher throughput.
Design the OMS as a pure state machine first. Resist the urge to add business logic inside it. When the OMS also does routing decisions or position-weighted sizing, it becomes hard to test and impossible to reason about under production pressure.
Risk Management
The risk engine has one job: prevent the system from doing things that cause catastrophic loss. It should be fast and simple. Complex risk logic has bugs, and bugs in risk logic are expensive in the specific way that ends careers.
Pre-trade checks run synchronously on every order submission before it reaches the EMS:
- Position limits: will this order push net position above the max per instrument?
- Notional limits: is the order value within the single-order notional cap?
- Fat finger: is the submitted price more than X% from the current market mid?
- Rate limits: are we submitting more than N orders per second?
- Duplicate detection: is this order ID already in the system?
Each check should be a single comparison against a pre-computed threshold. At Enigma, our full pre-trade suite ran in under 2 microseconds. That's achievable in C++ with no dynamic allocation on the hot path. Every microsecond you add here is a microsecond subtracted from your execution edge.
The kill switch is the most important control in the system. One API call that cancels all open orders, stops order submission, and triggers position flattening. Build it on day one. Test it regularly, including during market hours with real orders in flight. A kill switch you've never tested under load is not a kill switch.
Real-time P&L tracking means marking open positions to mid every few hundred milliseconds and comparing against drawdown limits. When cumulative P&L drops below a threshold, alert immediately. At a harder threshold, trigger the kill switch automatically. The first version can keep a human in the loop for the final decision.
Execution Management System
The EMS translates OMS orders into exchange instructions. For a single-venue setup, this layer is thin: wrap the order in a FIX message, send it over the FIX session to the exchange, receive execution reports back.
FIX (Financial Information eXchange) is the industry standard. Nearly every exchange, prime broker, and dark pool supports it. A FIX 4.4 or 5.0 session gives you order submission, modification, cancellation, and execution reports. QuickFIX/n in C++ and QuickFIX in Python are the standard open-source implementations. Both are production-tested at scale.
For multi-venue execution, the EMS adds Smart Order Routing (SOR): logic that decides which venue offers the best execution for a given order size. SOR consumes consolidated market data and outputs a routing decision per order. Build SOR only when you actually need it. It adds significant complexity and another potential layer of latency.
Co-location changes the EMS design at the margins. When you're physically in the exchange's data center, round-trip to the matching engine drops from single-digit milliseconds to sub-100 microseconds. At that point, your FIX library internals, socket I/O model, and CPU affinity settings become the bottleneck, not your application logic. Disable Nagle's algorithm with TCP_NODELAY. Pin execution threads to isolated cores. For sub-microsecond work, consider kernel bypass networking via DPDK.
For systematic strategies with holding periods above a few seconds, co-location is unnecessary. A cloud VM with good exchange connectivity is fine. The real question: does your strategy's edge get consumed by single-digit millisecond execution latency? If not, co-location is overhead, not an advantage.
Reference Architecture and Technology Choices
The full data flow from tick to trade:
[Exchange Feed] ──> [Feed Handler] ──> [Market Data Bus]
│
[Strategy Engine]
│
[Signal]
│
[Risk Engine] <──> [OMS]
│
[EMS]
│
[Exchange Gateway] Internal messaging format matters. At Enigma we used custom binary structs over shared memory for intra-process messaging on the critical path — no serialization, no copies, no dynamic allocation. For Python-based systems or less latency-sensitive designs, ZeroMQ over IPC is fast enough and far simpler to operate.
Technology stack by layer:
| Component | HFT (sub-ms latency) | Systematic (>1s holding) |
|---|---|---|
| Feed handler | C++, ITCH/SBE, lock-free ring buffer | Python asyncio or vendor normalized feed |
| Internal bus | Shared memory, custom binary structs | ZeroMQ IPC |
| OMS state | C++ unordered_map, slab allocator | Python dict |
| Persistence | Custom WAL, async writer thread | SQLite WAL or Kafka |
| FIX connectivity | QuickFIX/n (C++) | QuickFIX (Python) or exchange SDK |
| Time series | InfluxDB or custom binary tick files | TimescaleDB |
| Monitoring | Prometheus + Grafana, P&L dashboards | Prometheus + Grafana |
The critical path from tick to order should touch the minimum possible number of components. Every additional hop adds latency. Every allocation on the hot path adds GC pressure in Python or heap fragmentation in C++. Profile before you optimize, but design for simplicity from the start. A simple system you can reason about under pressure beats a complex one you can't.
The most expensive mistakes in trading systems are not bugs that crash the system. They're bugs that silently produce wrong positions — a stale book, a missed fill confirmation, a risk check that passed when it shouldn't have. Design for observability from day one.
Frequently Asked Questions
What programming language should I use for an algorithmic trading system?
Depends on your latency target. For sub-millisecond execution, C++ is standard — the control over allocation, threading, and CPU scheduling that you need doesn't exist in other languages. For systematic strategies with holding periods above a few seconds, Python with asyncio is completely workable and much faster to build with. The common pattern: C++ on the hot path, Python for everything else — strategy research, backtesting, risk monitoring, and ops tooling.
What's the difference between an OMS and an EMS in trading systems?
The OMS manages order state and positions. It tracks the lifecycle of every order and is the source of truth for what you own. The EMS handles execution routing — it takes an order from the OMS and handles the mechanics of getting it to the right exchange in the right format. In simple single-venue setups they're often the same component. As you add venues, smart order routing, and execution algorithms, they become distinct systems with separate concerns.
Do you need co-location for algorithmic trading?
Only for HFT. Co-location reduces round-trip latency from a few milliseconds to sub-100 microseconds, which matters when you're competing on speed at the microsecond level. For systematic strategies with holding periods of seconds or longer, a cloud VM with good exchange connectivity is fine. Ask yourself: does my strategy's edge get consumed by single-digit millisecond execution latency? If not, co-location is overhead, not an advantage.
How do you test an algorithmic trading system before going live?
Three stages. Backtesting against historical data first — this validates signal logic but tells you nothing about execution quality. Paper trading second: running your full system live with real market data but submitting orders to a simulated exchange. FIX-compatible simulators like BANZAI or SimBroker work well here. Then live trading with hard position and notional limits well below your actual risk appetite. Start small, measure everything, and verify that live fills match paper trading fills before sizing up.
How do you prevent a trading system from losing money due to bugs?
Multiple layers working together. Pre-trade risk checks on every order submission: position limits, notional caps, fat finger protection, rate limits. A kill switch that reliably cancels all open orders and stops submission instantly. A maximum daily drawdown that auto-triggers the kill switch. Staged rollout: paper trading before committing capital, then live with tiny size. Full logging of every order, fill, and risk check decision so you can reconstruct exactly what the system did. Regular simulation of the kill switch under load, not just in staging environments.
Working on something similar?
I build AI agents and low-latency systems. If you're trying to solve a version of this, let's talk.
Get in touchAuthor: Joe Archondis — AI systems engineer and HFT infrastructure builder.
Last updated: 2026-07-06