Connecting FXCM Over FIX – A Detailed Tutorial

48 min read

Connecting FXCM with Fix engine - A tutorial

By Sunith Reddy

We talked about the defacto standard for message communication in our previous article on FIX protocol.

“The Financial Information Exchange (FIX) Protocol is a message standard developed to facilitate the electronic exchange of information related to securities transactions. It is intended for use between trading partners wishing to automate communications.[1]

In this article, we are going to take a step further and talk about some operations involved in connecting FXCM over fix. We will be using the QuickFix engine to code the examples. Quickfix is an open source FIX engine.

“QuickFIX is an open source FIX engine. It integrates with financial applications giving them the connectivity they need to communicate with hundreds of systems around the world that support FIX. QuickFIX gives your applications the power to electronically interact with all these systems using a simple interface.”

You can learn more about QuickFIX from here.

Credentials

Just like we had credentials for connecting with Interactive Brokers using IBridgePY, we have credentials here as well. Connecting FCMX over fix will need some credentials which will identify the connecting entity for FXCM. The credentials consist of the following:

  • Socket connection host, port – where you would be connecting
  • Sender comp Id – identifies the sender of the message.
  • Target comp id – identifies the intended recipient of the message
  • Target sub id – a sub identifier which completes the recipient identification.

In case of quickFix, these credentials need to be configured in a configuration file. The configuration file would look as follows:

# Default settings. These settings are inherited by each
# individual session found below [DEFAULT]   BeginString=FIX.4.4 ConnectionType=initiator HeartBtInt=30 FileStorePath=.\Store FileLogPath=.\Logs  
# Start and End times for the FIX session (in UTC) StartDay=Sunday StartTime=21:15:00 EndDay=Friday EndTime=20:00:00   UseDataDictionary=Y DataDictionary=FIXFXCM10.xml ValidateUserDefinedFields=N ValidateFieldsHaveValues=N ValidateFieldsOutOfOrder=N   ReconnectInterval=20 ResetOnDisconnect=Y ResetSeqNumFlag=Y SendResetSeqNumFlag=Y ContinueInitializationOnError=Y  
# Session specific settings along with FIX credentials
# supplied by FXCM [SESSION]   SenderCompID=someID TargetCompID=FXCM SocketConnectHost=someHost SocketConnectPort=somePort TargetSubID=someTargetID Username=someUsername Password=somePassword

Logging In

All set? Good, if your configuration is correct then the quickfix engine will initiate the connection. This involves connecting to the host and port, and sending the login request.  The login request is the first message of any session. In case it is not the first message, all messages that were sent before the login request are simply ignored.

“All messages sent before login request are ignored.”

The example login message would look as follows:

#Send Username/Password on Logon (35=A)8=FIX.4.49=114 35=A 34=1 49=sendercompId 52=20120927-13:15:34.754 56=FXCM 57=someTargetSubId 553=someUsername 554=somePassword 98=0 108=30 141=Y 10=146

 

Tags 553 and 554 are meant to pass on the username and password.  Note that 34=1 assumes that this is the first attempt of the day. In other cases it will just be the sequence number of the message. If all credentials are right, then the logon response from FXCM would look as follows

#FXCM Logon response 8=FIX.4.49=92 35=A 34=1 49=FXCM 50=someTargetSubId 52=20120927-13:15:34.810 56=somesenderCompId 98=0 108=30 141=Y 10=187

Note that since the response is being sent by fxcm and the intended recipient is our system, the 49 tag is FXCM (sender) and tag 56 = somesenderCompId(intended recipient).

Once the credentials have been verified and the successful response received, One might want to get a few initialization parameters in place. For eg, what is the state of the market (open/closed), symbol information (lot sizes, ticksizes), etc.  In order to do this, we use the Trading Session Status Request (35=g)

The following code snippet shows how to send the trading session status request

FIX44::TradingSessionStatusRequest request;request.setField(FIX::TradSesReqID(NextId())); request.setField(FIX::SubscriptionRequestType(FIX::SubscriptionRequestType_SNAPSHOT_PLUS_UPDATES)); FIX::Session::sendToTarget(request,session_id);

This sends the request, the response to which is the trading session status message.  The session status message looks like the following

-- Core TradingSessionStatus Message -- 8=FIX.4.49=12384 35=h34=3 49=FXCM 50=U100D1 52=20120828-13:24:52.38756=fx1294946_client158=Market is closed. Any trading functionality is not available.60=20120828-13:24:52325=N 335=2336=FXCM339=2340=2625=U100D19019=09030=Coordinated Universal Time 
#-- Embedded SecurityList -- 
#NoRelatedSym (This field shows the total number of securities. Below we only show the first 5 to save space)146=69 55=CAD/JPY15=CAD228=1231=1460=4561=19000=189001=39002=0.019003=0.159004=-0.339005=18009076=D9080=19090=09091=09092=09093=09094=500000009095=19096=O55=GBP/CHF15=GBP228=1231=1460=4561=19000=139001=59002=0.00019003=0.159004=-0.379005=13009076=D9080=19090=09091=09092=09093=09094=500000009095=19096=O55=JPN22515=JPY228=100231=1460=7561=19000=10079001=09002=19003=0.079004=-0.099005=1007009076=T9080=29090=259091=19092=259093=19094=10009095=19096=O55=EUR/SEK15=EUR228=1231=1460=4561=19000=329001=59002=0.00019003=-0.829004=0.269005=32009076=D9080=19090=09091=09092=09093=09094=500000009095=19096=O55=AUD/USD15=AUD228=1231=1460=4561=19000=69001=59002=0.00019003=0.639004=-1.319005=6009076=V9080=19090=09091=09092=09093=09094=500000009095=19096=O  -- FXCM System Parameters --   NoFXCMParam (This field shows the total number of system parameters)9016=17  9017=TP_949018=Y9017=BASE_CRNCY9018=USD9017=TRAILING_STOP_USED9018=Y9017=SERVER_TIME_UTC9018=UTC9017=BASE_TIME_ZONE9018=America/New_York9017=EXT_PRICE_TERMINAL9018=PDEMO1_PRICES9017=COND_DIST9018=0.19017=COND_DIST_ENTRY9018=0.19017=TP_1729018=Y9017=BASE_CRNCY_SYMBOL9018=$9017=REPORTS_URL9018=https://fxpa.fxcorporate.com/fxpa/getreport.app/9017=BASE_UNIT_SIZE9018=100009017=END_TRADING_DAY9018=21:00:009017=BASE_CRNCY_PRECISION9018=29017=FixSupport9018=FIXONLY9017=TRAILING_STOP_DYNAMIC9018=Y 9017=FORCE_PASSWORD_CHANGE9018=N10=058

However, we are interested in reading this message from code.  The way to read each of these system parameters is as follows:

int param_count = FIX::IntConvertor::convert(status.getField(9016));

cout << "TSS - FXCM System Parameters" << endl;
 for(int i = 1; i =< param_count; i++)
 {
 FIX::FieldMap map = status.getGroupRef(1,9016);
 string param_name = map.getField(9017);
 string param_value = map.getField(9018);

cout << param_name << " - " << param_value << endl;
 }

FXCM sends additional tags in the tradingSessionStatus message. These can be found in the securityList and system Parameters. This information will be used while sending orders.

Custom fields in SecurityList

  • 9001 – symPrecision – precision of the security. Foreg, USD/JPY the value is 3 => the value fields for this symbol are quoted to a precision of 3 decimal places.
  • 9002 – point size – this represents what we mean by a pip for a security. For eg. EUR/USD would show a value of 0.0001 for this field.
  • 9003 – symInterestBuy – the price in the currency of your account for the default lot size of your server. For eg, if your account is in USD and the server default lot size is 10000. Then 9003=0.64 => you will get $0.64 for 10K size.
  • 9004 – symInterestSell – same as above. 9004=-1.48 => you ll pay $1.48 for every 10K size. You can read the default lot size from the tag 9017=BASE_UNIT_SIZE 9018=10000.
  • 9080 – productId – every security belongs to a product type. 1=Forex, 2=Index, 3=Commodity, 4=treasury, 5=bullion. For eg, EUR/USD would be 1.
  • 9090 – conditionalDistanceStop – The minimum distance of the stop orders from the current market price. If you have a buy position, then your stop order must be atleast this distance away from the current bid.
  • 9091 – conditionalDistanceLimit – The minimum distance of the limit order from the current market price. If you have a buy position, then your limit order must be atleast this distance away from the current bid.
  • 9092 – conditionalDistanceEntryStop – The minimum distance for a new stop entry (pending) order. If you want to place a stop entry order to buy then the price of this order must be atleast this distance away from the current ask.
  • 9093 – conditionalDistanceEntryLimit – The minimum distance for a new limit entry (pending) order. If you wanted to place a new limit entry order to buy, then the price of the order must be atleast this distance away from the current ask.
  • 9094 – maxQuantity – maximum size of a single order.
  • 9095 – minQuantity – minimum size of a single order.
  • 9096 – tradingStatus – This indicates whether the destination is open (‘O’) or closed (‘C’).

In addition to the above custom fields, the system parameters are also returned in this message (combination of 9017,9018 tags)

  • BASE_CRNCY – the currency of your account
  • SERVER_TIME_UTC – if this field is set to UTC then the server will express all time fields in UTC. If not, then it ll express it in the local timezone
  • BASE_TIME_ZONE – shows the local timezone of the server for example “Asia/Kolkatta”
  • COND_DIST – minimum distance between the price of the new stop or limit orders from the current market price. This value is expressed in pips and is generally defaulted to 0.10
  • COND_DIST_ENTRY – minimum distance between the price of the new stop entry or limit entry orders from the current market price. This value is expressed in pips and is generally defaulted to 0.10
  • BASE_UNIT_SIZE – minimum order size for FX securities. For CFD securities, we need to check the 9095 tag
  • END_TRADING_DAY – Timing for the close of the trading day. It is expressed in hh:mm:ss format. This time is always in UTC

Requesting Market Data

After making the connection with the server, we are ready to request for Market Data. MarketData consists of a snapshot message and an incremental update message.  The MarketDataSnapshotFullRefresh(W) message contains the updates to market data. It is obtained as a response to marketdatarequest (v) message.

The following snippet shows how to send the marketdatarequest message:

FIX44::MarketDataRequest mdr; mdr.set(FIX::MDReqID(NextId()));mdr.set(FIX::SubscriptionRequestType(FIX::SubscriptionRequestType_SNAPSHOT_PLUS_UPDATES));mdr.set(FIX::MarketDepth(0));mdr.set(FIX::NoMDEntryTypes(2)); FIX44::MarketDataRequest::NoMDEntryTypes types_group;types_group.set(FIX::MDEntryType(FIX::MDEntryType_BID));mdr.addGroup(types_group);types_group.set(FIX::MDEntryType(FIX::MDEntryType_OFFER));mdr.addGroup(types_group); int no_sym = FIX::IntConvertor::convert(security_list.getField(FIX::FIELD::NoRelatedSym));for(int i = 1; i <= no_sym; i++){   FIX44::SecurityList::NoRelatedSym sym_group;   mdr.addGroup(security_list.getGroup(i,sym_group));} FIX::Session::sendToTarget(mdr,session_id);

The sample marketDataSnapshotFullRefresh message looks like this:

8=FIX.4.49=53335=W34=3649=FXCM50=U100D152=2012091313:07:51.38756=fx157369001_client155=AUD/USD228=1231=1262=4460=4 # Number of MDEntries268=4# High 269=7270=1.03902272=20120913273=13:07:49# Low269=8270=1.03843272=20120913273=13:07:49# Bid269=0270=1.04437271=0272=20120913273=13:07:49336=FXCM625=U100D1276=A299=FXCM-AUDUSD-12638558537=1 # Offer269=1270=1.04459271=0272=20120913273=13:07:49336=FXCM625=U100D1276=A299=FXCM-AUDUSD-12638558537=1# Custom FXCM Fields9000=69001=59002=0.00019005=6009011=09020=19080=19090=09091=09092=09093=09094=500000009095=19096=O10=030

MDEntryTypes

The types of data you can receive are referred to as MDEntryTypes in FIX.  FXCM supports:

  • Bid(0)
  • Ask(1)
  • HighPrice(7)
  • LowPrice(8)

Additional MDEntryTypes such as MDEntryDate, MDEntryTime etc. are found only once within the first repeating group of the message.

Position Information

Before starting a strategy it is important to always retrieve the position information.  This is done using the Position Report (35=AP) message. One could not only request for a position report but also subscribe to updates. Sending a RequestForPositions (AN) message with subscription Request type (263) set to  1 will subscribe to updates.

PosReqType (tag 724) is used to determine if a received report is an open (indicated by 0) or closed (indicated by 1) position.

When an open position report is received, it also contains the price at which the position was opened (tag 730).   In case of a closed position report,  the price at which the position as closed, as well as some additional tags are present:

  • 9052 – gross P&L of the position.
  • 9040 – rollover interest applied to the position
  • 9053 – commission applied
  • 9043 – close price of the position

Example Position Report

5 Currency USD USD
37 OrderID 134757321 134757321
55 Symbol USD/JPY USD/JPY
58 Text I I
325 UnsolicitedIndicator Y Y
336 TradingSessionID FXCM FXCM
447 PartyIDSource D D
448 PartyID FXCM ID FXCMID
452 PartyRole 3 3
453 NoPartyIDs 1 1
523 PartySubID 32 32
581 AccountType 6 6
625 TradingSessionSubID U100D1 U100D1
702 NoPositions 1 1
703 PosType TQ TQ
704 LongQty 1000 1000
707 PosAmtType CASH CASH
708 PosAmt 0 0
715 ClearingBusinessDate 20121220 20121220
721 PosMaintRptID 2610968257 2610968446
724 PosReqType 0 1
727 TotalNumPosReports 0 0
728 PosReqResult 0 0
730 SettlPrice 84.242 84.242
731 SettlPriceType 1 1
734 PriorSettlPrice 0 0
753 NoPosAmt 1 1
802 NoPartySubIDs 4 4
803 PartySubIDType 26 26
9000 FXCMSymID 2 2
9038 FXCMUsedMargin 5
9040 FXCMPosInterest 0 0
9041 FXCMPosID 46961794 46961794
9042 FXCMPosOpenTime 20121220-03:46:25 20121220-03:46:25
9043 FXCMCloseSettlPrice 84.231
9044 FXCMCloseTime 20121220-03:46:37
9048 FXCMCloseClOrdID 1_157
9052 FXCMPosClosePNL -0.13
9053 FXCMPosCommission 0 0
9054 FXCMCloseOrderID 134757323

Overview of Basic Order types

Time In Force

Time in force represents the duration for which an order stays in the order book in case it is not executed. The different values possible are:

  • GTC (Good till cancel) – This means the order stays in force until its executed or cancelled out explicitly. Typically used when you don’t expect the order to be filled immediately. You want to keep the order active until it is filled.
  • Day - This means the order stays in force till its executed or the day expires.  Typically, this is used when your intent of the order gets obsolete with time.
  • IOC (Immediate or Cancel) – The order gets filled as much as possible at the time it enters the order book. The remaining gets cancelled. You want to take the fill at a particular price.
  • FOK(Fill or Kill) – The order either gets completely filled at the time it enters the orderbook or gets cancelled immediately. You are interested in taking the full fill at a particular price.

Order Types

Market

This order will hit the opposite side irrespective of the price. It will take the fill at the next available price as long as the order book is not empty. Typically used when filling the order is more important than the price of the fill. For a market order, the TIF can be GTC, DAY, IOC, FOK.

FIX44::NewOrderSingle order;order.setField(FIX::ClOrdID(NextClOrdID())); order.setField(FIX::Account(account));order.setField(FIX::Symbol("EUR/USD")); order.setField(FIX::Side(FIX::Side_BUY)); order.setField(FIX::TransactTime(FIX::TransactTime())); order.setField(FIX::OrderQty(10000));order.setField(FIX::OrdType(FIX::OrdType_MARKET)); FIX::Session::sendToTarget(order,session_id);
Stop Limit

This order is a market order that has a range for the fill price. This provides a protection against slippage of the fill price. The tag 40=4 means it’s a stopLimit order and the stopPrice is set in tag 99. This represents the worst price you are willing to take the fill at. The TIF can be IOC or FOK

FIX44::NewOrderSingle order;order.setField(FIX::ClOrdID(NextClOrdID())); order.setField(FIX::Account(account));order.setField(FIX::Symbol("EUR/USD")); order.setField(FIX::Side(FIX::Side_BUY)); order.setField(FIX::TransactTime(FIX::TransactTime())); order.setField(FIX::OrderQty(10000));order.setField(FIX::OrdType(FIX::OrdType_STOPLIMIT));order.setField(FIX::StopPx(stop)); FIX::Session::sendToTarget(order,session_id);
Limit

The order will be filled at a price that is equal or better than the price mentioned in the order message (tag 44).  This order type is used when the price of fill is more important than the fill itself. Supported TIFs are GTC, Day,IOC, FOK.

FIX44::NewOrderSingle order;order.setField(FIX::ClOrdID(NextClOrdID())); order.setField(FIX::Account(account));order.setField(FIX::Symbol("EUR/USD")); order.setField(FIX::Side(FIX::Side_BUY)); order.setField(FIX::TransactTime(FIX::TransactTime())); order.setField(FIX::OrderQty(10000));order.setField(FIX::OrdType(FIX::OrdType_LIMIT)); order.setField(FIX::Price(price)); FIX::Session::sendToTarget(order,session_id);
Stop

This is a market order that gets placed when the current market price is worse than your stop price as mentioned in the stopOrder message.  The price of the fill is not guaranteed, as this is a market order. Supported TIFs are GTC and Day.

FIX44::NewOrderSingle order;order.setField(FIX::ClOrdID(NextClOrdID())); order.setField(FIX::Account(account));order.setField(FIX::Symbol("EUR/USD")); order.setField(FIX::Side(FIX::Side_BUY)); order.setField(FIX::TransactTime(FIX::TransactTime())); order.setField(FIX::OrderQty(10000));order.setField(FIX::OrdType(FIX::OrdType_STOP)); order.setField(FIX::StopPx(price)); FIX::Session::sendToTarget(order,session_id);

Code

Example

This piece of code assumes that the quickfix library is installed. The include path needs to be edited accordingly.  This should give a basic understanding of how a FXCM session can be established, market data subscribed, and orders sent.

Main.cpp
#include "fix_application.h"   // -- FIX Example -- // // Upon starting this application, a FIX session will be created and the connection sequence will commence. This includes sending a Logon message, a request for TradingSessionStatus. After the responses to these requests are received, you can use the command prompt to test out the functionality seen below in the switch block.   int main() {             FixApplication app;             // Start session and Logon             app.StartSession();               while(true){                         int command = 0;                         bool exit = false;                         cin >> command;                           switch(command){                         case 0: // Exit example application                                     exit = true;                                     break;                         case 1: // Get positions                                     app.GetPositions();                                     break;                         case 2: // Subscribe to market data                                     app.SubscribeMarketData();                                     break;                         case 3: // Unsubscribe to market data                                     app.UnsubscribeMarketData();                                     break;                         case 4: // Send market order                                     app.MarketOrder();                                     break;                         }                         if(exit)                                     break;             }               // End session and logout             app.EndSession();             while(true){             } // Wait             return 0; } fix_application.h
#ifndef FIXAPPLICATION_H
#define FIXAPPLICATION_H  
#include <iostream>
#include <vector>
#include "quickfix\Application.h"
#include "quickfix\FileLog.h"
#include "quickfix\FileStore.h"
#include "quickfix\fix44\CollateralInquiry.h"
#include "quickfix\fix44\CollateralInquiryAck.h"
#include "quickfix\fix44\CollateralReport.h"
#include "quickfix\fix44\ExecutionReport.h"
#include "quickfix\fix44\MarketDataRequest.h"
#include "quickfix\fix44\MarketDataRequestReject.h"
#include "quickfix\fix44\MarketDataSnapshotFullRefresh.h"
#include "quickfix\fix44\NewOrderList.h"
#include "quickfix\fix44\NewOrderSingle.h"
#include "quickfix\fix44\PositionReport.h"
#include "quickfix\fix44\RequestForPositions.h"
#include "quickfix\fix44\RequestForPositionsAck.h"
#include "quickfix\fix44\SecurityList.h"
#include "quickfix\fix44\TradingSessionStatus.h"
#include "quickfix\fix44\TradingSessionStatusRequest.h"
#include "quickfix\MessageCracker.h"
#include "quickfix\Session.h"
#include "quickfix\SessionID.h"
#include "quickfix\SessionSettings.h"
#include "quickfix\SocketInitiator.h"   using namespace std; using namespace FIX;   class FixApplication : public MessageCracker, public Application { private:             SessionSettings  *settings;             FileStoreFactory *store_factory;             FileLogFactory   *log_factory;             SocketInitiator  *initiator;               // Used as a counter for producing unique request identifiers             unsigned int requestID;             SessionID sessionID;             vector<string> list_accountID;               // Custom FXCM FIX fields             enum FXCM_FIX_FIELDS             {                         FXCM_FIELD_PRODUCT_ID      = 9080,                         FXCM_POS_ID                = 9041,                         FXCM_POS_OPEN_TIME         = 9042,                         FXCM_ERROR_DETAILS         = 9029,                         FXCM_REQUEST_REJECT_REASON = 9025,                         FXCM_USED_MARGIN           = 9038,                         FXCM_POS_CLOSE_TIME        = 9044,                         FXCM_MARGIN_CALL           = 9045,                         FXCM_ORD_TYPE              = 9050,                         FXCM_ORD_STATUS            = 9051,                         FXCM_CLOSE_PNL             = 9052,                         FXCM_SYM_POINT_SIZE        = 9002,                         FXCM_SYM_PRECISION         = 9001,                         FXCM_TRADING_STATUS        = 9096,                         FXCM_PEG_FLUCTUATE_PTS     = 9061,                         FXCM_NO_PARAMS             = 9016,                         FXCM_PARAM_NAME            = 9017,                         FXCM_PARAM_VALUE           = 9018             };   public:             FixApplication();             // FIX Namespace. These are callbacks which indicate when the session is created,             // when we logon and logout, and when messages are exchanged             void onCreate(const SessionID& session_ID);             void onLogon(const SessionID& session_ID);             void onLogout(const SessionID& session_ID);             void toAdmin(Message& message, const SessionID& session_ID);             void toApp(Message& message, const SessionID& session_ID);             void fromAdmin(const Message& message, const SessionID& session_ID);             void fromApp(const Message& message, const SessionID& session_ID);               // Overloaded onMessage methods used in conjuction with MessageCracker class. FIX::MessageCracker             // receives a generic Message in the FIX fromApp and fromAdmin callbacks, constructs the             // message sub type and invokes the appropriate onMessage method below.             void onMessage(const FIX44::TradingSessionStatus& tss, const SessionID& session_ID);             void onMessage(const FIX44::CollateralInquiryAck& ack, const SessionID& session_ID);             void onMessage(const FIX44::CollateralReport& cr, const SessionID& session_ID);             void onMessage(const FIX44::RequestForPositionsAck& ack, const SessionID& session_ID);             void onMessage(const FIX44::PositionReport& pr, const SessionID& session_ID);             void onMessage(const FIX44::MarketDataRequestReject& mdr, const SessionID& session_ID);             void onMessage(const FIX44::MarketDataSnapshotFullRefresh& mds, const SessionID& session_ID);             void onMessage(const FIX44::ExecutionReport& er, const SessionID& session_ID);               // Starts the FIX session. Throws FIX::ConfigError exception if our configuration settings             // do not pass validation required to construct SessionSettings             void StartSession();             // Logout and end session             void EndSession();               // Sends TradingSessionStatusRequest message in order to receive as a response the             // TradingSessionStatus message             void GetTradingStatus();             // Sends the CollateralInquiry message in order to receive as a response the             // CollateralReport message.             void GetAccounts();             // Sends RequestForPositions which will return PositionReport messages if positions             // matching the requested criteria exist; otherwise, a RequestForPositionsAck will be             // sent with the acknowledgement that no positions exist. In our example, we request             // positions for all accounts under our login             void GetPositions();             // Subscribes to the EUR/USD trading security             void SubscribeMarketData();             // Unsubscribes from the EUR/USD trading security             void UnsubscribeMarketData();             // Sends a basic NewOrderSingle message to buy EUR/USD at the             // current market price             void MarketOrder();             // Generate string value used to populate the fields in each message             // which are used as a custom identifier             string NextRequestID();             // Adds string accountIDs to our vector<string> being used to             // account for the accountIDs under our login             void RecordAccount(string accountID); };  
#endif // FIXAPPLICATION_H fix_application.cpp
#include "fix_application.h" FixApplication::FixApplication() {             // Initialize unsigned int requestID to 1. We will use this as a             // counter for making request IDs             requestID = 1; }   // Gets called when quickfix creates a new session. A session comes into and remains in existence // for the life of the application. void FixApplication::onCreate(const SessionID& session_ID) {             // FIX Session created. We must now logon. QuickFIX will automatically send             // the Logon(A) message             cout << "Session -> created" << endl;             sessionID = session_ID; }   // Notifies you when a valid logon has been established with FXCM. void FixApplication::onLogon(const SessionID& session_ID) {             // Session logon successful. Now we request TradingSessionStatus which is             // used to determine market status (open or closed), to get a list of securities,             // and to obtain important FXCM system parameters             cout << "Session -> logon" << endl;             GetTradingStatus(); }   // Notifies you when an FIX session is no longer online. This could happen during a normal logout // exchange or because of a forced termination or a loss of network connection. void FixApplication::onLogout(const SessionID& session_ID) {             // Session logout             cout << "Session -> logout" << endl; }   // Provides you with a peak at the administrative messages that are being sent from your FIX engine // to FXCM. void FixApplication::toAdmin(Message& message, const SessionID& session_ID) {             // If the Admin message being sent to FXCM is of typle Logon (A), we want             // to set the Username and Password fields. We want to catch this message as it             // is going out.             string msg_type = message.getHeader().getField(FIELD::MsgType);             if(msg_type == "A"){                         // Get both username and password from our settings file. Then set these                         // respective fields                         string user = settings->get().getString("Username");                         string pass = settings->get().getString("Password");                         message.setField(Username(user));                         message.setField(Password(pass));             }             // All messages sent to FXCM must contain the TargetSubID field (both Administrative and             // Application messages). Here we set this.             string sub_ID = settings->get().getString("TargetSubID");             message.getHeader().setField(TargetSubID(sub_ID)); }   // A callback for application messages that you are being sent to a counterparty. void FixApplication::toApp(Message& message, const SessionID& session_ID) {             // All messages sent to FXCM must contain the TargetSubID field (both Administrative and             // Application messages). Here we set this.             string sub_ID = settings->get().getString("TargetSubID");             message.getHeader().setField(TargetSubID(sub_ID)); }   // Notifies you when an administrative message is sent from FXCM to your FIX engine. void FixApplication::fromAdmin(const Message& message, const SessionID& session_ID) {             // Call MessageCracker.crack method to handle the message by one of our             // overloaded onMessage methods below             crack(message, session_ID); }   // One of the core entry points for your FIX application. Every application level request will come through here. void FixApplication::fromApp(const Message& message, const SessionID& session_ID) {             // Call MessageCracker.crack method to handle the message by one of our             // overloaded onMessage methods below             crack(message, session_ID); }   // The TradingSessionStatus message is used to provide an update on the status of the market. Furthermore, // this message contains useful system parameters as well as information about each trading security (embedded SecurityList). // TradingSessionStatus should be requested upon successful Logon and subscribed to. The contents of the // TradingSessionStatus message, specifically the SecurityList and system parameters, should dictate how fields // are set when sending messages to FXCM. void FixApplication::onMessage(const FIX44::TradingSessionStatus& tss, const SessionID& session_ID) {             // Check TradSesStatus field to see if the trading desk is open or closed             // 2 = Open; 3 = Closed             string trad_status = tss.getField(FIELD::TradSesStatus);             cout << "TradingSessionStatus -> TradSesStatus -" << trad_status << endl;             // Within the TradingSessionStatus message is an embeded SecurityList. From SecurityList we can see             // the list of available trading securities and information relevant to each; e.g., point sizes,             // minimum and maximum order quantities by security, etc.             cout << "  SecurityList via TradingSessionStatus -> " << endl;             int symbols_count = IntConvertor::convert(tss.getField(FIELD::NoRelatedSym));             for(int i = 1; i <= symbols_count; i++){                         // Get the NoRelatedSym group and for each, print out the Symbol value                         FIX44::SecurityList::NoRelatedSym symbols_group;                         tss.getGroup(i,symbols_group);                         string symbol = symbols_group.getField(FIELD::Symbol);                         cout << "    Symbol -> " << symbol << endl;             }             // Also within TradingSessionStatus are FXCM system parameters. This includes important information             // such as account base currency, server time zone, the time at which the trading day ends, and more.             cout << "  System Parameters via TradingSessionStatus -> " << endl;             // Read field FXCMNoParam (9016) which shows us how many system parameters are             // in the message             int params_count = IntConvertor::convert(tss.getField(FXCM_NO_PARAMS)); // FXCMNoParam (9016)             for(int i = 1; i < params_count; i++){                         // For each paramater, print out both the name of the paramater and the value of the                         // paramater. FXCMParamName (9017) is the name of the paramater and FXCMParamValue(9018)                         // is of course the paramater value                         FIX::FieldMap field_map = tss.getGroupRef(i,FXCM_NO_PARAMS);                         cout << "    Param Name -> " << field_map.getField(FXCM_PARAM_NAME)                                     << " - Param Value -> " << field_map.getField(FXCM_PARAM_VALUE) << endl;             }             // Request accounts under our login             GetAccounts();               // ** Note on Text(58) **             // You will notice that Text(58) field is always set to "Market is closed. Any trading             // functionality is not available." This field is always set to this value; therefore, do not             // use this field value to determine if the trading desk is open. As stated above, use TradSesStatus for this purpose }   void FixApplication::onMessage(const FIX44::CollateralInquiryAck& ack, const SessionID& session_ID) {   }   // CollateralReport is a message containing important information for each account under the login. It is returned // as a response to CollateralInquiry. You will receive a CollateralReport for each account under your login. // Notable fields include Account(1) which is the AccountID and CashOutstanding(901) which is the account balance void FixApplication::onMessage(const FIX44::CollateralReport& cr, const SessionID& session_ID) {             cout << "CollateralReport -> " << endl;             string accountID = cr.getField(FIELD::Account);             // Get account balance, which is the cash balance in the account, not including any profit             // or losses on open trades             string balance = cr.getField(FIELD::CashOutstanding);             cout << "  AccountID -> " << accountID << endl;             cout << "  Balance -> " << balance << endl;             // The CollateralReport NoPartyIDs group can be inspected for additional account information             // such as AccountName or HedgingStatus             FIX44::CollateralReport::NoPartyIDs group;             cr.getGroup(1,group); // CollateralReport will only have 1 NoPartyIDs group             cout << "  Parties -> "<< endl;             // Get the number of NoPartySubIDs repeating groups             int number_subID = IntConvertor::convert(group.getField(FIELD::NoPartySubIDs));             // For each group, print out both the PartySubIDType and the PartySubID (the value)             for(int u = 1; u <= number_subID; u++){                         FIX44::CollateralReport::NoPartyIDs::NoPartySubIDs sub_group;                         group.getGroup(u,sub_group);                           string sub_type = sub_group.getField(FIELD::PartySubIDType);                         string sub_value = sub_group.getField(FIELD::PartySubID);                         cout << "    " << sub_type << " -> " << sub_value << endl;             }             // Add the accountID to our vector<string> being used to track all             // accounts under our login             RecordAccount(accountID); }   void FixApplication::onMessage(const FIX44::RequestForPositionsAck& ack, const SessionID& session_ID) {             string pos_reqID = ack.getField(FIELD::PosReqID);             cout << "RequestForPositionsAck -> PosReqID - " << pos_reqID << endl;               // If a PositionReport is requested and no positions exist for that request, the Text field will             // indicate that no positions mathced the requested criteria             if(ack.isSetField(FIELD::Text))                         cout << "RequestForPositionsAck -> Text - " << ack.getField(FIELD::Text) << endl; }   void FixApplication::onMessage(const FIX44::PositionReport& pr, const SessionID& session_ID) {             // Print out important position related information such as accountID and symbol             string accountID = pr.getField(FIELD::Account);             string symbol = pr.getField(FIELD::Symbol);             string positionID = pr.getField(FXCM_POS_ID);             string pos_open_time = pr.getField(FXCM_POS_OPEN_TIME);             cout << "PositionReport -> " << endl;             cout << "   Account -> " << accountID << endl;             cout << "   Symbol -> " << symbol << endl;             cout << "   PositionID -> " << positionID << endl;             cout << "   Open Time -> " << pos_open_time << endl; }   void FixApplication::onMessage(const FIX44::MarketDataRequestReject& mdr, const SessionID& session_ID) {             // If MarketDataRequestReject is returned as the result of a MarketDataRequest message,             // print out the contents of the Text field but first check that it is set             cout << "MarketDataRequestReject -> " << endl;             if(mdr.isSetField(FIELD::Text)){                         cout << " Text -> " << mdr.getField(FIELD::Text) << endl;             } }   void FixApplication::onMessage(const FIX44::MarketDataSnapshotFullRefresh& mds, const SessionID& session_ID) {             // Get symbol name of the snapshot; e.g., EUR/USD. Our example only subscribes to EUR/USD so             // this is the only possible value             string symbol = mds.getField(FIELD::Symbol);             // Declare variables for both the bid and ask prices. We will read the MarketDataSnapshotFullRefresh             // message for tthese values             double bid_price = 0;             double ask_price = 0;             // For each MDEntry in the message, inspect the NoMDEntries group for             // the presence of either the Bid or Ask (Offer) type             int entry_count = IntConvertor::convert(mds.getField(FIELD::NoMDEntries));             for(int i = 1; i < entry_count; i++){                         FIX44::MarketDataSnapshotFullRefresh::NoMDEntries group;                         mds.getGroup(i,group);                         string entry_type = group.getField(FIELD::MDEntryType);                         if(entry_type == "0"){ // Bid                                     bid_price = DoubleConvertor::convert(group.getField(FIELD::MDEntryPx));                         }else if(entry_type == "1"){ // Ask (Offer)                                     ask_price = DoubleConvertor::convert(group.getField(FIELD::MDEntryPx));                         }             }             cout << "MarketDataSnapshotFullRefresh -> Symbol - " << symbol                         << " Bid - " << bid_price << " Ask - " << ask_price << endl; }   void FixApplication::onMessage(const FIX44::ExecutionReport& er, const SessionID& session_ID) {             cout << "ExecutionReport -> " << endl;             cout << "  ClOrdID -> " << er.getField(FIELD::ClOrdID) << endl;             cout << "  Account -> " << er.getField(FIELD::Account) << endl;             cout << "  OrderID -> " << er.getField(FIELD::OrderID) << endl;             cout << "  LastQty -> " << er.getField(FIELD::LastQty) << endl;             cout << "  CumQty -> " << er.getField(FIELD::CumQty) << endl;             cout << "  ExecType -> " << er.getField(FIELD::ExecType) << endl;             cout << "  OrdStatus -> " << er.getField(FIELD::OrdStatus) << endl;               // ** Note on order status. **             // In order to determine the status of an order, and also how much an order is filled, we must             // use the OrdStatus and CumQty fields. There are 3 possible final values for OrdStatus: Filled (2),             // Rejected (8), and Cancelled (4). When the OrdStatus field is set to one of these values, you know             // the execution is completed. At this time the CumQty (14) can be inspected to determine if and how             // much of an order was filled. }   // Starts the FIX session. Throws FIX::ConfigError exception if our configuration settings // do not pass validation required to construct SessionSettings void FixApplication::StartSession() {             try{                         settings      = new SessionSettings("settings.cfg");                         store_factory = new FileStoreFactory(* settings);                         log_factory   = new FileLogFactory(* settings);                         initiator     = new SocketInitiator(* this, * store_factory, * settings, * log_factory/*Optional*/);                         initiator->start();             }catch(ConfigError error){                         cout << error.what() << endl;             } }   // Logout and end session void FixApplication::EndSession() {             initiator->stop();             delete initiator;             delete settings;             delete store_factory;             delete log_factory; }   // Sends TradingSessionStatusRequest message in order to receive as a response the // TradingSessionStatus message void FixApplication::GetTradingStatus() {             // Request TradingSessionStatus message             FIX44::TradingSessionStatusRequest request;             request.setField(TradSesReqID(NextRequestID()));             request.setField(TradingSessionID("FXCM"));             request.setField(SubscriptionRequestType(SubscriptionRequestType_SNAPSHOT));             Session::sendToTarget(request, sessionID); }   // Sends the CollateralInquiry message in order to receive as a response the // CollateralReport message. void FixApplication::GetAccounts() {             // Request CollateralReport message. We will receive a CollateralReport for each             // account under our login             FIX44::CollateralInquiry request;             request.setField(CollInquiryID(NextRequestID()));             request.setField(TradingSessionID("FXCM"));             request.setField(SubscriptionRequestType(SubscriptionRequestType_SNAPSHOT));             Session::sendToTarget(request, sessionID); }   // Sends RequestForPositions which will return PositionReport messages if positions // matching the requested criteria exist; otherwise, a RequestForPositionsAck will be // sent with the acknowledgement that no positions exist. In our example, we request // positions for all accounts under our login void FixApplication::GetPositions() {             // Here we will get positions for each account under our login. To do this,             // we will send a RequestForPositions message that contains the accountID             // associated with our request. For each account in our list, we send             // RequestForPositions.             int total_accounts = (int)list_accountID.size();             for(int i = 0; i < total_accounts; i++){                         string accountID = list_accountID.at(i);                         // Set default fields                         FIX44::RequestForPositions request;                         request.setField(PosReqID(NextRequestID()));                         request.setField(PosReqType(PosReqType_POSITIONS));                         // AccountID for the request. This must be set for routing purposes. We must                         // also set the Parties AccountID field in the NoPartySubIDs group                         request.setField(Account(accountID));                         request.setField(SubscriptionRequestType(SubscriptionRequestType_SNAPSHOT));                         request.setField(AccountType(                                     AccountType_ACCOUNT_IS_CARRIED_ON_NON_CUSTOMER_SIDE_OF_BOOKS_AND_IS_CROSS_MARGINED));                         request.setField(TransactTime());                         request.setField(ClearingBusinessDate());                         request.setField(TradingSessionID("FXCM"));                         // Set NoPartyIDs group. These values are always as seen below                         request.setField(NoPartyIDs(1));                         FIX44::RequestForPositions::NoPartyIDs parties_group;                         parties_group.setField(PartyID("FXCM ID"));                         parties_group.setField(PartyIDSource('D'));                         parties_group.setField(PartyRole(3));                         parties_group.setField(NoPartySubIDs(1));                         // Set NoPartySubIDs group                         FIX44::RequestForPositions::NoPartyIDs::NoPartySubIDs sub_parties;                         sub_parties.setField(PartySubIDType(PartySubIDType_SECURITIES_ACCOUNT_NUMBER));                         // Set Parties AccountID                         sub_parties.setField(PartySubID(accountID));                         // Add NoPartySubIds group                         parties_group.addGroup(sub_parties);                         // Add NoPartyIDs group                         request.addGroup(parties_group);                         // Send request                         Session::sendToTarget(request, sessionID);             } }   // Subscribes to the EUR/USD trading security void FixApplication::SubscribeMarketData() {             // Subscribe to market data for EUR/USD             string request_ID = "EUR_USD_Request_";             FIX44::MarketDataRequest request;             request.setField(MDReqID(request_ID));             request.setField(SubscriptionRequestType(                         SubscriptionRequestType_SNAPSHOT_PLUS_UPDATES));             request.setField(MarketDepth(0));             request.setField(NoRelatedSym(1));               // Add the NoRelatedSym group to the request with Symbol             // field set to EUR/USD             FIX44::MarketDataRequest::NoRelatedSym symbols_group;             symbols_group.setField(Symbol("EUR/USD"));             request.addGroup(symbols_group);               // Add the NoMDEntryTypes group to the request for each MDEntryType             // that we are subscribing to. This includes Bid, Offer, High, and Low             FIX44::MarketDataRequest::NoMDEntryTypes entry_types;             entry_types.setField(MDEntryType(MDEntryType_BID));             request.addGroup(entry_types);             entry_types.setField(MDEntryType(MDEntryType_OFFER));             request.addGroup(entry_types);             entry_types.setField(MDEntryType(MDEntryType_TRADING_SESSION_HIGH_PRICE));             request.addGroup(entry_types);             entry_types.setField(MDEntryType(MDEntryType_TRADING_SESSION_LOW_PRICE));             request.addGroup(entry_types);               Session::sendToTarget(request, sessionID); }   // Unsubscribes from the EUR/USD trading security void FixApplication::UnsubscribeMarketData() {             // Unsubscribe from EUR/USD. Note that our request_ID is the exact same             // that was sent for our request to subscribe. This is necessary to             // unsubscribe. This request below is identical to our request to subscribe             // with the exception that SubscriptionRequestType is set to             // "SubscriptionRequestType_DISABLE_PREVIOUS_SNAPSHOT_PLUS_UPDATE_REQUEST"             string request_ID = "EUR_USD_Request_";             FIX44::MarketDataRequest request;             request.setField(MDReqID(request_ID));             request.setField(SubscriptionRequestType(                         SubscriptionRequestType_DISABLE_PREVIOUS_SNAPSHOT_PLUS_UPDATE_REQUEST));             request.setField(MarketDepth(0));             request.setField(NoRelatedSym(1));               // Add the NoRelatedSym group to the request with Symbol             // field set to EUR/USD             FIX44::MarketDataRequest::NoRelatedSym symbols_group;             symbols_group.setField(Symbol("EUR/USD"));             request.addGroup(symbols_group);               // Add the NoMDEntryTypes group to the request for each MDEntryType             // that we are subscribing to. This includes Bid, Offer, High, and Low             FIX44::MarketDataRequest::NoMDEntryTypes entry_types;             entry_types.setField(MDEntryType(MDEntryType_BID));             request.addGroup(entry_types);             entry_types.setField(MDEntryType(MDEntryType_OFFER));             request.addGroup(entry_types);             entry_types.setField(MDEntryType(MDEntryType_TRADING_SESSION_HIGH_PRICE));             request.addGroup(entry_types);             entry_types.setField(MDEntryType(MDEntryType_TRADING_SESSION_LOW_PRICE));             request.addGroup(entry_types);               Session::sendToTarget(request, sessionID); }   // Sends a basic NewOrderSingle message to buy EUR/USD at the // current market price void FixApplication::MarketOrder() {             // For each account in our list, send a NewOrderSingle message             // to buy EUR/USD. What differentiates this message is the             // accountID             int total_accounts = (int)list_accountID.size();             for(int i = 0; i < total_accounts; i++){                         string accountID = list_accountID.at(i);                         FIX44::NewOrderSingle request;                         request.setField(ClOrdID(NextRequestID()));                                    request.setField(Account(accountID));                         request.setField(Symbol("EUR/USD"));                         request.setField(TradingSessionID("FXCM"));                         request.setField(TransactTime());                         request.setField(OrderQty(10000));                         request.setField(Side(FIX::Side_BUY));                         request.setField(OrdType(OrdType_MARKET));                         request.setField(TimeInForce(TimeInForce_GOODTILLCANCEL));                         Session::sendToTarget(request, sessionID);             } }   // Generate string value used to populate the fields in each message // which are used as a custom identifier string FixApplication::NextRequestID() {             if(requestID == 65535)                         requestID = 1;               requestID++;             string next_ID = IntConvertor::convert(requestID);             return next_ID; }   // Adds string accountIDs to our vector<string> being used to // account for the accountIDs under our login void FixApplication::RecordAccount(string accountID) {             int size = (int)list_accountID.size();             if(size == 0){                         list_accountID.push_back(accountID);             }else{                         for(int i = 0; i < size; i++){                                     if(list_accountID.at(i) == accountID)                                                 break;                                     if(i == size - 1){                                                 list_accountID.push_back(accountID);                                     }                         }             } }

Next Step

FXCM is definitely one of the leading online trading platforms that allow retail clients to speculate on forex. If you’re a retail trader or a tech professional looking to start your own automated trading desk or curious to know more about forex and algorithmic trading, begin with basic concepts like automated trading architecture, market microstructure, strategy backtesting system and order management system. Start learning algo trading today!

Disclaimer: All investments and trading in the stock market involve risk. Any decisions to place trades in the financial markets, including trading in stock or options or other financial instruments is a personal decision that should only be made after thorough research, including a personal risk and financial assessment and the engagement of professional assistance to the extent you believe necessary. The trading strategies or related information mentioned in this article is for informational purposes only.

Download The Code

  • FXCM over FIX Tutorial - C++ Code

    
AI-Powered Trading Workshop 2024