A single-process equity exchange written in C++, running on AWS c6i.metal bare-metal (128-core Intel Ice Lake). Clients connect over TCP using the FIX 4.2 protocol to submit orders and receive execution reports. Market data is broadcast over UDP multicast as binary packets. A price-time priority matching engine runs on a dedicated thread. Resting orders, fills, cancels, and runtime symbol registrations are persisted to SQLite so the book survives restarts and crashes. Pre-trade risk controls are enforced before orders reach the matching engine.
See docs/BENCHMARKS.md for methodology and scenario descriptions. See docs/PERFORMANCE.md for a history of performance improvements.
Benchmarks run automatically on AWS c6i.metal bare-metal after each release. Numbers reflect real hardware performance.
RTT latency distribution across all scenarios (latest release):
Solid lines are client-perceived RTT; dashed lines are internal exchange processing time (excludes TCP and client overhead). Results are recorded on each tagged release and committed to the repo.
![]() |
![]() |
To run the benchmark and record results for the current tagged version:
. .venv/bin/activate
pip install rich matplotlib numpy # first time only
python3 bench/bench.py --save # records results to bench/results.db
python3 bench/plot_history.py # regenerates trend charts from DB| Dependency | Version | Install |
|---|---|---|
| g++ or clang++ | C++14+ | system |
| CMake | 3.20+ | system |
| QuickFIX | 1.14+ | sudo apt install libquickfix-dev |
| SQLite3 | 3.x | sudo apt install libsqlite3-dev |
| OpenSSL | any | usually pre-installed |
| abseil-cpp | 20240722.0 | fetched automatically by CMake |
sudo apt install libquickfix-dev libsqlite3-devSee docs/ARCHITECTURE.md for the full component diagram and design decisions.
flowchart LR
Clients["Clients\n(FIX 4.2 / TCP\nport 5001)"]
Admin["Admin Client\n(plain TCP\nport 5002)"]
GW["FIX Gateway\n(QuickFIX acceptor)"]
ME["Matching Engine\n(single thread)"]
MDP["Market Data\nPublisher"]
MCast["UDP Multicast\n239.1.1.1:5003"]
Clients -->|"D / F"| GW
GW -->|"8"| Clients
GW -->|"Order / Cancel"| ME
ME -->|"Fill events"| GW
ME -->|"Fill events"| MDP
MDP -->|"binary packets"| MCast
Admin -->|"REGISTER"| ME
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j$(nproc)For a release build (targets c6i.metal / Ice Lake Xeon — run on the server, not locally):
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)The binary is placed at build/fix-exchange.
./build/fix-exchange config/exchange.cfgThe exchange starts a FIX acceptor on port 5001 and an admin gateway on port 5002. Session logs go to log/ and sequence number state to store/. Both directories are created automatically on first run. See docs/MESSAGES.md for the FIX message reference and UDP market data wire format.
Stop with Ctrl+C or SIGTERM.
To reset sequence numbers between runs, delete store/:
rm -rf store/A browser-based interface for placing orders and watching the live order book. See docs/UI.md for full details.
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
python3 ui/main.py # → http://localhost:8080Each server process claims one FIX session from the pool. Run two instances on different ports to get two independent clients that can trade against each other.
The test suite manages the exchange process itself — no manual server start required:
python3 tests/run_all.pyThe binary must be built first. Tests connect over raw TCP on port 5001 using hand-rolled FIX framing with no external Python libraries. UI server tests additionally start a uvicorn subprocess on port 18080 and use websockets to connect.
The config file is a QuickFIX acceptor config extended with [EXCHANGE] and [RISK] sections. See docs/CONFIGURATION.md for a full reference.
Key settings in config/exchange.cfg:
[DEFAULT]
BeginString=FIX.4.2
DataDictionary=spec/FIX42.xml
FileStorePath=store
FileLogPath=log
SocketAcceptPort=5001
[EXCHANGE]
Symbols=AAPL,MSFT,GOOG,AMZN
AdminPort=5002
MulticastGroup=239.1.1.1
MulticastPort=5003
SessionPool=8
DatabasePath=store/exchange.db


