Getting Started

We provide clean forex and CFD data via easy-to-use delivery methods. We use TCP Socket and FIX protocol, making it easier to receive data. Authentication is over a secure FIX protocol, and responses are in string format with numerical tags.

Following are a few simple steps to start receiving data. We will set up your trial connection in a few hours.

  1. Set up a Trial : Start a FIX Trial ↗
  2. Copy code from the below examples : Copy Code.
  3. Receive an email with connection details : Get your connection details ↗
  4. Start receiving data : Connect to API

Authentication

You need to include your API key with the initial request when you start your connection. You can find your api key under your account once you log in.

Our streaming data API documentation is extensive and you can see below all the available routes, currencies and CFDs available to help you integrate our data within your applications. If you are new to WebSockets and SocketIO visit our tutorial page that explains sockets in greater detail.

In this section

Authentication

Available Currencies for streaming data via FIX API

We support over 60+ currency pairs via FIX. You can access a list of currencies available by visiting our FIX currencies list page.

We also provide data for CFDs. See the full CFD list page. Also check the opening and closing times for the CFDs Opening and Closing Times PDF. Also, you can see below the list of all the CFDs we provide through our WebSockets (USD at the end of the CFD code does not imply that data is in dollar value. The below codes only applicable for SocketIO. For Websocket simply take USD off at the end. For example UK100 to get CFDs off Websocket /feedadv endpoint):

Commodities: COPPERUSD (Copper), OILUSD (US OIL), UKOILUSD (UK OIL), NATGASUSD (Natural Gas)

Indices: UK100USD (FTSE), SPX500USD, FRA40USD (CAC), GER30USD (DAX), JPN225USD (Nikkei), NAS100USD (Nasdaq), USA30USD (Dow), HKG33USD (Hang Seng), AUS200USD

Equities: AAPLUSD (Apple), FBUSD (Facebook), AMZNUSD (Amazon), NFLXUSD (Netflix), TSLAUSD (Tesla), GOOGLUSD (Alphabet), BABAUSD (Alibaba), TWTRUSD (Twitter), BACUSD (Bank of America), BIDUUSD (Baidu)

TraderMade FIX 4.4 Market Data Integration Guide

This document provides an overview of the key FIX 4.4 message types supported by the TraderMade market data server. It is intended for client developers who need to integrate their FIX initiator with the TraderMade FIX acceptor.

⚠️ Note: Only market data is supported. No order placement or trading is supported.


Standard Header (all messages)

Tag Field Req Description
8 BeginString Y Protocol version (always FIX.4.4)
9 BodyLength Y Length of message body
35 MsgType Y Identifies the message type
49 SenderCompID Y Assigned by TraderMade per client
56 TargetCompID Y Always TRADERMADE_M1 for this server
34 MsgSeqNum Y Sequence number, increasing per session
52 SendingTime Y UTC timestamp of sending

Standard Trailer

Tag Field Req Description
10 CheckSum Y 3-digit checksum

Session (Admin) Messages

  • Logon (35=A)
    Client initiates session. Includes:
  • HeartBtInt (108)
  • Username (553)
  • Password (554)

  • Heartbeat (35=0)
    Sent at fixed interval (30s default).

  • Test Request (35=1)
    Forces a heartbeat to verify connectivity.

  • Logout (35=5)
    Ends session gracefully.

  • Resend Request (35=2)
    Requests missing messages.

  • Reject (35=3) / Business Reject (35=j)
    Indicates error or unsupported message type.

  • Sequence Reset (35=4)
    Adjusts message sequence.


Market Data Messages

  • Market Data Request (35=V)
    Used to subscribe/unsubscribe from symbols. Key fields:
  • MDReqID (262) – unique request ID
  • SubscriptionRequestType (263)1=Subscribe, 2=Unsubscribe
  • MarketDepth (264) – depth (typically 1)
  • NoMDEntryTypes (267) with MDEntryType (269)0=Bid, 1=Offer
  • NoRelatedSym (146) with Symbol (55)

  • Market Data Request Reject (35=Y)
    Returned if request cannot be fulfilled. Includes:

  • Reason code (281)
  • Optional Text (58)

  • Market Data Snapshot Full Refresh (35=W)
    Server delivers snapshot per symbol. Fields:

  • Symbol (55)
  • MDReqID (262)
  • NoMDEntries (268)
  • Repeated group with:
    • MDEntryType (269)
    • MDEntryPx (270)
    • MDEntrySize (271)

Example Encodings

Logon

8=FIX.4.4|35=A|34=1|49=CLIENT123|56=TRADERMADE_M1|98=0|108=30|553=<Username>|554=<Password>|10=063|

Market Data Request (subscribe GBPUSD)

8=FIX.4.4|35=V|49=CLIENT123|56=TRADERMADE_M1|34=2|52=20251002-09:30:00|262=REQ1|263=1|264=1|267=2|269=0|269=1|146=1|55=GBPUSD|10=210|

Market Data Request Reject

8=FIX.4.4|35=Y|49=TRADERMADE_M1|56=CLIENT123|34=3|52=20251002-09:30:01|262=REQ1|281=0|58=Unknown symbol|10=082|

Market Data Snapshot Full Refresh

8=FIX.4.4|35=W|49=TRADERMADE_M1|56=CLIENT123|34=4|52=20251002-09:30:01|55=GBPUSD|262=REQ1|268=2|269=0|270=1.2995|271=1000000|269=1|270=1.2997|271=1000000|10=128|

Notes

  • Only market data messages are supported by this server (no order placement).
  • Clients must manage sequence numbers and heartbeats as per FIX 4.4.
  • Use ResetOnLogon=Y and ResetSeqNumFlag=Y to simplify reconnects.
  • Subscription limits and symbol availability are enforced server-side; rejected requests will return 35=Y.

Examples using QuickFIX

C++ Example

You can download all source code from GitHub C++ FIX Demo Client

#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <string>
#include <vector>

#include <quickfix/Application.h>
#include <quickfix/MessageCracker.h>
#include <quickfix/SocketInitiator.h>
#include <quickfix/Session.h>
#include <quickfix/SessionSettings.h>
#include <quickfix/FileStore.h>
#include <quickfix/FileLog.h>

#include <quickfix/fix44/MarketDataRequest.h>
#include <quickfix/fix44/MarketDataRequestReject.h>
#include <quickfix/fix44/MarketDataSnapshotFullRefresh.h>

static std::vector<std::string> splitCSV(const std::string& s) {
  std::vector<std::string> out; std::string cur;
  for (char c : s) { if (c==',') { if(!cur.empty()) out.push_back(cur); cur.clear(); } else cur.push_back(c); }
  if (!cur.empty()) out.push_back(cur);
  return out;
}

class App : public FIX::Application, public FIX::MessageCracker {
 public:
  explicit App(const FIX::SessionSettings& settings) : settings_(settings) {}

  void onCreate(const FIX::SessionID& sid) override { sid_ = sid; }
  void onLogon(const FIX::SessionID& sid) override { std::cout << "Logon: " << sid << "
"; sendSubscribe(); }
  void onLogout(const FIX::SessionID& sid) override { std::cout << "Logout: " << sid << "
"; }

  void toAdmin(FIX::Message& msg, const FIX::SessionID&) noexcept override {
    FIX::MsgType mt; msg.getHeader().getField(mt);
    if (mt == FIX::MsgType_Logon) {
      const auto& d = settings_.get(sid_);
      if (d.has("Username")) msg.setField(FIX::Username(d.getString("Username")));
      if (d.has("Password")) msg.setField(FIX::Password(d.getString("Password")));
    }
  }

  void fromAdmin(const FIX::Message&, const FIX::SessionID&)
    throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon) override {}

  void toApp(FIX::Message&, const FIX::SessionID&) throw(FIX::DoNotSend) override {}

  void fromApp(const FIX::Message& msg, const FIX::SessionID& sid)
    throw(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType) override {
    crack(msg, sid);
  }

  // --- Application messages ---
  void onMessage(const FIX44::MarketDataRequestReject& rej, const FIX::SessionID&) override {
    std::string id = rej.isSetField(262) ? rej.getField(262) : "";
    std::string rs = rej.isSetField(281) ? rej.getField(281) : "";
    std::string tx = rej.isSetField(58)  ? rej.getField(58)  : "";
    std::cout << "MD Reject (35=Y) MDReqID=" << id << " reason(281)=" << rs << " text=" << tx << "
";
  }

  void onMessage(const FIX44::MarketDataSnapshotFullRefresh& snap, const FIX::SessionID&) override {
    std::string sym = snap.getField(FIX::FIELD::Symbol);
    FIX::NoMDEntries n; snap.get(n);
    std::cout << "W: " << sym << " entries=" << n.getValue() << " :: ";
    for (int i=1; i<=n.getValue(); ++i) {
      FIX44::MarketDataSnapshotFullRefresh::NoMDEntries g; snap.getGroup(i, g);
      char t = g.getField(FIX::FIELD::MDEntryType)[0];
      double px = std::stod(g.getField(FIX::FIELD::MDEntryPx));
      std::cout << (t=='0'?"BID":"ASK") << '=' << px << (i<n.getValue()?" | ":"");
    }
    std::cout << '
';
    firstData_.store(true); cv_.notify_all();
  }

  // --- Helpers ---
  void sendSubscribe() {
    const auto& d = settings_.get(sid_);
    FIX44::MarketDataRequest req(FIX::MDReqID("REQ-1"), FIX::SubscriptionRequestType('1'), FIX::MarketDepth(1));
    { FIX44::MarketDataRequest::NoMDEntryTypes e; e.set(FIX::MDEntryType('0')); req.addGroup(e); }
    { FIX44::MarketDataRequest::NoMDEntryTypes e; e.set(FIX::MDEntryType('1')); req.addGroup(e); }
    std::string raw = d.has("Symbols") ? d.getString("Symbols") : "GBPUSD";
    for (auto& s : splitCSV(raw)) { FIX44::MarketDataRequest::NoRelatedSym g; g.set(FIX::Symbol(s)); req.addGroup(g); }
    FIX::Session::sendToTarget(req, sid_);
  }

  void sendUnsubscribeAll() {
    const auto& d = settings_.get(sid_);
    FIX44::MarketDataRequest u(FIX::MDReqID("REQ-1"), FIX::SubscriptionRequestType('2'), FIX::MarketDepth(0));
    std::string raw = d.has("Symbols") ? d.getString("Symbols") : "GBPUSD";
    for (auto& s : splitCSV(raw)) { FIX44::MarketDataRequest::NoRelatedSym g; g.set(FIX::Symbol(s)); u.addGroup(g); }
    FIX::Session::sendToTarget(u, sid_);
  }

  bool waitFirstDataMs(int ms) {
    std::unique_lock<std::mutex> lk(m_);
    return cv_.wait_for(lk, std::chrono::milliseconds(ms), [&]{ return firstData_.load(); });
  }

 private:
  FIX::SessionID sid_;
  const FIX::SessionSettings& settings_;
  std::atomic<bool> firstData_{false};
  std::condition_variable cv_; std::mutex m_;
};

int main(int argc, char** argv) {
  if (argc < 2) { std::cerr << "usage: " << argv[0] << " client.cfg
"; return 2; }
  try {
    FIX::SessionSettings settings(argv[1]);
    App app(settings);
    FIX::FileStoreFactory store(settings);
    FIX::FileLogFactory log(settings);
    FIX::SocketInitiator initiator(app, store, settings, log);

    initiator.start();
    app.waitFirstDataMs(60000); // optional health check

    std::cout << "Running… press Enter to unsubscribe and logout." << std::endl;
    std::cin.get();

    app.sendUnsubscribeAll();
    auto sids = settings.getSessions();
    if (!sids.empty()) if (auto* s = FIX::Session::lookupSession(*sids.begin())) s->logout("Client exit");
    std::this_thread::sleep_for(std::chrono::milliseconds(300));

    initiator.stop();
    return 0;
  } catch (const std::exception& e) {
    std::cerr << "fatal: " << e.what() << "
"; return 1;
  }
}

Below is the Client Configuration code. You will need to insert your SocketConnectionHost, SocketConnectionPort, SenderCompID, TargetCompID, Username, and Password into this. It is possible to set RefreshOnLogin if required. You get the config file from your dashboard when you register for a trial.

Example client.cfg (placeholders only):
[DEFAULT]
ApplicationID=FixClientApp
ConnectionType=initiator
ReconnectInterval=5
FileStorePath=store/TMS_Target_xxxx/
FileLogPath=log/
StartTime=00:00:00
EndTime=23:59:59
HeartBtInt=30
UseDataDictionary=Y
DataDictionary=./spec/FIX44.xml
Username=<YourUsername>
Password=<YourPassword>
Symbols=GBPUSD

[SESSION]
ResetOnLogon=Y
BeginString=FIX.4.4
SocketConnectPort=5000
SocketConnectHost=marketdata.tradermade.com
SenderCompID=<Your SenderCompID>
TargetCompID=TRADERMADE_M1

[LOGGING]
LogFilePath=/var/log/fix-client.log
LogLevel=INFO

Python Example

This example implementation is written in Python and uses the QuickFIX libraries we will install these using the command

pip install quickfix

You can download all source code from GitHub Python FIX Demo Client

import quickfix as fix
import quickfix44 as fix44
import sys
import threading
import time

class MarketDataApp(fix.Application, fix.MessageCracker):
    """
    FIX Application class implementing the client logic for subscribing 
    to and handling market data snapshots (FIX 4.4 MarketDataSnapshotFullRefresh).
    """

    def __init__(self, settings):
        self.settings = settings
        self.session_id = None
        # Use threading.Event to signal when the first market data message is received.
        self.first_data_event = threading.Event()
        print("QuickFIX Python Market Data Client initialized.")

    # --- Standard FIX Application Callbacks ---
    def onCreate(self, sessionID):
        """Called when a new session is created."""
        pass

    def onLogon(self, sessionID):
        """Called when a session successfully logs on."""
        self.session_id = sessionID
        print(f"Logon: {sessionID}")
        self.sendSubscribe()

    def onLogout(self, sessionID):
        """Called when a session logs out."""
        print(f"Logout: {sessionID}")

    def toAdmin(self, message, sessionID):
        """
        Allows modification of admin messages (like Logon).
        Adds Username/Password fields if present in the configuration.
        """
        msg_type = fix.MsgType()
        message.getHeader().getField(msg_type)
        if msg_type.getValue() == fix.MsgType_Logon:
            try:
                # Retrieve session-specific settings
                session_dict = self.settings.get(sessionID)

                if session_dict.has('Username'):
                    message.setField(fix.Username(session_dict.getString('Username')))
                if session_dict.has('Password'):
                    message.setField(fix.Password(session_dict.getString('Password')))
            except fix.ConfigError as e:
                # Handle case where sessionID is not found in settings
                print(f"Error accessing settings for {sessionID}: {e}")
            except fix.FieldNotFound:
                # Field not found is expected if Username/Password isn't set
                pass

    def fromAdmin(self, message, sessionID):
        """Called when an admin message is received from the counterparty."""
        pass

    def toApp(self, message, sessionID):
        """Called before an application message is sent to the counterparty."""
        pass

    def fromApp(self, message, sessionID):
        """
        Entry point for application-level messages. Cracks the message 
        to call the specific onMessage handler.
        """
        self.crack(message, sessionID)

    # --- FIX MessageCracker Handlers ---

    def onMessage(self, message, sessionID):
        """Handles FIX44::MarketDataSnapshotFullRefresh (35=W)"""
        try:
            # Cast message to the specific type for easy field access
            snap = fix44.MarketDataSnapshotFullRefresh(message)

            sym = snap.get(fix.Symbol()).getValue()
            n = snap.get(fix.NoMDEntries()).getValue()

            output = [f"W: {sym} entries={n} :: "]

            # Iterate through the repeating group (NoMDEntries)
            for i in range(1, n + 1):
                group = fix44.MarketDataSnapshotFullRefresh.NoMDEntries()
                snap.getGroup(i, group)

                t = group.get(fix.MDEntryType()).getValue()
                px = group.get(fix.MDEntryPx()).getValue()

                type_str = "BID" if t == '0' else "ASK"
                output.append(f"{type_str}={px}")

            print(' | '.join(output))

            # Signal that market data has been received
            self.first_data_event.set()

        except fix.FieldNotFound as e:
            print(f"Error processing MD Snapshot (Field Not Found): {e}")
        except Exception as e:
            print(f"An unexpected error occurred while processing MD Snapshot: {e}")

    def onMessage(self, message, sessionID):
        """Handles FIX44::MarketDataRequestReject (35=Y)"""
        rej = fix44.MarketDataRequestReject(message)

        md_req_id = rej.isSetField(fix.MDReqID()) and rej.get(fix.MDReqID()).getValue() or ""
        rej_reason = rej.isSetField(fix.MDReqReqID()) and rej.get(fix.MDReqReqID()).getValue() or ""
        text = rej.isSetField(fix.Text()) and rej.get(fix.Text()).getValue() or ""

        print(f"MD Reject (35=Y) MDReqID={md_req_id} reason(281)={rej_reason} text={text}")

    # --- Helpers ---

    def _get_symbols_from_settings(self):
        """Retrieves and splits the Symbols string from settings."""
        if self.session_id is None:
            return []

        try:
            session_dict = self.settings.get(self.session_id)
            symbols_raw = session_dict.has("Symbols") and session_dict.getString("Symbols") or "GBPUSD"
            return [s.strip() for s in symbols_raw.split(',') if s.strip()]
        except (fix.ConfigError, fix.RuntimeError) as e:
            print(f"Error retrieving Symbols from settings: {e}")
            return ["GBPUSD"]

    def sendSubscribe(self):
        """Sends a FIX 4.4 Market Data Request (35=V) to subscribe to level 1 data."""
        if not self.session_id: return

        # 1. Create the request message (MDReqID, SubReqType='1', MarketDepth=1)
        req = fix44.MarketDataRequest(
            fix.MDReqID("REQ-1"),
            fix.SubscriptionRequestType('1'), # '1' = Snapshot + Updates
            fix.MarketDepth(1) # Level 1 data (top of book)
        )

        # 2. Add MDEntryType Groups (Bid '0' and Offer '1')
        entries = fix44.MarketDataRequest.NoMDEntryTypes()
        entries.set(fix.MDEntryType('0')) # Bid
        req.addGroup(entries)
        entries.set(fix.MDEntryType('1')) # Offer
        req.addGroup(entries)

        # 3. Add Symbol Groups (NoRelatedSym)
        for symbol in self._get_symbols_from_settings():
            sym_group = fix44.MarketDataRequest.NoRelatedSym()
            sym_group.set(fix.Symbol(symbol))
            req.addGroup(sym_group)

        try:
            fix.Session.sendToTarget(req, self.session_id)
            print(f"Sent MD Subscription for: {', '.join(self._get_symbols_from_settings())}")
        except fix.SessionNotFound as e:
            print(f"Error: Session not found when attempting to send subscription: {e}")

    def sendUnsubscribeAll(self):
        """Sends a FIX 4.4 Market Data Request (35=V) to unsubscribe (SubReqType='2')."""
        if not self.session_id: return

        # 1. Create the request message (MDReqID, SubReqType='2', MarketDepth=0)
        unsub = fix44.MarketDataRequest(
            fix.MDReqID("REQ-1"),
            fix.SubscriptionRequestType('2'), # '2' = Unsubscribe
            fix.MarketDepth(0)
        )

        # 2. Add Symbol Groups (NoRelatedSym)
        for symbol in self._get_symbols_from_settings():
            sym_group = fix44.MarketDataRequest.NoRelatedSym()
            sym_group.set(fix.Symbol(symbol))
            unsub.addGroup(sym_group)

        try:
            fix.Session.sendToTarget(unsub, self.session_id)
            print(f"Sent MD Unsubscribe for: {', '.join(self._get_symbols_from_settings())}")
        except fix.SessionNotFound as e:
            print(f"Error: Session not found when attempting to send unsubscribe: {e}")

    def wait_for_first_data(self, timeout_ms):
        """
        Blocking call that waits for the first market data message to be received.
        Returns True if data was received, False otherwise.
        """
        timeout_seconds = timeout_ms / 1000.0
        print(f"Waiting up to {timeout_seconds} seconds for initial market data...")
        return self.first_data_event.wait(timeout_seconds)

def main():
    if len(sys.argv) < 2:
        print("usage: python market_data_client.py client.cfg")
        sys.exit(2)

    try:
        # 1. Load configuration and setup application components
        settings = fix.SessionSettings(sys.argv[1])
        application = MarketDataApp(settings)
        store_factory = fix.FileStoreFactory(settings)
        log_factory = fix.FileLogFactory(settings)

        # 2. Initialize the SocketInitiator
        # Note: Threading is managed internally by the QuickFIX library
        initiator = fix.SocketInitiator(application, store_factory, settings, log_factory)

        # 3. Start the initiator (connects and attempts logon)
        initiator.start()

        # 4. Optional Health Check: Wait for initial data
        if not application.wait_for_first_data(60000):
            print("Warning: Timed out waiting for initial market data.")

        print("Running... press Enter to unsubscribe and logout.")
        sys.stdin.readline()

        # 5. Cleanup: Unsubscribe and Logout
        application.sendUnsubscribeAll()
        time.sleep(0.3) # Give time for unsubscribe message to be processed

        # Logout the session
        sids = settings.getSessions()
        if sids:
            session = fix.Session.lookupSession(sids[0])
            if session:
                session.logout("Client exit")
                print("Sent Logout message.")

        initiator.stop()

    except fix.ConfigError as e:
        print(f"Configuration Error: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"Fatal error: {e}")
        sys.exit(1)

if __name__ == '__main__':
    main()

Below is the Client Configuration code. You will need to insert your SocketConnectionHost, SocketConnectionPort, SenderCompID, TargetCompID, Username, and Password into this. It is possible to set RefreshOnLogin if required. You get this information when you register for a trial.

# This is a client (initiator)

[DEFAULT]
FileStorePath=./session/
ConnectionType=initiator
StartTime=00:01:00
EndTime=23:59:00
HeartBtInt=30
UseDataDictionary=Y
DataDictionary=FIX44.xml
ValidateUserDefinedFields=N
ValidateIncomingMessage=Y
RefreshOnLogon=Y
FileLogPath=./Logs
SSL_PROTOCOL = all
#ClientCertificateFile=keystore/tradermade.pem


[SESSION]
BeginString=FIX.4.4
SocketConnectHost=fix-marketdata.tradermade.com
SocketConnectPort=9883
SenderCompID=SENDER_COMP_ID
TargetCompID=TARGET_COMP_ID

[LOGGING]

ScreenLogEvents=N
ScreenLogShowIncoming=N
ScreenLogShowOutgoing=N
ScreenLogShowHeartBeats=N

[ACCOUNT]
Username=USERNAME
Password=PASSWORD

Add the following code into your section in our pom.xml. This step will download the libraries from the Maven repository.

Once you have this project set up you should be able to run "python client.py" and you should see the financial data.


Java Example

This example implementation is written in Java and uses the QuickFIX/J libraries. For this example, we will acquire them from the Maven repository. But they can also be downloaded from www.quickfixj.org or cloned from GitHub.

You can download all source code from GitHub Java FIX Demo Client

This tutorial will assume that you have Java programming knowledge and experience setting up a Maven project.

This tutorial assumes that you have contacted TraderMade support and have the login credentials required to connect to the service.

Below is the ClientApplication Code. This example is a simple class that will make a connection to the TraderMade FIX server and returns data for requested instruments.

// File: MdClient.java
// Compile with: javac -cp quickfixj-core-2.x.jar:slf4j-api.jar MdClient.java
// Run with: java -cp .:quickfixj-core-2.x.jar:slf4j-api.jar:slf4j-simple.jar MdClient client.cfg

import quickfix.*;
import quickfix.field.*;
import quickfix.fix44.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;

public class MdClient extends MessageCracker implements Application {
    private final SessionSettings settings;
    private SessionID sid;
    private final CountDownLatch firstData = new CountDownLatch(1);

    public MdClient(SessionSettings settings) {
        this.settings = settings;
    }

    // ---- Lifecycle ----
    @Override public void onCreate(SessionID sessionID) { this.sid = sessionID; }

    @Override public void onLogon(SessionID sessionID) {
        System.out.println("Logon: " + sessionID);
        try { sendSubscribe(); } catch (Exception e) { System.out.println("subscribe error: " + e.getMessage()); }
    }

    @Override public void onLogout(SessionID sessionID) { System.out.println("Logout: " + sessionID); }

    // ---- Admin / App ----
    @Override public void toAdmin(Message message, SessionID sessionID) {
        try {
            String msgType = message.getHeader().getString(MsgType.FIELD);
            if (MsgType.LOGON.equals(msgType)) {
                Dictionary d = settings.get(sessionID);
                if (d.isSetField("Username")) message.setField(new Username(d.getString("Username")));
                if (d.isSetField("Password")) message.setField(new Password(d.getString("Password")));
            }
        } catch (Exception ignore) { }
    }

    @Override public void fromAdmin(Message message, SessionID sessionID) throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon { }

    @Override public void toApp(Message message, SessionID sessionID) throws DoNotSend { }

    @Override public void fromApp(Message message, SessionID sessionID)
            throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType {
        crack(message, sessionID);
    }

    // ---- Typed handlers ----
    public void onMessage(MarketDataRequestReject rej, SessionID sessionID) throws FieldNotFound {
        String id = rej.isSetField(MDReqID.FIELD) ? rej.getString(MDReqID.FIELD) : "";
        String rs = rej.isSetField(MDReqRejReason.FIELD) ? rej.getString(MDReqRejReason.FIELD) : "";
        String tx = rej.isSetField(Text.FIELD) ? rej.getString(Text.FIELD) : "";
        System.out.println("MD Reject (35=Y) MDReqID=" + id + " reason(281)=" + rs + " text=" + tx);
    }

    public void onMessage(MarketDataSnapshotFullRefresh snap, SessionID sessionID) throws FieldNotFound {
        String sym = snap.getString(Symbol.FIELD);
        int n = snap.isSetField(NoMDEntries.FIELD) ? snap.getInt(NoMDEntries.FIELD) : 0;

        List<String> parts = new ArrayList<>();
        for (int i = 1; i <= n; i++) {
            MarketDataSnapshotFullRefresh.NoMDEntries g = new MarketDataSnapshotFullRefresh.NoMDEntries();
            try {
                snap.getGroup(i, g);
                char t = g.getChar(MDEntryType.FIELD);
                String side = (t == MDEntryType.BID) ? "BID" : "ASK";
                double px = Double.parseDouble(g.getString(MDEntryPx.FIELD));
                parts.add(side + "=" + px);
            } catch (FieldNotFound ignore) {
                // skip entries missing required fields
            }
        }
        System.out.println("W: " + sym + " entries=" + n + " :: " + String.join(" | ", parts));
        firstData.countDown();
    }

    // ---- Helpers ----
    private static List<String> splitCSV(String s) {
        List<String> out = new ArrayList<>();
        for (String p : s.split(",")) {
            String t = p.trim();
            if (!t.isEmpty()) out.add(t);
        }
        return out;
    }

    private List<String> symbolsFromCfg() throws ConfigError {
        Dictionary d = settings.get(sid);
        String raw = d.isSetField("Symbols") ? d.getString("Symbols") : "GBPUSD";
        List<String> syms = splitCSV(raw);
        if (syms.isEmpty()) syms.add("GBPUSD");
        return syms;
    }

    private void sendSubscribe() throws SessionNotFound, ConfigError {
        MarketDataRequest req = new MarketDataRequest(
                new MDReqID("REQ-1"),
                new SubscriptionRequestType(SubscriptionRequestType.SNAPSHOT_PLUS_UPDATES),
                new MarketDepth(1)
        );
        // 269=0,1
        MarketDataRequest.NoMDEntryTypes e1 = new MarketDataRequest.NoMDEntryTypes();
        e1.setField(new MDEntryType(MDEntryType.BID));
        req.addGroup(e1);
        MarketDataRequest.NoMDEntryTypes e2 = new MarketDataRequest.NoMDEntryTypes();
        e2.setField(new MDEntryType(MDEntryType.OFFER));
        req.addGroup(e2);

        // Symbols 146/55
        for (String s : symbolsFromCfg()) {
            MarketDataRequest.NoRelatedSym g = new MarketDataRequest.NoRelatedSym();
            g.setField(new Symbol(s));
            req.addGroup(g);
        }
        Session.sendToTarget(req, sid);
    }

    private void sendUnsubscribeAll() throws SessionNotFound, ConfigError {
        MarketDataRequest u = new MarketDataRequest(
                new MDReqID("REQ-1"),
                new SubscriptionRequestType(SubscriptionRequestType.DISABLE_PREVIOUS_SNAPSHOT),
                new MarketDepth(0)
        );
        for (String s : symbolsFromCfg()) {
            MarketDataRequest.NoRelatedSym g = new MarketDataRequest.NoRelatedSym();
            g.setField(new Symbol(s));
            u.addGroup(g);
        }
        Session.sendToTarget(u, sid);
    }

    // Wait up to ms for first tick
    public boolean waitFirstDataMs(long ms) {
        try { return firstData.await(ms, java.util.concurrent.TimeUnit.MILLISECONDS); }
        catch (InterruptedException ie) { Thread.currentThread().interrupt(); return false; }
    }

    // ---- Main ----
    public static void main(String[] args) {
        if (args.length < 1) {
            System.err.println("usage: java MdClient client.cfg");
            System.exit(2);
        }
        String cfg = args[0];

        try {
            SessionSettings settings = new SessionSettings(cfg);
            Application app = new MdClient(settings);
            MessageStoreFactory store = new FileStoreFactory(settings);
            LogFactory log = new FileLogFactory(settings);
            Initiator initiator = new SocketInitiator(app, store, settings, log);

            initiator.start();

            // Optional health check
            if (app instanceof MdClient) ((MdClient) app).waitFirstDataMs(60000);

            System.out.println("Running… press Enter to unsubscribe and logout.");
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            br.readLine();

            // Unsubscribe and logout
            try { ((MdClient) app).sendUnsubscribeAll(); } catch (Exception e) { System.out.println("unsubscribe error: " + e.getMessage()); }

            // Graceful logout of first session
            @SuppressWarnings("unchecked")
            Iterator<SessionID> it = settings.getSessions().iterator();
            if (it.hasNext()) {
                SessionID sid = it.next();
                Session s = Session.lookupSession(sid);
                if (s != null) s.logout("Client exit");
            }

            Thread.sleep(300);
            initiator.stop();
            System.exit(0);
        } catch (Exception e) {
            System.err.println("fatal: " + e.getMessage());
            System.exit(1);
        }
    }
}

Below is the Client Configuration code. You will need to insert your SocketConnectionHost, SocketConnectionPort, SenderCompID, TargetCompID, Username, and Password into this. You can also set RefreshOnLogin, and you obtain this information.

[default]
ApplicationID=client2
FileStorePath=messagestore/messages/
ConnectionType=initiator
StartTime=00:01:00
EndTime=23:59:00
HeartBtInt=30
UseDataDictionary=Y
DataDictionary=FIX44.xml
ValidateUserDefinedFields=N
ValidateIncomingMessage=N
RefreshOnLogon=Y
ResetOnLogon=Y

[session]
BeginString=FIX.4.4
SocketConnectHost=SOCKET_CONNECTION_HOST
SocketConnectPort=SOCKET_CONNECTION_PORT
SenderCompID=SENDER_COMP_ID
TargetCompID=CLIENT_COMP_ID
​
[SSL]

#SocketUseSSL=Y
#SocketKeyStore=KEYSTORE_NAME
#SocketKeyStorePassword=KEYSTORE_PASSWORD

[Logging]
FileLogPath=log
ScreenLogEvents=N
ScreenLogShowIncoming=N
ScreenLogShowOutgoing=N
ScreenLogShowHeartBeats=N

[Account]
Username=USERNAME
Password=PASSWORD

Symbols=EURUSD
#Symbols=USDCAD USDCZK USDDKK USDHUF USDILS USDNOK USDPLN USDSGD USDTHB

Add the following code into your section in our pom.xml. This step will download the libraries from the Maven repository.

<dependency>
  <groupId>org.quickfixj</groupId>
  <artifactId>quickfixj-core</artifactId>
  <version>2.3.0</version> <!-- or latest -->
</dependency>

Once you have the code set up, you can run the program. You will need to pass the location of the 'config' file as a parameter, and you should see live price data in the console.