A C++ FIX decoding library and CLI tool.
FixDecoder loads QuickFIX XML dictionaries, parses raw FIX messages, and decodes fields into typed C++ values.
It can return:
fix::DecodedMessagefor field-by-field inspection (tag,name,type, typed value)fix::DecodedObjectfor enum/tag-indexed access (msg[FieldTag::kX])
It supports:
FIX.4.0FIX.4.1FIX.4.2FIX.4.3FIX.4.4FIX.5.0/FIX.5.0SP1/FIX.5.0SP2(transportFIXT.1.1with1128handling)FIXT.1.1session/admin messages
The project includes:
fixdecoderstatic libraryexample_usageCLI executablefix_controller_demoCLI executable for session-level FIX protocol exchangestools/fix-web-uiDockerized web UI for interactive parsing/validation- GoogleTest-based test suite with version-specific sample messages
The decoder follows five steps:
- Dictionary loading
fix::Dictionaryparses one QuickFIX XML file.fix::DictionarySetloads all XML dictionaries in a directory.
- Message normalization and parsing
fix::Decoderaccepts SOH (0x01) and|separated FIX messages.- It normalizes delimiters and splits the message into
(tag, value)fields.
- FIX version-first decoder selection
- The decoder reads tag
8(BeginString) first. - If tag
1128(ApplVerID) is present, it maps that application version and uses it for decode-map selection. - It then selects the generated per-version decoder map in
data/generated_src/*_decoder_map.h.
- The decoder reads tag
- Typed decode and object materialization
- Each tag is resolved through generated
decoderTagFor(tag)and decoded to a typed C++ value (bool,std::int64_t,float,double,std::string_view). decode(...)returnsfix::DecodedMessage(ordered field list plus dictionary metadata).decodeObject(...)returnsfix::DecodedObject(map-like object for enum/tag lookup).
- Each tag is resolved through generated
- Structural semantic validation
- Decoder validates message/component/group requirements from dictionaries.
- Output includes:
structurally_valid(true/false)validation_errors(human-readable semantic errors, for example missing required fields or group-count mismatches)
include/public headers (fix_decoder.h,fix_dictionary.h,fix_msgtype_key.h)src/library sources and example executable (examples.cc)scripts/helper scripts to fetch dictionaries and sample messagestools/fix-web-ui/Dockerized Flask web app + parser utilitydata/quickfix/dictionary XML filesdata/samples/valid/valid sample messages per FIX versiontest/unit/GoogleTest suite and unit test sample messagestest/integration/dockerized integration/performance harness
tools/fix-web-ui/README.md- Dockerized FIX web parser UItest/integration/README.md- Integration and profiling container workflowsMessageToObject/README.md- Message-to-object generator map library details
Build and install were tested on Ubuntu 24.04.
Required:
- CMake
>= 3.26 - C++23 compiler (
g++orclang++)
Default behavior:
tinyxml2andgoogletestare fetched automatically with CMakeFetchContent.
If you want to use system-installed dependencies instead:
- Configure with
-DFIXDECODER_FETCH_TINYXML2=OFF -DFIXDECODER_FETCH_GOOGLETEST=OFF.
git clone https://github.com/kingkybel/FixDecoder.git
cd FixDecoder
# Only needed if your environment requires explicit submodule checkout.
git submodule update --init --recursive
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build --parallel $(nproc)# Change this to your preferred install location.
INSTALL_PREFIX=/usr/local
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}
cmake --build build --parallel $(nproc)
sudo cmake --install buildCurrent install target installs public headers to:
${INSTALL_PREFIX}/include/dkyb
Example include after install:
#include <dkyb/fix_decoder.h>Fetch dictionary XML files:
./scripts/fetch_quickfix_dicts.shFetch per-version valid sample messages used by tests:
./scripts/fetch_sample_fix_messages.shFetch reference symbols/participants and generate realistic grouped FIX samples:
./scripts/generate_realistic_fix_samples.py
# or override category sizes:
# ./scripts/generate_realistic_fix_samples.py --num_correct 850 --num_semantic_incorrect 100 --num_garbled 50This writes:
data/samples/reference/realistic_reference_data.jsondata/samples/realistic/<VERSION>_realistic_correct_850.messagesdata/samples/realistic/<VERSION>_realistic_semantic_incorrect_100.messagesdata/samples/realistic/<VERSION>_realistic_garbled_50.messagesdata/samples/realistic/<VERSION>_realistic_1000.messages(combined compatibility file)
Run with all built-in demo messages:
./build/RelWithDebInfo/bin/example_usageRun with explicit arguments:
./build/RelWithDebInfo/bin/example_usage \
data/quickfix \
"8=FIX.4.2|9=65|35=D|49=BUY|56=SELL|34=2|52=20100225-19:41:57.316|11=ABC|21=1|55=IBM|54=1|60=20100225-19:41:57.316|38=100|40=1|10=062|" \
"8=FIX.4.2|9=61|35=T|55=IBM|38=100|44=123.45|10=000|" \
"8=FIXT.1.1|9=108|35=D|1128=9|49=BUY|56=SELL|34=2|52=20260211-12:00:00.000|11=DEF|55=MSFT|54=1|60=20260211-12:00:00.000|38=250|40=2|44=420.50|10=000|"Argument order:
- arg1: dictionary directory (default:
data/quickfix) - arg2: message for basic
decode(...)field dump - arg3: message for
decodeObject(...)enum/tag lookup - arg4: message for
FIXT.1.1+ApplVerIDrouting example
The executable demonstrates:
decode(...)with field names and typed valuesdecodeObject(...)with enum-based lookupApplVerID(1128) driven selection underFIXT.1.1generator_mapobject construction byMsgType
#include "fix_decoder.h"
fix::Decoder decoder;
std::string err;
if(!decoder.loadDictionariesFromDirectory("data/quickfix", err))
{
// handle error
}
auto decoded = decoder.decode("8=FIX.4.4|35=1|49=TW|56=ISLD|34=2|52=20240210-12:00:00.000|112=HELLO|");#include "fix_decoder.h"
#include "FIX42_decoder_map.h"
fix::Decoder decoder;
auto msg = decoder.decodeObject("8=FIX.4.2|35=T|55=IBM|38=100|44=123.45|");
auto symbol = msg[fix::generated::fix42::FieldTag::kSymbol].as<std::string_view>();
auto qty = msg[fix::generated::fix42::FieldTag::kOrderQty].as<double>();
auto px = msg[fix::generated::fix42::FieldTag::kPrice].as<double>();
if(symbol && qty && px)
{
// use *symbol, *qty, *px
}Build the demo executable:
cmake --build build --target fix_controller_demo --parallel $(nproc)Run locally in two terminals.
Terminal 1 (exchange/listener):
FIX_ROLE=exchange FIX_PORT=5001 ./build/RelWithDebInfo/bin/fix_controller_demoTerminal 2 (client/connector):
FIX_ROLE=client FIX_HOST=127.0.0.1 FIX_PORT=5001 FIX_SCENARIO=handshake ./build/RelWithDebInfo/bin/fix_controller_demoSupported scenarios (FIX_SCENARIO):
handshake(default)out_of_syncgarbledconversation(client sendsFIX_CONVERSATION_MESSAGEStest requests and waits for heartbeat replies)performance(same flow asconversation, with longTestReqIDpayload controlled byFIX_PERF_PAYLOAD_SIZE)
Additional controller-demo environment variables:
FIX_CONVERSATION_MESSAGES(default100)FIX_PERF_PAYLOAD_SIZE(default512)FIX_RUNTIME_SECONDS(default30)FIX_BEGIN_STRING(defaultFIX.4.4, e.g.FIX.4.2orFIXT.1.1)FIX_HOSTS(comma-separated exchange hosts for client role)FIX_PORTS(comma-separated ports forFIX_HOSTS, or a single port reused for all)
Build and run:
docker build -t fix-web-ui -f tools/fix-web-ui/Dockerfile .
docker run --rm -p 8081:8081 fix-web-uiOpen:
http://localhost:8081
Port is configurable via PORT:
docker run --rm -e PORT=8090 -p 8090:8090 fix-web-uiDictionary directory inside container is configurable via FIX_DICT_DIR (default /app/data/quickfix).
Web UI behavior:
- accepts
|or SOH-delimited FIX messages - displays parsed fields with dictionary names/types and typed values
- shows semantic validation status (
Structure Valid) and validation errors - for malformed messages, displays fields parsed up to first malformed token and explains the error
Run all tests:
ctest --test-dir build --output-on-failureThe test suite includes:
- dictionary and decoder unit tests
- data-driven tests over all supported FIX versions
- invalid-message mutation tests derived from valid sample messages
- generated realistic sample tests for:
- correct messages (
*_realistic_correct_850.messages) - semantically incorrect messages (
*_realistic_semantic_incorrect_100.messages) - garbled messages (
*_realistic_garbled_50.messages)
- correct messages (
- controller unit tests for logon handshake, out-of-sync sequence detection, and garbled-message rejection
Unit-test message fixtures are located in:
test/unit/test_messages/
The new session-level controller (fix::Controller) is available in:
include/fix_controller.hsrc/fix_controller.cc
Containerized peer-to-peer tests are defined in:
test/integration/compose.ymltest/integration/Dockerfile.buildertest/integration/Dockerfile.runtimetest/integration/run_all_scenarios.sh- Detailed Docker test documentation:
test/integration/README.md
Profile hot paths + open dashboard:
./test/integration/profile_hotpaths.sh --duration 60
FIX_UID=$(id -u) FIX_GID=$(id -g) FIX_PROFILE_UI_PORT=8080 docker compose -f test/integration/compose.yml --profile profiler-ui up --build -d fix-profiler-ui
xdg-open "http://localhost:8080/"http://localhost:8080/ redirects to test/integration/profiles/latest/index.html.
Run FIX handshake containers:
docker compose -f test/integration/compose.yml --profile single-client up --build --abort-on-container-failure --exit-code-from fix-client-1Run out-of-sync or garbled scenarios:
FIX_SCENARIO=out_of_sync docker compose -f test/integration/compose.yml --profile single-client up --build --abort-on-container-failure --exit-code-from fix-client-1
FIX_SCENARIO=garbled docker compose -f test/integration/compose.yml --profile single-client up --build --abort-on-container-failure --exit-code-from fix-client-1Run longer request/reply conversations and performance loads:
FIX_SCENARIO=conversation docker compose -f test/integration/compose.yml --profile single-client up --build --abort-on-container-failure --exit-code-from fix-client-1
FIX_SCENARIO=performance FIX_CONVERSATION_MESSAGES=300 FIX_PERF_PAYLOAD_SIZE=2048 FIX_RUNTIME_SECONDS=60 docker compose -f test/integration/compose.yml --profile single-client up --build --abort-on-container-failure --exit-code-from fix-client-1Run one client against multiple exchanges and enable a second client:
docker compose -f test/integration/compose.yml --profile multi-exchange up --build --abort-on-container-failure --exit-code-from fix-client-multi
docker compose -f test/integration/compose.yml --profile multi-client up --build --abort-on-container-failure --exit-code-from fix-client-2
docker compose -f test/integration/compose.yml --profile multi-mesh up --build --abort-on-container-failure --exit-code-from fix-client-mesh-1Conversation sample data:
data/samples/conversations/FIX44_conversation_100.messages(100 valid FIX.4.4 conversation messages)
- Generated FIX tag enums are
std::uint32_t, and dictionary/decoder tag storage uses compatible unsigned tag types. - The decoder supports dictionary-driven structural semantic checks (required members/components and group-count consistency).
- It is not a full FIX protocol validator (wire ordering constraints, checksum/body-length enforcement, or session state).
- QuickFIX-style XML dictionaries are expected as dictionary input.
Reduce the smells, keep on top of code-quality. Sonar Qube is run on every push to the main branch on GitHub.