TraderMade Docs
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.
- Set up a Trial : Start a FIX Trial ↗
- Copy code from the below examples : Copy Code.
- Receive an email with connection details : Get your connection details ↗
- 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
AuthenticationAvailable 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 IDSubscriptionRequestType (263)
–1=Subscribe
,2=Unsubscribe
MarketDepth (264)
– depth (typically 1)NoMDEntryTypes (267)
withMDEntryType (269)
–0=Bid
,1=Offer
-
NoRelatedSym (146)
withSymbol (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
andResetSeqNumFlag=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.