Looking for the product overview? See the main README.
- Getting Started
- Configuration (
.mxrc) - Custom Dictionaries / Vendor Specs
- Scripting & Macros
- GUI-Specific
- FIX Protocol
- Troubleshooting
Unlike QuickFIX(/J/n) where you have to write an entire application layer against an API just to get started, MicroFIX is a complete, out-of-the-box workstation. Connecting, sending, validating, and scripting are all available immediately with zero code required. It acts as a native desktop GUI (mxgui) and an interactive CLI (mxshell) built on top of the same deterministic session, scripting, and validation engine.
MicroFIX was born out of the pain of dealing with legacy FIX testing tools. It started as a deep dive into Golang and the inner workings of the FIX protocol, with the goal of building an open-source tool that could benefit developers and testers who are tired of struggling with outdated, clunky interfaces.
Yes! It is fully free, open-source, and carries no strings attached. This will never change. As a consequence of being a free open-source project maintained in free time, some highly specific or unreasonable feature requests may be declined or deprioritized. You are always free to fork the codebase and modify it for your organization's specific needs.
They share the exact same engine, so behavior is identical either way.
- MXGUI - best for interactive/manual work: live log streaming, message inspection/diffing, dictionary browsing, and visually building/running scripts.
- MXShell - best for headless automation: CI pipelines, batch
.mxsscripts, quick REPL-style testing over SSH.
Pre-built binaries are on the Releases page. To build from source see the Installation section - go install for either tool, or a manual go build (the GUI needs CGO + WebKitGTK on Linux/macOS via Wails v3; the CLI is a plain CGO_ENABLED=0 build).
MicroFIX is fully dictionary-driven. It supports standard FIX versions natively and can load custom XML dictionaries for venue-specific extensions or proprietary dialects. Use the following exact values in your .mxrc file to load the internal dictionaries:
| Protocol | Spec Config Value |
|---|---|
| FIX 4.0 | FIX40 |
| FIX 4.1 | FIX41 |
| FIX 4.2 | FIX42 |
| FIX 4.3 | FIX43 |
| FIX 4.4 | FIX44 |
| FIXT 1.1 | FIXT11 |
| FIX 5.0 | FIX50 |
| FIX 5.0 SP1 | FIX50SP1 |
| FIX 5.0 SP2 | FIX50SP2 |
Both tools check ./.mxrc (current directory) and ~/.mxrc (home directory). If neither exists, sensible defaults are used, so you can start MicroFIX with zero setup.
| Setting | Default |
|---|---|
| SenderCompID | SENDER |
| TargetCompID | TARGET |
| FIX Version | FIX44 |
| Heartbeat Interval | 30s |
| Listen Address | 0.0.0.0:1234 |
| Script Timeout | 5s |
| Validation Mode | Strict |
It's a flat JSON file. Common fields:
You can edit this by hand, through the MXGUI Session Settings page, or via the MXShell config command.
This is controlled by whether MicroFIX connects out to IpAddr:Port or listens on it - use the connect vs listen commands in MXShell (or the equivalent buttons in MXGUI's session controls).
Set IpAddr/Port in .mxrc, or override them per-session via the CLI config/connect commands or the GUI Session Settings page.
Set SessionSpec and/or ApplicationSpec to an absolute or relative path to your XML file instead of one of the built-in names (e.g. FIX44). MicroFIX is fully dictionary-driven, so custom message types, components, and fields defined in your XML are immediately available for validation, the Dictionary Browser, and message sampling.
SessionSpec covers admin-level messages (Logon, Heartbeat, Sequence Reset, etc.), while ApplicationSpec covers business messages (orders, executions, etc.). Splitting the two is what allows a FIXT1.1 session layer to be paired with, say, a FIX50/FIX50SP1/FIX50SP2 application layer.
Yes - set SessionSpec: "FIXT11" and ApplicationSpec to whichever FIX50 variant (or custom XML path) your venue uses.
Validation always runs at least at a Basic level (checksum, body length, required fields, repeating groups). Setting FixValidateStrict: true (the default) additionally enforces Strict checks - field type checking and rejection of unknown fields. Set it to false if your venue's messages don't fully conform to the dictionary but you still want basic structural checks.
You can extract your MiniFIX transConf templates and import them as MicroFIX aliases using either the CLI or the GUI. Both methods will report the number of successfully parsed and failed aliases.
Option 1: Via the GUI (Recommended)
- Open the UI and navigate to Settings > Aliases (or use the shortcut
Alt + A). - Click the Import... button and select your MiniFIX
.xmlfile from the dialog. - The review modal will display all successfully extracted transactions and highlight any that failed to parse.
- Use the search bar and checkboxes to filter and select exactly which aliases you want to keep.
- Click import. Note: Existing aliases with the same name will be overwritten, and changes are saved automatically.
Option 2: Via the CLI
You can use the mxshell tool to extract the aliases directly to your terminal:
mxshell -x minifix.xml
This will print a summary of the extraction followed by a JSON object containing the successfully parsed templates. Copy the resulting JSON output and paste it directly into the "Alias" block inside your .mxrc configuration file.
If you are working in the terminal, run mxshell -h for a complete syntax reference. You can also view the scripting syntax directly in the Script Runner within MXGUI.
Each macro prefix serves a specific lifecycle and purpose:
$LASTIN/$LASTOUT: Dynamic extractors valid only during an active session. They query the engine for the most recently processed message of a specific type. Because they pull live data, their values can change unexpectedly if a new message of that type arrives asynchronously. They are best used for quick, immediate lookups.$BUF: A stable, script-local snapshot of a message. It contains the raw message explicitly loaded into the buffer viawait,expect, orloadmsg. Unlike$LASTIN/$LASTOUT, the buffer will not change asynchronously, making it safer and more performant when extracting multiple fields from the same message without risking race conditions.$VARS: An ephemeral scratch namespace used to hold state (like loop counters or stored IDs) during script execution. These variables vanish when the script ends.$ALIAS: Reusable text blobs loaded from your configuration. While they usually contain FIX templates, they are technically just dumb string fragments—meaning you cannot slice them like$BUFor$LASTIN. Aliases are persisted across runs, but only if you explicitly save the config (e.g., via the GUI orconfig savein MXShell). Headless CI runs do not persist aliases modified during execution.
Variables can be injected into scripts, CLI commands, or GUI inputs using the $ prefix.
System & State
| Variable | Description |
|---|---|
$UNIQUE |
Generates a random UUID (e.g. for ClOrdID). |
$UNIQUE[N] |
Generates a random alphanumeric string of length N (maximum 1000). |
$TIMESTAMP |
Current UTC timestamp in YYYYMMDD-HH:MM:SS.000 format. |
$DATE |
Current date in YYYYMMDD format. |
$DATE[+N] |
Current date offset by N days (e.g. $DATE[+1] is tomorrow). |
$STATUS |
Current session state (e.g. Active, Closed). |
$SEQ_IN |
Current internal inbound sequence number. |
$SEQ_OUT |
Current internal outbound sequence number. |
$ERROR |
Error message from the most recent failed condition. |
Context & Store
| Variable | Description |
|---|---|
$CFG.<key> |
Reads a value from the session configuration. |
$VARS.<key> |
Reads a script-defined variable created with the set command. |
$ALIAS.<name> |
Expands a saved alias template. |
$ALIAS.<name>[params] |
Expands a saved alias and substitutes specific tag values at runtime (e.g., $ALIAS.Order[54.2=2,55.2=GOOG]). |
$ENV.<name> |
Reads an environment variable. |
$BUF |
The complete raw FIX message currently in the buffer. |
Message Context (Tag Extraction & Slicing)
$BUF, $LASTIN, and $LASTOUT support tag extraction and string slicing using bracket notation.
| Syntax | Description |
|---|---|
$BUF[Tag] |
Extracts the first occurrence of Tag from the buffered message. |
$BUF[Tag,Inst] |
Extracts the Inst-th occurrence of Tag. |
$BUF[Tag,Inst,End] |
Returns characters from index 0 through End. |
$BUF[Tag,Inst,Start,End] |
Returns characters from Start through End. |
$LASTIN[Msg,Tag,...] |
Extracts or slices a tag from the last incoming message of type Msg. |
$LASTOUT[Msg,Tag,...] |
Extracts or slices a tag from the last outgoing message of type Msg. |
(Note: The instance number defaults to 1 when omitted. Tag instances are counted in message order, starting from 1. Slicing uses zero-based string indexes.)
No - $VARS, $vars, and $VaRs are all treated the same, as are all other macro prefixes. Arguments, values, and payload contents (e.g. bracket contents like the message type in $LASTIN[d,11]) remain case-sensitive.
Aliases are reusable FIX message templates stored under $ALIAS.<name> in .mxrc (or set at runtime). Rather than hardcoding values, you can parameterize them in two ways:
1. Flat Substitution: Embed macros directly into the alias string.
# Define the alias
set $ALIAS.AAPL 35=D|55=AAPL|54=1|38=$UNIQUE[3]|40=2|11=$UNIQUE|
# Send it
send $ALIAS.AAPL
2. Runtime Tag Substitution: Reuse a base alias but override specific tags on the fly using bracket syntax: [tag.instance=value, ...]. (Instance is optional and defaults to 1).
set $ALIAS.BaseOrder 35=D|55=AAPL|54=1|
send $ALIAS.BaseOrder[55=TSLA,54=2]
If a substituted value contains a comma, you must escape it with a backslash (\,). Note: MicroFIX supports complex recursive macro trees (aliases containing aliases containing variables), but if you build deep recursion trees, you must manually ensure that commas remain properly escaped down the call stack.
No, tag substitution is a strict override. If you try to substitute 112=ABC into an alias that doesn't already contain tag 112, it will result in a hard failure.
Rationale: Blindly inserting tags into a raw FIX string breaks structural validity. Inserting a field at random could inadvertently break a repeating group or corrupt the trailer. Therefore, substitution requires a fully valid boilerplate template with dummy values already in place. MicroFIX will substitute the values and auto-finalize the message length/checksums before sending.
No. $LASTIN and $LASTOUT use a simple, lightweight map structure to cache only the most recently seen message for a given MsgType (e.g., the last 35=D). Handling deep history lookup by type would increase the engine's memory footprint for an exceedingly rare use case.
Yes. For example, $LASTIN[V,52,1,8] extracts the first eight characters of tag 52. This is useful when a FIX timestamp or other field contains multiple pieces of information and only part of the value is required. Note that string slicing is supported by $LASTIN, $LASTOUT, and $BUF, but not by $ALIAS.
Use loadmsg <in|out> to pull a specific message from session history straight into the script buffer so $BUF[...] can extract from it. While this accomplishes the same thing as inspecting $LASTIN or $LASTOUT, it has two distinct advantages:
-
Stability: $LASTIN and $LASTOUT can change unpredictably if a new message of that type arrives asynchronously in the background. The buffer ($BUF), however, is a stable snapshot that will never change until your next wait, expect, or loadmsg call.
-
Performance: Executing multiple $BUF extractions against this stable snapshot is faster and more performant than repeatedly querying the live session engine with $LASTIN and $LASTOUT.
Use include <path> to pull in and execute another script file inline - handy for sharing common setup (connecting, logon, common aliases) across several test scripts instead of duplicating it in each one.
Prefix it with not, e.g. not isset VARS.Foo or if not assert 1 == 2. It succeeds whenever the wrapped command would have failed, and vice versa.
Use seq in <SeqNum> / seq out <SeqNum> (or the equivalent options via reset). Moving the outbound sequence forward is safe; forcing it backward, or forcing the inbound sequence to an arbitrary value, is intentionally permitted for chaos-testing scenarios but will likely desync you from a real counterparty - expect a disconnect or rejected messages afterward if you do this against anything other than a test harness.
Scripts are deterministic: wait/expect block until a matching message arrives or the configured timeout elapses, and assert fails immediately if its condition is false. Any failure exits the script (and, in mxshell -f, exits the process with a non-zero status).
Think of wait as a barrier: it blocks until either the timeout fires or a matching message shows up anywhere in the incoming stream. expect is stricter - it requires the very next message to match, and fails immediately if anything else arrives first. When in doubt, prefer wait; expect is easy to trip up with an unrelated heartbeat or admin message landing in between.
Yes - both frontends invoke the exact same executor/session engine, so a script behaves identically whether run interactively in MXGUI's Script Runner or headlessly via mxshell -f.
See the Continuous Integration section in the main README - install mxshell via go install and run mxshell -f your_script.mxs; a failed wait/expect/assert fails the build automatically.
Toggle it from the Settings/About panel. The choice is saved to the browser's local storage, so it persists across restarts.
The Live Session Monitor streams real-time logs for an active connected session. The Toolbox works fully offline on pasted raw FIX text - useful for finalizing (BodyLength/CheckSum), validating, or decoding messages without a live connection.
Open the Message Inspector and select two messages (from the live log or Toolbox) to diff - differing tags are highlighted automatically.
Open the Dictionary page and search by tag number, field name, or message type - it reads directly from your configured SessionSpec/ApplicationSpec XML.
The log search bar has two modes: Filter hides everything that doesn't match your regex, while Jump keeps the full log visible and steps you through matches one at a time (with Enter/Shift+Enter or the arrow buttons), showing a running match count so you always know where you are.
I have a message open in the Inspector but closed the log panel - how do I get back to it in context?
Reopen the Inspector for that message and use the Locate button - it clears any active filters and scrolls the log back to that exact message, briefly highlighting it so it's easy to spot among its neighbors.
Yes - when saving an alias whose name already exists, the check will offer a Reload Payload option, letting you pull in the live message content instead of retyping the template from scratch.
Yes - the Form Builder (available from the send form toolbar) lets you add, remove, drag-reorder, and edit tag/value pairs individually, then apply the assembled result back into the message editor. It's useful when you want to construct a message without needing to remember exact tag numbers or delimiter syntax by hand.
Yes - the Script Runner shows a Stop Execution button whenever a script is running (or press Esc). This cancels the in-flight script the same way a failed wait/expect/assert would, without needing to close the whole session.
When enabled, the console interleaves your script's own print output with the underlying session's admin-level traffic (Logons, Heartbeats, Test Requests, Sequence Resets, etc.) as it happens - useful for understanding exactly what the engine did behind the scenes during a run, not just what your script explicitly printed. Turn it off for cleaner output when you only care about your script's own messages.
Yes. The protocol engine natively handles standard session-level behavior including Logon, Logout, Heartbeats, Test Requests, and Sequence Resets.
Yes. Messages such as 35=D|55=AAPL|54=1|38=100|40=2| can be pasted directly into MicroFIX. The engine can normalize delimiters, compute BodyLength, and calculate CheckSum on the fly.
No. Both use the exact same parser, validator, message model, session engine, and scripting engine. A FIX flow debugged interactively in MXGUI uses the exact same underlying protocol implementation that will execute it from MXShell in CI.
Verify IpAddr/Port and Sender/TargetCompID match what the counterparty expects, and confirm nothing else is bound to the port if you're listening (acceptor mode). Check the Live Session Monitor / logs output for rejected Logon messages, which usually indicate a CompID or sequence number mismatch.
Sometimes the rejection reason returned isn't detailed enough to diagnose from your side alone - if the mismatch isn't obvious from the logs, it's worth confirming directly with the counterparty what they expected to see.
If your message is structurally incomplete according to the dictionary, outbound validation will block it from sending. If you intentionally want to send an invalid message, check the send raw checkbox on MXGUI (or send -r in MXShell) to bypass the outbound dictionary checks.
Note that MicroFIX will never automatically inject missing fields into a message before sending (aside from standard tags like Length and Checksum during finalization). It will only substitute values for fields that are already present in the string. Attempting to intelligently inject fields risks corrupting the structural validity of repeating groups and trailers.
Strict validation rejects unknown fields and enforces field data types against your XML dictionary. Check the validation error for the offending tag, and confirm it's actually defined in your ApplicationSpec/SessionSpec XML for that message type. If your venue diverges from spec, set FixValidateStrict: false to fall back to Basic-level checks.
Use the regex search in the Live Stream log view, or the logs command in MXShell for CLI-based filtering/export.
Open an issue or pull request on the GitHub repo.
{ "SenderCompID": "SENDER", "TargetCompID": "TARGET", "HeartbeatInt": 30, "SessionSpec": "FIX44", // or a path to a custom XML dictionary "ApplicationSpec": "FIX44", "FixValidateStrict": true, "IpAddr": "0.0.0.0", "Port": 1234, "Alias": { "MyOrder": "35=D|55=AAPL|54=1|38=100|40=2|" } }