QuickfixJ: Broker OMS

In this tutorial, we are creating a simple Order Management System (OMS) template for a broker system.  The broker will utilize the MarketIntelligence class created in the last tutorial to simulate a market data source for bid and ask prices for securities on the Hong Kong Stock Exchange.  In order to implement the OMS with QuickfixJ, we will need to create two java classes, one for implementing a QuickfixJ application, and one for launching the application and supplying parameters to the application.  In this tutorial, our broker is Lan Fong Securities, an imaginary, boutique brokerage firm based in Hong Kong, and the client is Baker Street Capital, also an imaginary, London-based boutique investment firm.

Implementing a QuickfixJ application requires two steps.  The first step is to create a class deriving from quickfix.MessageCracker and implementing the quickfix.Application interface. To implement the interface, your class needs to override the following methods:

  • void onCreate(SessionID sessionID)
  • void onLogon(SessionID sessionID)
  • void onLogout(SessionID sessionID)
  • void toAdmin(Message msg, SessionID sessionID)
  • void fromAdmin(Message msg, SessionID sessionID)
  • void toApp(Message msg, SessionID sessionID)
  • void fromApp(Message msg, SessionID sessionID)

For more details, refer to the QuickfixJ documentation.  Lets just jump right in with the application class for the broker:

import java.io.*;
import java.util.*;

// Quickfix Exceptions
import quickfix.DoNotSend;
import quickfix.FieldConvertError;
import quickfix.ConfigError;
import quickfix.IncorrectDataFormat;
import quickfix.IncorrectTagValue;
import quickfix.FieldNotFound;
import quickfix.UnsupportedMessageType;
import quickfix.SessionNotFound;
import quickfix.RejectLogon;

// Quickfix Data Types
import quickfix.SessionID;
import quickfix.Message;
import quickfix.Session;
import quickfix.SessionSettings;
import quickfix.FixVersions;
import quickfix.MessageUtils;
import quickfix.field.*;

public class Application extends quickfix.MessageCracker
                         implements quickfix.Application
{
    private MarketIntelligence mi = null;

    public Application(SessionSettings settings)
        throws ConfigError, FieldConvertError
    {
        mi = new MarketIntelligence();
        String ask = mi.getAsk("0001");
        System.out.println(ask);
    }

    // ===================================================
    // Overriden quickfix.MessageCracker onMessage()
    // ===================================================
    public void onMessage(quickfix.fix42.NewOrderSingle order,
                          SessionID sessionID)
        throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue
    {
    }

    // ===================================================
    // quickfix.Application interface implementation
    // ===================================================

    // Called when quickfix creates a new session
    public void onCreate(SessionID sessionID)
    {
    }

    // notification that a valid logon has been established
    // with the countery party
    public void onLogon(SessionID sessionID)
    {
    }

    // notification that a FIX session is no longer online
    public void onLogout(SessionID sessionID)
    {
    }

    // allows for peaking at msgs from this apps FIX engine to
    // the counter party
    public void toAdmin(Message msg, SessionID sessionID)
    {
    }

    // callback notify for when admin msgs are received by FIX from
    // the counter party
    public void fromAdmin(Message msg, SessionID sessionID)
        throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,
               RejectLogon
    {
    }
   // callback for app messages this app  send to the counter party
    public void toApp(Message msg, SessionID sessionID)
        throws DoNotSend
    {
    }

    // all app level requests comes through here
    public void fromApp(Message msg, SessionID sessionID)
        throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,
               UnsupportedMessageType
    {
        // call base class message parser
        crack(msg, sessionID);
    }
}

In the constructor, a reference to a MarketIntelligence object is created, and we get the ask price for symbol 0001 just for testing.  Six of the seven interface methods are implemented but left blank as they are not used yet.  In the fromApp() method, the crack() method inherited from quickfix.MessageCracker is called.

The next step is to create a java class that acts as a QuickfixJ acceptor, a software component that waits for clients, called initiators, to connect and create a new FIX session.  This class is also responsible for passing parameters to the application class.  Again, it is probably easier to just jump into the code.

import java.io.*;
import java.util.*;

// Quickfix Exceptions
import quickfix.ConfigError;
import quickfix.FieldConvertError;
import quickfix.RuntimeError;

// Quickfix Data Types
import quickfix.SessionID;
import quickfix.SessionSettings;
import quickfix.SocketAcceptor;
import quickfix.mina.acceptor.DynamicAcceptorSessionProvider;
import quickfix.mina.acceptor.DynamicAcceptorSessionProvider.TemplateMapping;
import quickfix.DefaultMessageFactory;
import quickfix.FileStoreFactory;
import quickfix.LogFactory;
import quickfix.ScreenLogFactory;
import quickfix.MessageFactory;
import quickfix.MessageStoreFactory;

public class LanFongOMS
{
    private final SocketAcceptor acceptor;

    public LanFongOMS(SessionSettings settings)
        throws ConfigError, FieldConvertError
    {
        Application app = new Application(settings);
        MessageStoreFactory msgStoreFactory = new FileStoreFactory(settings);
        LogFactory logFactory = new ScreenLogFactory(true, true, true);
        MessageFactory msgFactory = new DefaultMessageFactory();

        acceptor = new SocketAcceptor(app, msgStoreFactory, settings,
                                      logFactory, msgFactory);
    }

    // Accept incoming connections
    public void start() throws RuntimeError, ConfigError
    {
        acceptor.start();
    }

    // Stop accepting incoming connections
    public void stop()
    {
        acceptor.stop();
    }

    // Program entry-point
    public static void main( String args[] )
    {
        SessionSettings sessionSettings = null;
        try{
            if( args.length != 1 ){
                System.err.println(" Usage: LanFongOMS ");
            }
            sessionSettings = loadConfiguration(args[0]);
            Iterator sectionIterator = sessionSettings.sectionIterator();
            while( sectionIterator.hasNext() ){
                SessionID id = sectionIterator.next();
                System.out.println( id.toString() );
            }

            LanFongOMS oms = new LanFongOMS(sessionSettings);
            oms.start();
            System.out.println("press  to quit");
            System.in.read();
            oms.stop();
        }
        catch( Exception ex ){
            System.err.println( "Exception!" );
            System.err.println( ex.getMessage() );
        }
        System.exit(0);
    }

    // Load customer configuration from first argument
    private static SessionSettings loadConfiguration( String configFilePath )
        throws FileNotFoundException
    {
        SessionSettings settings = null;
        try{
            InputStream inputStream = new FileInputStream(configFilePath);
            settings = new SessionSettings(inputStream);
            inputStream.close();
        }
        catch( Exception ex ){
            System.err.println( ex.getMessage() );

        }
        return settings;
    }

}

The code should be quite straight forward.  The entry point main() loads a configuration file taken as a command line argument and creates an instance of the broker’s OMS, which in turn creates a QuickfixJ application object in its constructor.  Back in main(), the acceptor is started that listens on a port number (from the config file) for incoming connections.  When the user at the controlling terminal presses the enter key, the acceptor is stopped and the OMS exists.

Before running the OMS, we need to update the ant build.xml file to support our new code.  The following modifications should suffice:

<target name=”jar” depends=”compile”>
<jar destfile=”LanFongOMS.jar” basedir=”.” includes=”**/*.class”>
<manifest>
<attribute name=”Main-Class” value=”LanFongOMS”/>
</manifest>
</jar>
</target>

<target name=”run” depends=”jar”>
<java classname=”LanFongOMS” fork=”true”>
<classpath refid=”project.class.path”/>
<arg value=”lanfong.cfg”/>
</java>
</target>

Basically, we need to update the jar target to specify the java file containing the entry point function for the JAR manifest, and we need to specify the argument for the OMS, a configuration file (lanfong.cfg).

Now that all of the code has been implemented, we must supply a config file that sets the application specific parameters for the QuickfixJ application.  One field of interest is ConnectionType, which can be set to acceptor or initiator.  In our example, the customer, Baker St. Capital, will connect to its broker, Lan Fong Securities, and thus they are initiator and acceptor, respectively.

[default]
FileStorePath=logs
ConnectionType=acceptor
StartTime=08:00:00
EndTime=19:00:00
HeartBtInt=30
SenderCompID=LANFONG
TargetCompID=BAKERST
UseDataDictionary=Y

[session]
BeginString=FIX.4.2
SocketAcceptPort=9878
SocketAcceptAddress=127.0.0.1
SocketAcceptProtocol=TCP

Other fields of interest are SenderCompID and TargetCompID, which specify the source and target of FIX messages.  Also of interest is the BeginString in the session section.  We use FIX version 4.2, but you could use any version of the FIX protocol supported by QuickfixJ, or even supported multiple versions of the FIX protocol in one config file.  Note that Quickfix sessions are identified as a combination of BeginString, SenderCompID and TargetCompID.  There are many, many more parameters that we can set, so please refer to the documentation for further information about config files.

Finally, we can bring up the Lan Fong Securities OMS:

trantor:quickfix_demo$ ant
Buildfile: build.xml

compile:
    [javac] Compiling 5 source files

jar:
      [jar] Building jar: /Users/globalengineer/Development/quickfix_demo/LanFongOMS.jar

run:
     [java] FIX.4.2:LANFONG->BAKERST
     [java] 41.59
     [java] <20100429-03:59:35, FIX.4.2:LANFONG->BAKERST, event> (Session FIX.4.2:LANFONG->BAKERST schedule is daily, 08:00:00 UTC - 19:00:00 UTC (daily, 08:00:00 UTC - 19:00:00 UTC))
     [java] <20100429-03:59:35, FIX.4.2:LANFONG->BAKERST, event> (Session state is not current; resetting FIX.4.2:LANFONG->BAKERST)
     [java] <20100429-03:59:35, FIX.4.2:LANFONG->BAKERST, event> (Created session: FIX.4.2:LANFONG->BAKERST)
     [java] Apr 28, 2010 11:59:35 PM quickfix.mina.NetworkingOptions logOption
     [java] INFO: Socket option: SocketTcpNoDelay=true
     [java] Apr 28, 2010 11:59:35 PM quickfix.mina.NetworkingOptions logOption
     [java] INFO: Socket option: SocketSynchronousWrites=false
     [java] Apr 28, 2010 11:59:35 PM quickfix.mina.NetworkingOptions logOption
     [java] INFO: Socket option: SocketSynchronousWriteTimeout=30000
     [java] Apr 28, 2010 11:59:35 PM quickfix.mina.acceptor.AbstractSocketAcceptor startAcceptingConnections
     [java] INFO: Listening for connections at /127.0.0.1:9878
     [java] press  to quit

Though our OMS does not do anything at this point, it has been brought up and is listening for incoming connections.  Notice that the quote retrieved from the market data source in the application’s class is printed out in the second line of the application output.  In the next installment of this tutorial, we’ll implement the a simple OMS for the client that acts as a QuickfixJ initiator and connects to our broker’s OMS.  Stay tuned!

QuickfixJ: data source simulator

Before getting started with QuickfixJ, in order to simulate market data on the broker side, we need some sort of data source that provides bid and ask prices on demand.  We could use historical data, but in order to keep things interesting lets use randomly generated data.  We will create an object titled MarketIntelligence, which the broker uses to generate quotes for customers.

Symbols on the Hong Kong Stock Exchange are based on numbers and not letters like NYSE, which makes the job of generating the symbols on the fly very easy.  We’ll limit the set of symbols to 1 through 1000, and prices will be updated every 200ms based on java.util.Timer timeouts.  Prices will randomly generated in the range of 0 to 500, and will be increased or decreased in small increments to simulate the movement of the security on the stock exchange.  The code for the MarketIntelligence object is sweet and simple:

import java.util.Random;
import java.util.Map;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;

public class MarketIntelligence
{
    private static final int maxPrice = 500;
    private Random generator;
    private HashMap bids;
    private HashMap asks;

    MarketIntelligence()
    {
        bids = new HashMap();
        asks = new HashMap();
        generator = new Random();

        // Generate some random symbols
        Integer basePrice;
        Double decimal, price;
        String symbol, bid_price, ask_price;
        for( int index=1; index<1000; index++)
        {
            basePrice = generator.nextInt(maxPrice);
            decimal = generator.nextDouble();
            price = basePrice.doubleValue() + decimal;
            symbol = String.format("%04d", index);
            bid_price = String.format("%.2f",price);
            ask_price = String.format("%.2f",price+0.01);
            bids.put(symbol, bid_price);
            asks.put(symbol, ask_price);
        }

        // Update market data every 200 ms
        Timer timer = new Timer();
        TimerTask task = new TimerTask(){
             public void run(){
                 update();
             }
        };
        timer.scheduleAtFixedRate( task, 0, 200 );

    }

    public String getBid(String symbol)
    {
        return bids.get(symbol);
    }

    public String getAsk(String symbol)
    {
        return asks.get(symbol);
    }

    public void update()
    {
        Double current_bid, current_ask, new_bid, new_ask;
        Double delta, price;
        Integer up_dn;
        String current_key;

        for(Map.Entry entry : bids.entrySet())
        {
            current_key = entry.getKey();
            current_bid = Double.parseDouble(bids.get(current_key));
            current_ask = Double.parseDouble(asks.get(current_key));

            delta = generator.nextDouble();
            up_dn = generator.nextInt() % 2;
            delta = up_dn.doubleValue() * delta;
            new_bid = current_bid + delta;
            new_ask = new_bid + 0.1;

            bids.put(current_key, String.format("%.2f", new_bid));
            asks.put(current_key, String.format("%.2f", new_ask));
        }
    }
}

The following simple test class shows how we can query the MarketIntelligence object to get the ask price for symbol 0001.

class TestMarketIntelligence
{
    public static void main(String args[]) throws InterruptedException
    {
        MarketIntelligence mi = new MarketIntelligence();
        while( true ){
             String ask = mi.getAsk("0001");
             System.out.println(ask);
             Thread.sleep(1000);
        }
    }
}

Executing the test module, we can verify that the ask prices are moving up and down and in not too drastic of a rise and fall (so we don’t scare our customers).

trantor:quickfix_demo$ javac -classpath . MarketIntelligence.java
trantor:quickfix_demo$ javac -classpath . TestMarketIntelligence.java
trantor:quickfix_demo$ java -cp . TestMarketIntelligence
315.60
314.84
315.60
314.43
312.53
311.86
312.02
312.77
312.93
312.20
311.81
312.69
312.09
310.41
311.16
311.47
310.88

While MarketIntelligence is not so intelligent, with a data source for the broker we can now build the broker’s order management system (OMS).  Stay tuned for more updates!

QuickfixJ: Getting started

I’m going to be writing a series of tutorials on using QuickfixJ to implement a simple financial application.  While I hope others can benefit from the tutorial, my primary purpose is so that I can actually get more comfortable with Java after having worked with C++ for my entire career.

QuickfixJ is a Java implementation of the popular open-source Quickfix C++ library that provides a FIX protocol engine for financial applications.  FIX is a simple text-based protocol built on TCP that consists of key-value fields separated by commas, where the keys are numerical indexes that have predefined and user-defined meanings.  There is also an XML version of the protocol, known as FIXML, though it is not utilized in industry as often due to the extra overhead of processing the larger XML messages.

Before we get started with QuickfixJ, we should set up our development environment.  The first step is to do yourself a favor and download Apache Ant.  While you could use makefiles or just build everything from the command line, Ant was made for building Java projects and if you plan to grow your application in any way, Ant will save you a lot of headaches a long the way.  From the Apache Ant project page you can download the libraries as-is and run it on your system.  Install Ant to your system path, and then run the following command to make sure that Ant is in your path and you’re ready to use it:

trantor:Development$ ant -v
Apache Ant version 1.7.1 compiled on October 13 2009
Buildfile: build.xml does not exist!
Build failed

Next, download QuickfixJ from the project website.  After you get everything uncompressed, take a look inside:

trantor:Development$ ls quickfixj/
LICENSE				quickfixj-msg-fix41-1.4.0.jar
bin				quickfixj-msg-fix42-1.4.0.jar
doc				quickfixj-msg-fix43-1.4.0.jar
etc				quickfixj-msg-fix44-1.4.0.jar
lib				quickfixj-msg-fix50-1.4.0.jar
quickfixj-all-1.4.0.jar		quickfixj-msg-fixt11-1.4.0.jar
quickfixj-core-1.4.0.jar	src
quickfixj-examples-1.4.0.jar	src.zip
quickfixj-msg-fix40-1.4.0.jar

QuickfixJ supports multiple versions of the FIX protocol, and you can either build with the Java library for your desired version of the protocol, or build with the quickfixj-all library that includes all versions of the protocol. Look in the lib folder too, as it contains the other required libraries.

Finally, lets set up a skeleton Ant build file that we can use to build and run our code later.  If you simply run “ant” in the working directory, it looks for a build.xml file.  We can specify a default target so that when we type “ant” on the command line, it does what we expect, just as we would with makefiles.

<project default="run">
  <path id="project.class.path">
    <!-- QuickfixJ libraries -->
    <pathelement location="/Users/globalengineer/Development/quickfixj/quickfixj-all-1.4.0.jar"/>
    <pathelement location="/Users/globalengineer/Development/quickfixj/lib/slf4j-api-1.5.3.jar"/>
    <pathelement location="/Users/globalengineer/Development/quickfixj/lib/slf4j-jdk14-1.5.3.jar"/>
    <pathelement location="/Users/globalengineer/Development/quickfixj/lib/mina-core-1.1.0.jar"/>
    <!-- Project directory-->
    <pathelement location="."/>
  </path>

  <target name="compile">
    <javac srcdir=".">
      <classpath refid="project.class.path"/>
    </javac>
  </target>

  <target name="jar depends="compile">
    <jar destfile="" basedir="." includes="**/*.class">
      <manifest>
        <attribute name="Main-Class" value=""/>
      </manifest>
    </jar>
  </target>

  <target name="run" depends="jar">
    <java classname="" fork="true">
      <classpath refid="project.class.path"/>
      <arg value="config.cfg"/>
    </java>
  </target>

  <target name="clean">
    <delete>
      <fileset dir="." includes="*.class"/>
      <fileset dir="." includes="*.jar"/>
    </delete>
  </target>
</project>

The Ant build file contains targets for compiling, creating executable jars, running jars, and cleaning up the workspace.  The classpath is specified in a single place so that we can add libraries that can be utilized by both the the compile and run targets.  Stay tuned for future tutorials on using QuickfixJ to implement a financial application.

Follow

Get every new post delivered to your Inbox.