QuickFIX/J Book a FX Trade

In our last tutorial we created a basic initiator and an acceptor. In this post we will enhance our code and we will book a single order.

First we will enhance our executor class which is TestTradeAppExecutor.java. Important thing to note :

  1. Extends MessageCracker – which provides callback methods
  2. Implementation of fromApp cracks the message which invokes onMessage callback
  3. onMessage is overridden and it prepares the execution report (for a successful scenario)

TestTradeAppExecutor.java


package com.mumz.test.fix.executor;

import java.util.HashMap;
import java.util.Map;

import quickfix.Application;
import quickfix.DoNotSend;
import quickfix.FieldNotFound;
import quickfix.IncorrectDataFormat;
import quickfix.IncorrectTagValue;
import quickfix.Message;
import quickfix.MessageCracker;
import quickfix.RejectLogon;
import quickfix.Session;
import quickfix.SessionID;
import quickfix.SessionNotFound;
import quickfix.UnsupportedMessageType;
import quickfix.field.AvgPx;
import quickfix.field.CumQty;
import quickfix.field.ExecID;
import quickfix.field.ExecTransType;
import quickfix.field.ExecType;
import quickfix.field.LeavesQty;
import quickfix.field.OrdStatus;
import quickfix.field.OrdType;
import quickfix.field.OrderID;
import quickfix.field.Price;
import quickfix.field.Side;
import quickfix.field.Symbol;
import quickfix.fix42.ExecutionReport;
import quickfix.fix42.NewOrderSingle;

/**
 * The Class TestTradeAppExecutor.
 * 
 * @author prabhat.jha
 */
public class TestTradeAppExecutor extends MessageCracker implements Application {

	private Map<String, Double> priceMap = null;

	public TestTradeAppExecutor() {
		priceMap = new HashMap<String, Double>();
		priceMap.put("EUR/USD", 1.234);
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see quickfix.Application#onCreate(quickfix.SessionID)
	 */
	@Override
	public void onCreate(SessionID sessionId) {
		System.out.println("Executor Session Created with SessionID = "
				+ sessionId);
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see quickfix.Application#onLogon(quickfix.SessionID)
	 */
	@Override
	public void onLogon(SessionID sessionId) {

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see quickfix.Application#onLogout(quickfix.SessionID)
	 */
	@Override
	public void onLogout(SessionID sessionId) {

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see quickfix.Application#toAdmin(quickfix.Message, quickfix.SessionID)
	 */
	@Override
	public void toAdmin(Message message, SessionID sessionId) {

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see quickfix.Application#fromAdmin(quickfix.Message, quickfix.SessionID)
	 */
	@Override
	public void fromAdmin(Message message, SessionID sessionId)
			throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,
			RejectLogon {

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see quickfix.Application#toApp(quickfix.Message, quickfix.SessionID)
	 */
	@Override
	public void toApp(Message message, SessionID sessionId) throws DoNotSend {

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see quickfix.Application#fromApp(quickfix.Message, quickfix.SessionID)
	 */
	@Override
	public void fromApp(Message message, SessionID sessionId)
			throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,
			UnsupportedMessageType {
		crack(message, sessionId);
	}

	public void onMessage(NewOrderSingle message, SessionID sessionID)
			throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue {
		OrdType orderType = message.getOrdType();
		Symbol currencyPair = message.getSymbol();
		/*
		 * Assuming that we are directly dealing with Market
		 */
		Price price = null;
		if (OrdType.FOREX_MARKET == orderType.getValue()) {
			if(this.priceMap.containsKey(currencyPair.getValue())){
				price = new Price(this.priceMap.get(currencyPair.getValue()));
			} else {
				price = new Price(1.4589);
			}
			
		} 
		//We are hardcoding this to 1, but in real world this may be something like a sequence.
		OrderID orderNumber = new OrderID("1");
		//Id of the report, a unique identifier, once again this will be somthing like a sequence
		ExecID execId = new ExecID("1");
		//In this case this is a new order with no exception hence mark it as NEW
		ExecTransType exectutionTransactioType = new ExecTransType(ExecTransType.NEW);
		//This execution report is for confirming a new Order
		ExecType purposeOfExecutionReport =new ExecType(ExecType.FILL);
		//Represents status of the order, since this order was successful mark it as filled.
		OrdStatus orderStatus = new OrdStatus(OrdStatus.FILLED);
		//Represents the currencyPair
		Symbol symbol = currencyPair;
		//Represents side
		Side side = message.getSide();
		//What is the quantity left for the day, we will take 100 as a hardcoded value, we can also keep a note of this from say limit module.
		LeavesQty leavesQty = new LeavesQty(100);
		//Total quantity of all the trades booked in this applicaition, here it is hardcoded to be 100.
		CumQty cummulativeQuantity = new CumQty(100);
		//Average Price, say make it 1.235
		AvgPx avgPx = new AvgPx(1.235);
		ExecutionReport executionReport = new ExecutionReport(orderNumber,execId, exectutionTransactioType,
				purposeOfExecutionReport, orderStatus, symbol, side, leavesQty, cummulativeQuantity, avgPx);
		executionReport.set(price);
		try {
			Session.sendToTarget(executionReport, sessionID);
		} catch (SessionNotFound e) {
			e.printStackTrace();
		}
	}
}

Second we will enhance our TestTradeAppInitiator so that it logs the order id which has been sent from our server. Important things to note:

  1. Extends MessageCracker – which provides callback methods
  2. Implementation of fromApp cracks the message which invokes onMessage callback
  3. onMessage is overridden and it retrieves information from execution report.

TestTradeAppInitiator

package com.mumz.test.fix.initiator;

import quickfix.Application;
import quickfix.DoNotSend;
import quickfix.FieldNotFound;
import quickfix.IncorrectDataFormat;
import quickfix.IncorrectTagValue;
import quickfix.Message;
import quickfix.RejectLogon;
import quickfix.SessionID;
import quickfix.UnsupportedMessageType;
import quickfix.fix42.ExecutionReport;
import quickfix.fix42.MessageCracker;

/***
 * The Class TestTradeAppInitiator.
 * @author prabhat.jha
 */
public class TestTradeAppInitiator extends MessageCracker implements Application {

	/** (non-Javadoc)
	 * @see quickfix.Application#onCreate(quickfix.SessionID)
	 */
	@Override
	public void onCreate(SessionID sessionId) {

	}

	/** (non-Javadoc)
	 * @see quickfix.Application#onLogon(quickfix.SessionID)
	 */
	@Override
	public void onLogon(SessionID sessionId) {
		System.out.println("On logged on");
	}

	/** (non-Javadoc)
	 * @see quickfix.Application#onLogout(quickfix.SessionID)
	 */
	@Override
	public void onLogout(SessionID sessionId) {

	}

	/** (non-Javadoc)
	 * @see quickfix.Application#toAdmin(quickfix.Message, quickfix.SessionID)
	 */
	@Override
	public void toAdmin(Message message, SessionID sessionId) {

	}

	/** (non-Javadoc)
	 * @see quickfix.Application#fromAdmin(quickfix.Message, quickfix.SessionID)
	 */
	@Override
	public void fromAdmin(Message message, SessionID sessionId)
			throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,
			RejectLogon {

	}

	/** (non-Javadoc)
	 * @see quickfix.Application#toApp(quickfix.Message, quickfix.SessionID)
	 */
	@Override
	public void toApp(Message message, SessionID sessionId) throws DoNotSend {

	}

	/*** (non-Javadoc)
	 * @see quickfix.Application#fromApp(quickfix.Message, quickfix.SessionID)
	 */
	@Override
	public void fromApp(Message message, SessionID sessionId)
			throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,
			UnsupportedMessageType {
		crack(message, sessionId);
	}
	
	@Override
	public void onMessage(ExecutionReport message, SessionID sessionID)
			throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue {
		System.out.println("Received Execution report from server");
		System.out.println("Order Id : " + message.getOrderID().getValue());
		System.out.println("Order Status : " + message.getOrdStatus().getValue());
		System.out.println("Order Price : " + message.getPrice().getValue());
	}
}

Third let’s change our test code to send an order for booking.
TestTradeAppInitiatorApp.java


package com.mumz.test.fix.initiator;

import java.io.IOException;
import java.util.Scanner;

import quickfix.Application;
import quickfix.ConfigError;
import quickfix.DefaultMessageFactory;
import quickfix.FileLogFactory;
import quickfix.FileStoreFactory;
import quickfix.MessageFactory;
import quickfix.Session;
import quickfix.SessionID;
import quickfix.SessionNotFound;
import quickfix.SessionSettings;
import quickfix.SocketInitiator;
import quickfix.field.ClOrdID;
import quickfix.field.HandlInst;
import quickfix.field.OrdType;
import quickfix.field.OrderQty;
import quickfix.field.Side;
import quickfix.field.Symbol;
import quickfix.field.TransactTime;
import quickfix.fix42.NewOrderSingle;

/**
 * The Class TestTradeAppInitiatorApp.
 * 
 * @author prabhat.jha
 */
public class TestTradeAppInitiatorApp {
	
	/**
	 * The main method.
	 * 
	 * @param args
	 *            the args
	 */
	public static void main(String[] args) {
		SocketInitiator socketInitiator = null;
		try {
			SessionSettings initiatorSettings = new SessionSettings(
					"C:/Prabhat/test/QuickFixJ/initiator/initiatorSettings.txt");
			Application initiatorApplication = new TestTradeAppInitiator();
			FileStoreFactory fileStoreFactory = new FileStoreFactory(
					initiatorSettings);
			FileLogFactory fileLogFactory = new FileLogFactory(
					initiatorSettings);
			MessageFactory messageFactory = new DefaultMessageFactory();
			socketInitiator = new SocketInitiator(initiatorApplication, fileStoreFactory, initiatorSettings, fileLogFactory, messageFactory);
			socketInitiator.start();
			SessionID sessionId = socketInitiator.getSessions().get(0);
			Session.lookupSession(sessionId).logon();
			while(!Session.lookupSession(sessionId).isLoggedOn()){
				System.out.println("Waiting for login success");
				Thread.sleep(1000);
			}
			System.out.println("Logged In...");
			
			Thread.sleep(5000);
			bookSingleOrder(sessionId);
			
			System.out.println("Type to quit");
			Scanner scanner = new Scanner(System.in);
			scanner.next();
			Session.lookupSession(sessionId).disconnect("Done",false);
			socketInitiator.stop();
		} catch (ConfigError e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void bookSingleOrder(SessionID sessionID){
		//In real world this won't be a hardcoded value rather than a sequence.
		ClOrdID orderId = new ClOrdID("1");
		//to be executed on the exchange
		HandlInst instruction = new HandlInst(HandlInst.AUTOMATED_EXECUTION_ORDER_PRIVATE);
		//Since its FX currency pair name
		Symbol mainCurrency = new Symbol("EUR/USD");
		//Which side buy, sell
		Side side = new Side(Side.BUY);
		//Time of transaction
		TransactTime transactionTime = new TransactTime();
		//Type of our order, here we are assuming this is being executed on the exchange
		OrdType orderType = new OrdType(OrdType.FOREX_MARKET);
		NewOrderSingle newOrderSingle = new NewOrderSingle(orderId,instruction,mainCurrency, side, transactionTime,orderType);
		//Quantity
		newOrderSingle.set(new OrderQty(100));
		try {
			Session.sendToTarget(newOrderSingle, sessionID);
		} catch (SessionNotFound e) {
			e.printStackTrace();
		}
	}
}

That’s all. I have added comments in the code for better readability, hope you find it useful.

There are 5 comments

  1. Marcos Bennet

    Hi Prabhat Jha,

    The code seems not to be working anymore. It is not being able to login and keeps printing Waiting for login success. Do you know what is the problem? Thanks

  2. vsingh

    Its great and very useful. For others- there are couple of things you need to be aware of to make it run on your machines:
    1) this works on FIX 4.2 ,but it will not work for 4.3. You need to make changes to get around that (like different constructor for ExecutionReport etc..).
    2) Make sure the paths in the App classes or the settings file are correct as per your local environment.

    Happy to help anyone regarding 4.3 version.

    Thanks again Prabhat for the article.

  3. Marwan Ben

    Prabhat Jha,

    You missed in your code the onMessage(Logon message, SessionID sessionID), that’s why it’s not being able to login. (Marcos, I think you had the same issue).

    Thank you for your code. It really helped me to test my FIX engine.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s