Zero-dependency FIX (Financial Information eXchange) protocol library for Rust, optimized for low-latency trading engines.
Handles FIX message building, parsing, checksum validation, and field access with minimal heap allocations.
[dependencies]
rust-fix = { git = "https://github.com/MyJetTools/rust-fix.git", tag = "0.2.0" }The library provides three main types. Pick the one that fits your use case:
| Type | Use case | Allocations | Owns data? |
|---|---|---|---|
FixMessageWriter |
Build and send outgoing messages | 1 (Vec<u8> body) |
Yes |
FixMessageReader |
Parse and read incoming messages | 0 (borrows input) | No |
FixMessageBuilder |
Parse, store, access, and reserialize | 2 (Vec<u8> buffer + Vec index) |
Yes |
- Hot path (sending):
FixMessageWriter - Hot path (receiving):
FixMessageReader- zero-copy, iterates over borrowed input - Need to store parsed message:
FixMessageBuilder
use rust_fix::FixMessageWriter;
let mut msg = FixMessageWriter::new("FIX.4.4", "A"); // version, message type (35=A Logon)
msg.with_value(34, "1092"); // MsgSeqNum
msg.with_value(49, "TESTBUY1"); // SenderCompID
msg.with_value(52, "20180920-18:24:59.643"); // SendingTime
msg.with_value(56, "TESTSELL1"); // TargetCompID
msg.with_value(98, "0"); // EncryptMethod
msg.with_value(108, "60"); // HeartBtInt
// Tags 8 (BeginString), 9 (BodyLength), 10 (CheckSum) are calculated automatically.
let bytes: Vec<u8> = msg.compile_message(); // SOH-delimited binary
let text: String = msg.to_string(); // pipe-delimited for debugging
// "8=FIX.4.4|9=75|35=A|34=1092|49=TESTBUY1|52=20180920-18:24:59.643|56=TESTSELL1|98=0|108=60|10=178|"From binary (SOH \x01 delimiter):
use rust_fix::FixMessageReader;
let raw = b"8=FIX.4.4\x019=75\x0135=A\x0134=1092\x0149=TESTBUY1\x0156=TESTSELL1\x0198=0\x01108=60\x0110=162\x01";
let reader = FixMessageReader::from_bytes(raw);From human-readable string (pipe | delimiter):
use rust_fix::FixMessageReader;
let text = "8=FIX.4.4|9=75|35=A|34=1092|49=TESTBUY1|56=TESTSELL1|98=0|108=60|10=162|";
let reader = FixMessageReader::from_str(text);Validate checksum and access fields:
// Validate checksum (returns Err if invalid)
let reader = reader.check_payload().unwrap();
// Get single field
let msg_type = reader.get_message_type().unwrap(); // "A"
let sender = reader.get_value(49).unwrap(); // Ok(Some("TESTBUY1"))
// Get multiple values for a repeating tag
let values = reader.get_values(49).unwrap(); // Ok(vec!["TESTBUY1"])
// Iterate all tags
for item in reader.iter() {
let item = item.unwrap();
println!("tag {}={}", item.key.get_value(), item.value);
}Use FixMessageBuilder when you need to keep the parsed data beyond the input lifetime:
use rust_fix::FixMessageBuilder;
let raw = b"8=FIX.4.4\x019=75\x0135=A\x0134=1092\x0149=TESTBUY1\x0156=TESTSELL1\x0198=0\x01108=60\x0110=162\x01";
// Parse with checksum validation (second argument: true = validate, false = skip)
let msg = FixMessageBuilder::from_bytes(raw, true).unwrap();
// Access fields
let msg_type = msg.get_message_type(); // b"A"
let sender = msg.get_value_as_str(49); // Some("TESTBUY1")
let raw_value = msg.get_value(49.into()); // Some(b"TESTBUY1")
// Iterate all values for a tag (useful for repeating groups)
for val in msg.get_values_as_str(49) {
println!("{}", val);
}
// Reserialize
let bytes = msg.compile();
let text = msg.to_string();Build from scratch:
use rust_fix::FixMessageBuilder;
let mut msg = FixMessageBuilder::new("FIX.4.4", "D"); // D = NewOrderSingle
msg.with_value(49, "SENDER");
msg.with_value(56, "TARGET");
msg.with_value(11, "order-123");
let bytes = msg.compile();All parsing operations return Result<T, FixSerializeError>:
| Error | Meaning |
|---|---|
VersionTagNotFound |
Missing tag 8 (BeginString) |
MessageTypeTagNotFound |
Missing tag 35 (MsgType) |
CheckSumTagNotFound |
Missing tag 10, only when validation is enabled |
InvalidCheckSum |
Tag 10 value doesn't match calculated checksum |
BodyLenTagNotFound |
Missing tag 9 (BodyLength) |
FixDelimiterNotFound |
No SOH/pipe delimiter found in input |
InvalidFixItem |
Malformed tag (missing = separator) |
InvalidFixKey |
Tag key is not a valid integer |