Tick-to-Trade Latency Numbers using CoralMD and CoralGateway

In a previous article we measured the tick-to-trade latency using CoralFIX and CoralReactor. In this article we use wireshark and tcpdump to analyze the latency numbers of a test trading strategy that receives a market data tick through UDP using CoralMD and places a trade order through FIX using CoralGateway.

Test Details

A pseudo market data generator sends UDP packets containing market data updates (i.e. ticks). Our test trading strategy receives these packets, parses them and places a trade order using the FIX protocol on a pseudo exchange that immediately executes the order. To warm up the test strategy, we initially send 1 million market data updates. Then we proceed to send one packet every 5 seconds. To measure the latency, we use tcpdump to record the UDP packet coming in and the FIX order going out. With the packet capture file from tcpdump we then use wireshark to calculate the difference in the timestamps of the packet arriving and the FIX order leaving.


As you can see from the wireshark screenshot below, the tick-to-trade latencies are around 10-11 microseconds. If you compare these numbers with the results from the same tick-to-trade test using CoralFIX and CoralReactor, you will see that CoralMD and CoralGateway add around 1-3 microseconds on top of CoralReactor and CoralFIX.


Source Code

Market data is received through a feed implemented using CoralMD. The trade order is placed through a gateway implemented using CoralGateway. As you can see from the source code below, our test strategy receives a book update from the market data feed and proceeds to place an order using the gateway. Also note that the source code down below produces zero garbage for the GC.

package com.coralblocks.coralgateway.bench;

import com.coralblocks.coralgateway.FixGateway;
import com.coralblocks.coralgateway.FixGateway.FixOrder;
import com.coralblocks.coralgateway.Order.Side;
import com.coralblocks.coralgateway.Order.TimeToLive;
import com.coralblocks.coralgateway.Order.Type;
import com.coralblocks.coralgateway.sample.TestFixGateway;
import com.coralblocks.coralmd.Book;
import com.coralblocks.coralmd.Book.State;
import com.coralblocks.coralmd.BookListener;
import com.coralblocks.coralmd.Security;
import com.coralblocks.coralmd.feed.Feed;
import com.coralblocks.coralmd.sample.SimpleFeed;
import com.coralblocks.coralreactor.nio.NioReactor;
import com.coralblocks.coralreactor.util.MapConfiguration;

public class BenchStrategy implements BookListener {
	private final FixGateway<FixOrder> gateway;
	private final Feed feed;
	private boolean isStarted = false;
	public BenchStrategy(FixGateway<FixOrder> gateway, Feed feed) {
		this.gateway = gateway;
		this.feed = feed;
	public void open() {
	public void close() {
	public void setStarted(boolean flag) {
		this.isStarted = flag;

	///// BookListener ////
    public void onBookUpdated(Book book) {
		if (!isStarted) return;
		Security sec = book.getSecurity();
		long size = book.getBestAskSize();
		long price = book.getBestAskPrice();
		gateway.send(TimeToLive.IOC, Side.BUY, Type.LIMIT, sec.getSymbolString(), size, price);

    public void onTopOfBookChanged(Book book, 
               int oldNumberOfBidQuotes, long oldBidSize, long oldBidPrice, 
               int oldNumberOfAskQuotes, long oldAskSize, long oldAskPrice) {

    public void onStateChanged(Book book, State oldState) {
	public static void main(String[] args) {
		NioReactor nio = NioReactor.create();
		MapConfiguration gwConfig = new MapConfiguration();
		gwConfig.add("fixVersion", 44);
		gwConfig.add("senderComp", "TEST1");
		gwConfig.add("forceSeqReset", true);
		FixGateway<FixOrder> gateway = new TestFixGateway(nio, args.length > 0 ? args[0] : "localhost", 55559, gwConfig);
		MapConfiguration feedConfig = new MapConfiguration();
		feedConfig.add("host", "");
		feedConfig.add("port", 55558);
		Feed feed = new SimpleFeed(nio, feedConfig);
		BenchStrategy strategy = new BenchStrategy(gateway, feed);