A high-performance FIX engine in modern C++ built for low-latency electronic trading infrastructure.
openfix delivers a complete FIX protocol implementation — session management, message persistence, TLS, and real-time monitoring — on top of an epoll-driven architecture tuned for microsecond-level latency.
- Full FIX Protocol — Header/body/trailer parsing, validation, and serialization with configurable XML-based dictionaries
- Session Management — Logon/logout state machine, heartbeat monitoring, sequence number tracking, and automatic message recovery via resend requests
- Acceptor & Initiator — Dual-role support for both server and client FIX endpoints
- High-Performance I/O — Multi-threaded epoll with non-blocking sockets, TCP_NODELAY/TCP_QUICKACK, vectored writes, and configurable reader threads
- TLS/SSL — BoringSSL-backed secure connections with certificate validation, client certs, and custom CA bundles
- CPU Orchestrator — Thread-to-core affinity pinning with automatic physical core detection, NUMA awareness, and SMT sibling avoidance
- Lock-Free Worker Queues —
moodycamel::ConcurrentQueuepowers the optional dispatcher/timer infrastructure - SIMD Acceleration — AVX2/SSE2-optimized FIX checksum (32 bytes/cycle via
_mm256_sad_epu8), SIMD-acceleratedmemchrfield scanning in the parse loop - Message Persistence — File-based message caching and logging per session for sequence recovery and audit trails
- Admin Dashboard — Built-in Crow web interface for real-time session monitoring, sequence management, and diagnostics
- Zero-Copy Parsing —
string_view-based message parsing to minimize allocations on the hot path
openfix uses Bazel for hermetic, reproducible builds. The repo is configured for gnu++23, so you will need a C++23-capable compiler.
# Build the core library
bazel build //src:openfix
# Build and run the example application
bazel build //example:openfix-example
# Run the test suite
bazel test //test:openfix-test
# Run performance benchmarks
bazel run //test/performance:openfix-perfThe example application demonstrates a simple acceptor/initiator pair communicating over FIX 4.2.
Terminal 1 — Start the acceptor:
bazel run //example:openfix-example -- acceptorTerminal 2 — Start the initiator:
bazel run //example:openfix-example -- initiator#include <openfix/Application.h>
Application app;
SessionSettings settings;
settings.setString(SessionSettings::SESSION_TYPE_STR, "acceptor");
settings.setString(SessionSettings::BEGIN_STRING, "FIX.4.2");
settings.setString(SessionSettings::SENDER_COMP_ID, "SERVER");
settings.setString(SessionSettings::TARGET_COMP_ID, "CLIENT");
settings.setString(SessionSettings::FIX_DICTIONARY, "/path/to/FIXDictionary.xml");
settings.setLong(SessionSettings::ACCEPT_PORT, 12121);
app.createSession("MY_SESSION", settings);
app.start();openfix is configured through PlatformSettings (global) and SessionSettings (per-session).
| Setting | Default | Description |
|---|---|---|
InputThreads |
1 |
Number of epoll reader threads |
SocketSendBufSize |
OS default | TCP send buffer size |
SocketRecvBufSize |
OS default | TCP receive buffer size |
UpdateDelay |
1000 |
Session update interval (ms) |
EpollTimeout |
1000 |
Epoll wait timeout (ms) |
LogPath |
./log |
Directory for log output |
DataPath |
./data |
Directory for persistent data |
AdminWebsitePort |
51234 |
Admin dashboard HTTP port (0 to disable) |
CpuCores |
auto | Explicit core list (e.g. 2,3,4,5) |
CpuAvoidHT |
false |
Skip SMT siblings in auto-detection |
CpuNumaNode |
-1 |
NUMA node affinity (-1 = auto) |
| Setting | Default | Description |
|---|---|---|
BeginString |
— | FIX version (e.g. FIX.4.2) |
SenderCompID |
— | Sender identifier |
TargetCompID |
— | Target identifier |
TestSession |
false |
Mark the session as a FIX test session |
FIXDictionary |
— | Path to FIX dictionary XML |
SessionType |
— | acceptor or initiator |
AcceptPort |
— | Listening port (acceptor) |
ConnectHost / ConnectPort |
— | Remote endpoint (initiator) |
HeartbeatInterval |
10 |
Heartbeat interval (seconds) |
LogonInterval |
10 |
Logon retry interval (seconds) |
ReconnectInterval |
10 |
Reconnect interval (seconds) |
ConnectTimeout |
5000 |
Connection timeout (ms) |
ResetSeqNumOnLogon |
false |
Reset sequence numbers on each logon |
AllowResetSeqNumFlag |
false |
Accept incoming ResetSeqNumFlag on logon |
SendNextExpectedMsgSeqNum |
true |
Include tag 789 in logon |
TCPNoDelay |
true |
Enable TCP_NODELAY on session sockets |
TCPQuickAck |
true |
Enable TCP_QUICKACK on session sockets |
RelaxedParsing |
false |
Tolerate more malformed input during parse |
LoudParsing |
true |
Log parse/validation errors verbosely |
ValidateRequiredFields |
false |
Enforce required dictionary fields |
TestRequestThreshold |
2.0 |
Heartbeat multiplier before sending a test request |
SendingTimeThreshold |
10 |
Allowed inbound sending-time skew (seconds) |
TLSEnabled |
false |
Enable TLS |
TLSVerifyPeer |
true |
Verify peer certificate |
TLSRequireClientCert |
false |
Require client certificates on TLS acceptors |
TLSCAFile |
— | CA certificate bundle path |
TLSCertFile |
— | Client/server certificate path |
TLSKeyFile |
— | Private key path |
TLSServerName |
— | SNI / TLS hostname override for initiators |
The tables above cover the most important settings; see src/Config.h for the full list.
Application
├── Session (FIX state machine, one per configured endpoint)
│ ├── Dictionary (FIX protocol definition)
│ ├── NetworkHandler (network I/O delegate)
│ ├── IFIXCache (message caching for resends)
│ ├── IFIXLogger (per-session message logging)
│ └── IFIXStore (persistent session state)
└── Network (I/O orchestrator)
└── ReaderThread (epoll event loop, one per InputThreads)
├── ConnectionHandle (per-socket state)
├── ReadBuffer (inbound accumulation)
└── WriteBuffer (outbound batching)
Message flow: The ReaderThread receives data via epoll, parses complete FIX messages, and delivers them to the owning Session through the NetworkDelegate interface. The Session validates checksums, sequence numbers, and timestamps before dispatching to application-level callbacks. Outbound messages go back through the NetworkHandler; on non-TLS sockets they are sent inline when possible, otherwise they are queued and flushed by the ReaderThread with vectored writes.
| Library | Version | Purpose |
|---|---|---|
| spdlog | 1.13.0 | Structured logging |
| pugixml | 1.14 | FIX dictionary XML parsing |
| BoringSSL | 0.20240930.0 | TLS/SSL |
| Crow | 1.0 (pinned git override) | Admin web dashboard |
| concurrentqueue | 1.0.4 | Lock-free MPMC queues |
| unordered_dense | 4.8.1 | High-performance hash maps |
| Google Test | 1.17.0 | Testing framework |
All dependencies are fetched automatically by Bazel via MODULE.bazel — no manual installation required.
Benchmarks are available under test/performance/ and include CPU-only parse / serialize / checksum tests plus network comparison tests against QuickFIX.
# Run openfix benchmarks
bazel run //test/performance:openfix-perf
# Run QuickFIX comparison benchmarks
bazel run //test/performance/quickfix:quickfix-perf
# Run isolated benchmarks with CPU pinning (best results with sudo)
./test/performance/run_isolated.shRecent isolated run on Linux (x86_64), optimized build (-c opt), primary cores auto-detected by run_isolated.sh, 5 iterations with warmup. Median values reported.
| Benchmark | openfix | QuickFIX | Result |
|---|---|---|---|
| Throughput (median, msg/s) | 1,968,514 | 563,234 | openfix 3.5x higher |
| RoundTrip TestReq-HB (median avg, µs) | 8.927 | 11.199 | openfix 20.3% lower |
| RoundTrip Multileg-AB-8 (median avg, µs) | 11.343 | 14.580 | openfix 22.2% lower |
openfix leads across all three network benchmarks — aggregate throughput, simple TestRequest -> Heartbeat round trip, and the multileg application-flow round trip. Exact numbers will vary with CPU governor, kernel scheduling, and whether the script is run with sudo.
Note: For the most stable results, run the isolated benchmark script with
sudoto enable CPU frequency pinning:sudo ./test/performance/run_isolated.sh
| Benchmark | Msgs/sec | Avg Latency (µs) |
|---|---|---|
| Parse/Heartbeat | 5,733,115 | 0.174 |
| Parse/NewOrderSingle | 4,193,032 | 0.238 |
| Serialize/Heartbeat | 2,765,232 | 0.362 |
| Serialize/NewOrderSingle | 1,400,075 | 0.714 |
Parse throughput benefits from memchr-based SIMD field scanning, O(1) flat-array lookups for field types and group specs, bitset-accelerated duplicate detection, and zero-copy string_view storage.