QuickFIX Performance Tips: Low-Latency FIX Engines

QuickFIX Cookbook: Practical Examples and Snippets

QuickFIX is a popular open-source FIX (Financial Information eXchange) engine used to build trading systems, market data feeds, and other financial messaging applications. This cookbook gives concise, practical examples and snippets for common tasks with QuickFIX (C++/Java/.NET), focusing on patterns you can copy-paste and adapt.

1. Quick start: minimal acceptor (C++)

This shows the smallest acceptor server that accepts incoming FIX sessions and logs messages.

cpp

// MinimalAcceptor.cpp #include “Application.h” #include “SessionSettings.h” #include “SocketAcceptor.h” #include “FileStore.h” #include “ScreenLog.h” class App : public FIX::Application { public: void onCreate(const FIX::SessionID&) override {} void onLogon(const FIX::SessionID& s) override { std::cout << “Logon: “ << s.toString() << ” “; } void onLogout(const FIX::SessionID& s) override { std::cout << “Logout: “ << s.toString() << ” “; } void toAdmin(FIX::Message&, const FIX::SessionID&) override {} void fromAdmin(const FIX::Message&, const FIX::SessionID&) override {} void toApp(FIX::Message&, const FIX::SessionID&) override {} void fromApp(const FIX::Message& m, const FIX::SessionID& s) override { std::cout << “Received: “ << m.toString() << ” “; } }; int main() { FIX::SessionSettings settings(“acceptor.cfg”); App application; FIX::FileStoreFactory storeFactory(settings); FIX::ScreenLogFactory logFactory(settings); FIX::SocketAcceptor acceptor(application, storeFactory, settings, logFactory); acceptor.start(); std::cout << “Acceptor started. Press Enter to quit. “; std::cin.get(); acceptor.stop(); return 0; }

Example acceptor.cfg (FIX 4.2):

Code

[DEFAULT] ConnectionType=acceptor HeartBtInt=30 FileLogPath=log FileStorePath=store

[SESSION] BeginString=FIX.4.2 SenderCompID=SERVER TargetCompID=CLIENT SocketAcceptPort=5001

2. Minimal initiator (Java)

Initiators connect to counterparties. This example shows setup and sending a simple NewOrderSingle after logon.

java

// MinimalInitiator.java import quickfix.; import quickfix.field.; import quickfix.fix44.NewOrderSingle; public class MinimalInitiator implements Application { private SessionID sessionID; public void onCreate(SessionID sessionId) { this.sessionID = sessionId; } public void onLogon(SessionID sessionId) { System.out.println(“Logon: “ + sessionId); // Send a NewOrderSingle NewOrderSingle order = new NewOrderSingle( new ClOrdID(“12345”), new Side(Side.BUY), new TransactTime(new java.util.Date()), new OrdType(OrdType.MARKET) ); order.set(new Symbol(“AAPL”)); try { Session.sendToTarget(order, sessionId); } catch (Exception e) { e.printStackTrace(); } } public void onLogout(SessionID sessionId) { System.out.println(“Logout”); } public void toAdmin(Message message, SessionID sessionId) {} public void fromAdmin(Message message, SessionID sessionId) {} public void toApp(Message message, SessionID sessionId) throws DoNotSend {} public void fromApp(Message message, SessionID sessionId) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue { System.out.println(“Received: “ + message); } public static void main(String[] args) throws Exception { SessionSettings settings = new SessionSettings(“initiator.cfg”); Application application = new MinimalInitiator(); MessageStoreFactory storeFactory = new FileStoreFactory(settings); LogFactory logFactory = new ScreenLogFactory(true, true, true); MessageFactory messageFactory = new DefaultMessageFactory(); Initiator initiator = new SocketInitiator(application, storeFactory, settings, logFactory, messageFactory); initiator.start(); } }

initiator.cfg (FIX 4.4):

Code

[DEFAULT] ConnectionType=initiator HeartBtInt=30 ReconnectInterval=5 FileLogPath=log FileStorePath=store

[SESSION] BeginString=FIX.4.4 SenderCompID=CLIENT TargetCompID=SERVER SocketConnectHost=127.0.0.1 SocketConnectPort=5001

3. Parsing and responding to market data (Python with QuickFIX)

Example handler for MarketDataIncrementalRefresh and sending MarketDataSnapshotFullRefresh.

python

# market_data_app.py import quickfix as fix from quickfix import Message, SessionID class MarketDataApp(fix.Application): def onCreate(self, sessionID): pass def onLogon(self, sessionID): print(“Logon”, sessionID) def onLogout(self, sessionID): print(“Logout”, sessionID) def toAdmin(self, message, sessionID): pass def fromAdmin(self, message, sessionID): pass def toApp(self, message, sessionID): pass def fromApp(self, message, sessionID): msgType = fix.MsgType() message.getHeader().getField(msgType) if msgType.getValue() == fix.MsgType_MarketDataIncrementalRefresh: print(“Incremental MD:”, message) # Build a snapshot in response snapshot = fix.Message() snapshot.getHeader().setField(fix.MsgType(fix.MsgType_MarketDataSnapshotFullRefresh)) snapshot.setField(fix.Symbol(“EUR/USD”)) snapshot.setField(fix.MDReqID(“snap-1”)) # add repeating group MDEntries as needed… fix.Session.sendToTarget(snapshot, sessionID) if name == main: settings = fix.SessionSettings(“market.cfg”) app = MarketDataApp() storeFactory = fix.FileStoreFactory(settings) logFactory = fix.ScreenLogFactory(settings) initiator = fix.SocketInitiator(app, storeFactory, settings, logFactory) initiator.start()

4. Common snippets

  • Validate required fields before sending (Java):

java

if (!order.isSetField(quickfix.field.Symbol.FIELD)) throw new IllegalArgumentException(“Symbol required”);
  • Send to a specific session (C++):

cpp

FIX::SessionID sid(“FIX.4.2”,“SENDER”,“TARGET”); FIX::Message order = ...; FIX::Session::sendToTarget(order, sid);
  • Persist custom state in FileStore using a custom key: Implement Application::onCreate to store session-specific metadata in the FileStore directory alongside usual QuickFIX files.

  • Handle resend requests (C++): Implement fromApp/fromAdmin to properly interpret PossDupFlag and OrigSendingTime; pass application-level sequence repair messages using SequenceReset (GapFill) when appropriate.

5. Performance tips (concise)

  • Use in-memory stores for low-latency (MemoryStoreFactory) but persist for production.
  • Batch small writes; avoid sync fs calls for every message.
  • Use non-blocking I/O builds (where supported) and tune TCP keepalive/reconnect.
  • Minimize logging in hot code paths.

6. Troubleshooting snippets

  • Reset sequence numbers (quickfix-cli or config):

    • Manually delete store files in FileStorePath for the session and restart.
    • Or set ResetOnLogon=Y in session settings (use with caution).
  • Debugging raw messages:

bash

# Enable raw message logging in log config (example for ScreenLogFactory) # or set LogHeartbeats=N and inspect message.toString()

7. Useful fields & groups cheat-sheet

  • NewOrderSingle: ClOrdID(11), Side(54), TransactTime(60), OrderQty(38) / Price(44), Symbol(55)
  • ExecutionReport: ExecType(150), OrdStatus(39), ExecID(17), LastQty(32), LastPx(31)
  • MarketData: MDReqID(262), MDEntryType(269), MDEntryPx(270), MDEntrySize(271)

8. Example: transform FIX 4.2 -> 4.4 message (pseudo)

  • Map header BeginString, translate or drop deprecated fields, update required fields like ApplVerID if using FIXT.
  • Example: replace SecurityIDSource with new IDSource semantics and ensure MsgType mappings.

9. Where to extend

  • Implement custom MessageCracker (Java) or use Message::getHeader in C++ to dispatch by MsgType.
  • Add protocol-level monitoring hooks in toAdmin/fromAdmin to track logons and heartbeats.
  • Wrap QuickFIX sessions behind an internal API to centralize retries, timeouts, and business validation.

10. Quick debugging checklist

  1. Check session settings (CompIDs, ports, BeginString).
  2. Verify network connectivity and ports.
  3. Inspect store files for seq numbers.
  4. Enable screen/file logs and reproduce the issue.
  5. Use FIX message validators (lightweight) to check required fields/tags.

For copy-paste usage: adapt the provided snippets to your project’s language binding and build system. If you tell me which language and FIX version you’re using (assumed: Java/C++/Python and FIX 4.2–4.4), I can produce a focused example with full build/run steps.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *