CoralFIX (Straight to the Point)

Below an example of a FIX client and server that you can use as reference to start coding your own clients and servers. You can run their main methods and the client will connect to the server and start heartbeating.

The client:

package com.coralblocks.coralfix.sample;

import static com.coralblocks.coralfix.FixTags.*;

import java.util.Iterator;

import com.coralblocks.coralfix.FixConstants.MsgTypes;
import com.coralblocks.coralfix.FixGroup;
import com.coralblocks.coralfix.FixGroupElement;
import com.coralblocks.coralfix.FixMessage;
import com.coralblocks.coralfix.FixTags;
import com.coralblocks.coralfix.client.FixApplicationClient;
import com.coralblocks.coralreactor.client.Client;
import com.coralblocks.coralreactor.nio.NioReactor;
import com.coralblocks.coralreactor.util.Configuration;
import com.coralblocks.coralreactor.util.MapConfiguration;

public class SimpleFixApplicationClient extends FixApplicationClient {
	
	public SimpleFixApplicationClient(NioReactor nio, String localhost, int port, Configuration config) {
		
	    super(nio, localhost, port, config);
	    
	    // Add any extra / non-standard fix tags from your spec so you can work with the name instead of the id
	    FixTags.addTag("MySpecialTag", 3434);
	    
	    // Specify the repeating group you are expecting:
	    addGroupTemplate(MsgTypes.NewOrderSingle, NoPartyIDs, PartyID, PartyIDSource, PartyRole);
	    
        /* Explanation about addGroupTemplate above:
         *
         * MsgTypes.NewOrderSingle is the message type containing the repeating group
         *    
         * NoPartyIDs is the main repeating group tag, meaning the repeating groups will
         * start after that tag. This tag contains the number (i.e. an integer) of repeating groups that follow.
         *
         * PartyID is the very first tag that every repeating group will have. That's important
         * to correctly denote the starting of a new repeating group and is enforced by the
         * FIX spec (i.e. you can't start a new repeating group with another tag)
         *
         * PartyIDSource, PartyRole, etc. are all the other tags (not included the very first
         * one above) that this repeating group can contain. They can be specified in any order
         * and can appear in the repeating group in any order. They are also all optional, in other words,
         * they do not need to appear in the FIX message but they do have to be specified here.
         */
    }
	
	@Override
	protected void handleOpened() {
		// client was opened and will now try to connect to its destination
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleClosed() {
		// client was closed, its connection was terminated and it will now sit idle
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleConnectionEstablished() {
		// socket connection was established but no login handshake was done yet
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleConnectionOpened() {
		// the FIX session handshake through LOGON messages was performed...
		// the client is now ready to start sending/receiving messages
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleConnectionTerminated() {
		// the socket connection was broken/terminated
		
		// do anything you want to do here...
	}
	
	@Override
	protected void prepareLogon(FixMessage outFixMsg) {
		super.prepareLogon(outFixMsg);
		
		// add/remove any tag here... probably not necessary because the base
		// class is already adding all the required tags
	}
	
	@Override
	protected void handleFixApplicationMessage(FixMessage fixMsg, boolean possDupe) {
		
		// do what you have to do with this FIX application message
		
		if (fixMsg.checkType(MsgTypes.NewOrderSingle)) {
			
			double price = fixMsg.getDouble(Price);
			System.out.println("Read price: " + price);
			
			CharSequence clOrdID = fixMsg.getString(ClOrdID);
			System.out.println("Read ClOrdID: " + clOrdID);
			
			char mySpecialTag = fixMsg.getChar("MySpecialTag");
			System.out.println("Read MySpecialTag: " + mySpecialTag);
			
			FixGroup fixGroup = fixMsg.getGroup(NoPartyIDs);
			System.out.println(fixGroup.getNumberOfElements());
		 
			// you can also print the whole fix group for debugging purposes:
			System.out.println(fixGroup);
		         
	        Iterator<FixGroupElement> iter = fixGroup.iterator();
		         
	        while(iter.hasNext()) {
	             
	            FixGroupElement elem = iter.next();
	            
	            CharSequence partyID = elem.getString(PartyID);
	            System.out.println(partyID);
	            
	            char partyIDSource = elem.getChar(PartyIDSource);
	            System.out.println(partyIDSource);
	            
	            int partyRole = elem.getInt(PartyRole);
	            System.out.println(partyRole);
	             
	            // you can also print the whole element for debugging purposes:
	            System.out.println(elem);
	        }
		}
	}		
	
	public static void main(String[] args) {
		
		NioReactor nio = NioReactor.create();
		
		MapConfiguration config = new MapConfiguration();
		
		// the FIX version to use (4.2 = 42, 4.4 = 44, and so on)
		// REQUIRED
		config.add("fixVersion", 44);
		
		// the senderComp of the fix client
		// REQUIRED
		config.add("senderComp", "testClient");
		
		// NOT required:
		// config.add("targetComp", "testAcct"); // the TargetCompID tag
		// config.add("senderSub", "xxx"); // the SenderSubID tag
		// config.add("targetSub", "yyy"); // the TargetSubID tag
		// config.add("senderLoc", "aaa"); // the SenderLocationID tag
		// config.add("heartbeat", "20"); // the HeartBtInt tag (defaults to 30 seconds)
		// config.add("encrypt", "1"); // the EncryptMethod tag (defaults to 0)
		// config.add("password", "mypass"); // the RawData tag (the RawDataLength tag will also be included)
		
		// print all messages received and sent to STDOUT for debugging purposes
		// (default is false)
		config.add("debugMessages", true);
		
		// send a ResetSeqNumFlag=Y on Logon to force a sequence reset on both sides (inbound and outbound)
		// (default is false)
		config.add("forceSeqReset", true);
		
		// persist sequences on a file across client restarts (so you don't lose your current sequences)
		// (default is false)
		config.add("persistSequences", false);
		
		// turn on asynchronous audit logging
		// (default is false)
		config.add("auditLog", false);

		Client client = new SimpleFixApplicationClient(nio, "localhost", 45451, config);
		client.open();
		
		nio.start();
	}
}

The server:

package com.coralblocks.coralfix.sample;

import static com.coralblocks.coralfix.FixTags.*;

import com.coralblocks.coralfix.FixConstants.MsgTypes;
import com.coralblocks.coralfix.FixGroup;
import com.coralblocks.coralfix.FixMessage;
import com.coralblocks.coralfix.FixTags;
import com.coralblocks.coralfix.server.FixApplicationServer;
import com.coralblocks.coralreactor.client.Client;
import com.coralblocks.coralreactor.nio.NioReactor;
import com.coralblocks.coralreactor.util.Configuration;
import com.coralblocks.coralreactor.util.MapConfiguration;

public class SimpleFixApplicationServer extends FixApplicationServer {

	public SimpleFixApplicationServer(NioReactor nio, int port, Configuration config) {
	    
		super(nio, port, config);
	    
	    // Add any extra / non-standard fix tags from your spec so you can work with the name instead of the id
	    FixTags.addTag("MySpecialTag", 3434);
    }
	
	@Override
	protected void handleOpened() {
		// sever was opened and will now accept connections from clients...
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleClosed() {
		// server was closed, all clients were disconnected and it will not accept any connection...
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleConnectionEstablished(Client client) {
		// socket connection was established for a client but no login handshake was done yet
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleConnectionOpened(Client client) {
		// the FIX session handshake through LOGON messages was performed for this client...
		// the client is now ready to start sending/receiving messages
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleConnectionTerminated(Client client) {
		// the socket connection was broken/terminated for this client
		
		// do anything you want to do here...
	}
	
	@Override
	protected void handleFixApplicationMessage(Client client, FixMessage fixMsg, boolean possDupe) {
		
		// do whatever you want to do with the application message received from this client...
		
		FixMessage outFixMsg = getOutFixMessage(client, MsgTypes.NewOrderSingle);
		
		// Add the tags you want to outFixMsg
		// NOTE: there is no need to add any session-level tags like MsgSeqNum, SenderCompID, etc.
		outFixMsg.add(Price, 50.34);
		outFixMsg.addTimestamp(TransactTime, System.currentTimeMillis());
		outFixMsg.add(ClOrdID, "A123");
		outFixMsg.add("MySpecialTag", 'Y');
		
		// Add a repeating group
		FixGroup partyIds = outFixMsg.createGroup(NoPartyIDs);
		partyIds.nextElement().add(PartyID, "BLAH").add(PartyIDSource, 'D').add(PartyRole, 54);
		partyIds.nextElement().add(PartyID, "TRD").add(PartyIDSource, 'D').add(PartyRole, 36);
		partyIds.nextElement().add(PartyID, "FOO").add(PartyIDSource, 'D').add(PartyRole, 7);
		
		// Send out the message to the client
		send(client, outFixMsg);
	}
	
	public static void main(String[] args) {

		NioReactor nio = NioReactor.create();
		
		MapConfiguration config = new MapConfiguration();
		
		// the FIX version to use (4.2 = 42, 4.4 = 44, and so on)
		// REQUIRED
		config.add("fixVersion", 44);
		
		// print all messages received and sent to STDOUT for debugging purposes
		// (default is false)
		config.add("debugMessages", true);
		
		// accept as the client inbound sequence whatever sequence I receive in the first message coming from the client
		// (default is false)
		config.add("acceptInboundSeqFromClient", false);
		
		FixApplicationServer server = new SimpleFixApplicationServer(nio, 45451, config);
		server.open();

		nio.start();
	}
}

Server log output:

13:34:08.139710-INFO SimpleFixApplicationServer-45451 Server opened! host=0.0.0.0 port=45451
13:34:08.143470-INFO NioReactor Reactor started! type=OptimumNioReactor impl=KQueueSelectorImpl
13:34:15.360907-INFO TcpServerClient-/127.0.0.1:51230 Client connection established!
IN <= BeginString(8)=FIX.4.4 BodyLength(9)=67 MsgType(35)=Logon("A") MsgSeqNum(34)=1 SenderCompID(49)=testClient LastMsgSeqNumProcessed(369)=0 SendingTime(52)=20150427-17:34:15.485 EncryptMethod(98)=0 HeartBtInt(108)=10 CheckSum(10)=146
13:34:15.499388-INFO TcpServerClient-/127.0.0.1:51230 Client connection opened!
13:34:15.499710-INFO SimpleFixApplicationServer-45451 Client logged to fix session! client=TcpServerClient-/127.0.0.1:51230 heartbeat=10 isResetSeqNumFlag=false
OUT => BeginString(8)=FIX.4.4 BodyLength(9)=62 MsgType(35)=Logon("A") MsgSeqNum(34)=1 TargetCompID(56)=testClient LastMsgSeqNumProcessed(369)=1 SendingTime(52)=20150427-17:34:15.489 HeartBtInt(108)=10 CheckSum(10)=XXX
OUT => BeginString(8)=FIX.4.4 BodyLength(9)=55 MsgType(35)=Heartbeat("0") MsgSeqNum(34)=2 TargetCompID(56)=testClient LastMsgSeqNumProcessed(369)=1 SendingTime(52)=20150427-17:34:25.502 CheckSum(10)=XXX
IN <= BeginString(8)=FIX.4.4 BodyLength(9)=55 MsgType(35)=Heartbeat("0") MsgSeqNum(34)=2 SenderCompID(49)=testClient LastMsgSeqNumProcessed(369)=2 SendingTime(52)=20150427-17:34:25.506 CheckSum(10)=101
OUT => BeginString(8)=FIX.4.4 BodyLength(9)=55 MsgType(35)=Heartbeat("0") MsgSeqNum(34)=3 TargetCompID(56)=testClient LastMsgSeqNumProcessed(369)=2 SendingTime(52)=20150427-17:34:35.503 CheckSum(10)=XXX
IN <= BeginString(8)=FIX.4.4 BodyLength(9)=55 MsgType(35)=Heartbeat("0") MsgSeqNum(34)=3 SenderCompID(49)=testClient LastMsgSeqNumProcessed(369)=3 SendingTime(52)=20150427-17:34:35.509 CheckSum(10)=107

Client log output:

13:34:15.343527-INFO SimpleFixApplicationClient-localhost:45451 Client opened! sequence=1 session=null
13:34:15.356203-INFO NioReactor Reactor started! type=OptimumNioReactor impl=KQueueSelectorImpl
13:34:15.485377-INFO SimpleFixApplicationClient-localhost:45451 Connection established!
13:34:15.485712-INFO SimpleFixApplicationClient-localhost:45451 Connected to server. Sending logon message. outboundSeq=1 logonAttempts=1
OUT => BeginString(8)=FIX.4.4 BodyLength(9)=67 MsgType(35)=Logon("A") MsgSeqNum(34)=1 SenderCompID(49)=testClient LastMsgSeqNumProcessed(369)=0 SendingTime(52)=20150427-17:34:15.485 EncryptMethod(98)=0 HeartBtInt(108)=10 CheckSum(10)=XXX
IN <= BeginString(8)=FIX.4.4 BodyLength(9)=62 MsgType(35)=Logon("A") MsgSeqNum(34)=1 TargetCompID(56)=testClient LastMsgSeqNumProcessed(369)=1 SendingTime(52)=20150427-17:34:15.489 HeartBtInt(108)=10 CheckSum(10)=177
13:34:15.504399-INFO SimpleFixApplicationClient-localhost:45451 Received logon response from server! sequence=1 hearbeat=10
13:34:15.504864-INFO SimpleFixApplicationClient-localhost:45451 Connection opened!
IN <= BeginString(8)=FIX.4.4 BodyLength(9)=55 MsgType(35)=Heartbeat("0") MsgSeqNum(34)=2 TargetCompID(56)=testClient LastMsgSeqNumProcessed(369)=1 SendingTime(52)=20150427-17:34:25.502 CheckSum(10)=094
OUT => BeginString(8)=FIX.4.4 BodyLength(9)=55 MsgType(35)=Heartbeat("0") MsgSeqNum(34)=2 SenderCompID(49)=testClient LastMsgSeqNumProcessed(369)=2 SendingTime(52)=20150427-17:34:25.506 CheckSum(10)=XXX
IN <= BeginString(8)=FIX.4.4 BodyLength(9)=55 MsgType(35)=Heartbeat("0") MsgSeqNum(34)=3 TargetCompID(56)=testClient LastMsgSeqNumProcessed(369)=2 SendingTime(52)=20150427-17:34:35.503 CheckSum(10)=098
OUT => BeginString(8)=FIX.4.4 BodyLength(9)=55 MsgType(35)=Heartbeat("0") MsgSeqNum(34)=3 SenderCompID(49)=testClient LastMsgSeqNumProcessed(369)=3 SendingTime(52)=20150427-17:34:35.509 CheckSum(10)=XXX