FIX Message

FIXMessage represents a message in FIX protocol.

 

Creating a FIX Message

In QWFIX, a FIXMessage has to be created by a FIXSession. A FIXMessage cannot live without a FIXSession anyway. Also, a FIXMessage needs to reference a FIXSchema, which is also from the FIXSession. Actually, a FIXMessage is directly linked to a FIXSessionPersistence instead of a FIXSession. Remember each FIXSessionPersistence represents an intraday FIXSession communication? It is natural that people need to know which intraday session communication a FIXMessage actually belongs to.

In short, a FIXMessage directly references a FIXSessionPersistence and thus indirectly references the FIXSession and FIXSchema.

FIXSession.createMessage() is the method that creates the FIX message. For FIX sessions that support new transport protocol (5.0+), more than one schemas may be supported per session. In that case application can find out the index of the schema in FIXSession.getSchemas(), and then use that index to create a FIX message that uses that specific schema. If no index is specified, the system will be the default message schema user specified in the "Schema Editor" GUI tool in "QWFIX Enterprise Manager".

FIXMessage may contain FIXMessageRepeatingGroup. FIXMessageRepeatingGroup is a collection of FIXMessageRepeatingInstance.

FIXMessageRepeatingInstance and FIXMessage share a common base type FIXMessageComponent.

FIXMessage has a getDirection() property that indicates the direction of message (Incoming or Outgoing).

The following code block demonstrates how to set repeating group in a message:

    // The following code creates a "NewOrderSingle" FIX message for 20000 shares of IBM.
    // The we use allocation repeating group to allocate 10000 share to account "100001"
    // and another 10000 shares to account "100002"
    FIXMessage newOrder = session.createMessage(MsgTypes.NewOrderSingle);
    newOrder.setValue(Tags.Symbol, "IBM");
    newOrder.setValue(Tags.OrderQty, 20000);
    
    // Create the allocation repeating group
    FIXMessageRepeatingGroup allocGroup = newOrder.createRepeatingGroup(Tags.NoAllocs);
    
    // Create the first allocation repeating instance and add it to group
    FIXMessageRepeatingInstance alloc1 = newOrder.createRepeatingInstance(Tags.NoAllocs);
    alloc1.setValue(Tags.AllocID, "100001");
    alloc1.setValue(Tags.AllocQty, 10000);
    allocGroup.add(alloc1);
    
    // Create the second allocation repeating instance and add it to group
    FIXMessageRepeatingInstance alloc2 = newOrder.createRepeatingInstance(Tags.NoAllocs);
    alloc2.setValue(Tags.AllocID, "100002");
    alloc2.setValue(Tags.AllocQty, 10000);
    allocGroup.add(alloc2);
    
    // Set repeating group to message
    newOrder.setRepeatingGroup(allocGroup);

 

Data Types

.Net Style of DateTime and TimeSpan

QWFIX_J is implemented in pure Java. The naming convention and design patterns are all Java style. Probably the only thing not "Java style" is the .Net style date and time related classes.

Because QWFIX_J is cross platform, we need to maintain 100% interoperability among different programming languages and platforms. During the design time we choose to use .Net style date and time across all implementations.

In QWFIX_J we introduced two classes, DateTime and TimeSpan. Those two classes behave exactly the same as the corresponding classes in .Net framework. In addition, we added the conversion methods in DateTime class to convert from and back to the Java Date class.

Native Data Types

QWFIX.J uses the following native data types to representing fields in a FIXMessage, int, double, boolean, char, String, DateTime, TimeSpan, FIXMultipleValueString, byte[], FIXTZTimestamp and FIXTZTimeOnly. Please read the next paragraph for details about the type conversion rules.

Because DataTime and TimeSpan can both be represented as a long integer, we added long integer type in the FIXMessage getValue and setValue methods, only for date and time related fields. Also we added corresponding static methods in DateTime and TimeSpan classes to operate on long integer instead of a class instance to avoid unnecessary object allocation and garbage collection.

Date and Time Related Optimization

Code snippet below demonstrates how to get and set DateTime value on a FIXMessage without creating an object.

    message.setValue(Tags.TransactTime, DateTime.getUtcNowAsBinary());
    long transactTimeBinary = message.getDateTimeBinaryValue(Tags.TransactTime);
    int year = DateTime.getYear(transactTimeBinary);
    int month = DateTime.getMonth(transactTimeBinary);
    int day = DateTime.getDay(transactTimeBinary);

 

Accessing Fields in a FIXMessage

Developer may read the value of fields inside a FIXMessageComponent. For more information please read the getValue or SetXXXXValue methods in FIXMessageComponent.

QWFIX implements smart field access type checking rules. The goal is to keep maximum compatibility without sacrificing safety. For example, the field "OrderQty" is an integer in FIX 4.0 but has been changed to Float (QTY) type in FIX 4.2+. In QWFIX SDK, we make sure setValue(int) works on a field with type "float". Also we make getIntValue conditionally work only if the float value doe not have fractions.

Similar rule also applies to "Boolean" type and "char" or "string".

The following table demonstrates the details about field access type conversion rules.

Native Type int double char DateTime TimeSpan String MultipleValueString byte[] (data) TZTimestamp TZTimeOnly
Set/Get IntValue
setValue is OK.
GetIntValue will throw an exception if the value is not an integer (has fractions).

setValue is OK.
GetIntValue will throw an exception if the value is not a string representation of a valid integer.
Set/Get FloatValue
GetValue is OK.

SetFloatValue will throw an exception if the value is not an integer (has fractions).

setValue is OK.
getFloatValue will throw an exception if the value is not a string representation of a valid floating point number.
Set/Get BooleanValue
setValue is OK.
GetBooleanValue will throw an exception if value is neither 'Y' nor 'N'.

setValue is OK.
GetBooleanValue will throw an exception if value is neither 'Y' nor 'N'.

setValue is subject to validation rule.
GetBooleanValue will throw an exception if value is neither 'Y' nor 'N'.
Set/Get CharValue
setValue is OK.
GetCharValue will throw an exception if string length is not 1.

setValue is subject to validation rule.
GetCharValue will throw an exception if string length is not 1.
Set/Get DateTimeValue
setValue is OK.
GetDateTimeValue will throw an exception if string is not a valid Timestamp.
Set/Get TimeOnlyValue
setValue is OK.
GetTimeOnlyValue will throw an exception if string is not a valid time value.
Set/Get StringValue
setValue will throw an exception if the input string is not a valid integer.
GetStringValue is OK.

setValue will throw an exception if the input string is not a valid floating point.
GetStringValue is OK.

setValue will throw an exception if the length of the input string is not 1.
GetStringValue is OK.

setValue will throw an exception if the input string is not a valid Timestamp.
GetStringValue is OK.

setValue will throw an exception if the input string is not a valid time.
GetStringValue is OK.

setValue is subject to validation rule.
GetStringValue is OK.

setValue will convert string into byte array using schema's MessageEncoding
GetStringValue will convert byte array into string using schema's MessageEncoding

setValue is subject to validation rule.
GetStringValue is OK.

setValue is subject to validation rule.
GetStringValue is OK.
Set/Get MultipleValueStringValue
setValue is OK.
GetMultipleValueStringValue is subject to validation rule.

setValue will convert string into byte array using schema's MessageEncoding
GetMultipleValueStringValue is subject to validation rule.
Set/Get DataValue
Set/Get TZTimestamp
setValue is OK.
GetTZTimestamp will throw an exception if string is not a valid value.
Set/Get TZTimeOnly
setValue is OK.
GetTZTimeOnly will throw an exception if string is not a valid value.

 

Field Existence Test

Some fields defined in a FIX message schema may be optional. QWFIX provides methods in FIXMessage class that combines field retrieving together with existence test.

There are two ways to detect that a field is not defined in a message instance. Both are demonstrated in code snippet below. We highly recommend the second one, which avoids throwing exceptions by using a default value.

    // This method will throw an exception if "Price" field is not defined in message.
    double price = message.getFloatValue(Tags.Price)
    // This method will return the default value if "Price" field is not defined in message.
    // In this case the default value is the second parameter (0).
    price = message.getFloatValue(Tags.Price, 0);

Tips: The default value for string value should be "null". The default value of char value can be Character.MAX_VALUE. User can choose an apparently invalid value as default value for other type of fields based on the business logic.

 

Precision Concerns about Float and Timestamp (or TimeOnly, TZTimestamp, TZTimeOnly) Values

Cautions should be used while dealing with the following types: float (.Net native double precision), Timestamp (.Net DateTime) or TimeOnly (.Net TimeSpan). FIX specification states that "all float fields must accommodate up to fifteen significant digits". QWFIX complies with the specification. However, the float data used in setValue() may still be different than the value later retrieved using getFloatValue method, only "fifteen significant digits" rule will be followed. The reason is that the float point value has to converted into string during encoding and converted back during decoding process. Precision may be lost during that process.

It's a general rule that programmer should never ever directly evaluate the equality of two floating point numbers. The should use "Math.Abs(f1 - f2) < Double.Epsilon" instead.

The same applies to DateTime and TimeSpan data types, too. Never directly evaluate the equality of those types of data!

 

Sending a FIX Message

A FIXMessage can be sent on the same FIXSession that created the message, by making a call to FIXSession.sendMessage(). The "alwaysQueue" parameter controls the behavior when the underlying is down. If the value is true the session will queue the message and try to send the message when the connection recovers; otherwise an exception will be thrownout and the message will not be sent.

Note, FIXSession.sendMessage() cannot guarantee the delivery of the message. If the connection is down for the rest of the day the message will stay in the message queue forever. FIX protocol delivers messages on "best effort" basis, as with our QWFIX system. FIXSession.sendMessage() only throws exception if it knows "for sure" that the connection is down. So if your recipient is receiving messages at a slower rate than you are sending them, the message will be queued on your side no matter which mathod is called.

Even if one chooses to queue the message FIXSession.sendMessage() may still throw out exceptions, if the session has not been started or the session has expired, or the message is not created by the same session, or the message doesn't pass validation (for example, required field is missing).

 

FIX Message Token and FIX Message Cache

FIXSession.sendMessage() returns a "FIX message token". In QWFIX_J the "FIX message token" is a long integer used to access the message. Note in QWFIX.Net the message token is a value type (structure), which is also 8 bytes long on both 32 bit and 64 bit platforms.

FIX message token is used to uniquely identify every FIX message during run time. QWFIX_J provides helper function defined in FIXMessageTokenHelper class to get information associated with a FIX message token. For example, FIXMessageTokenHelper.getPersistence() can get the FIXSessionPersistence associated with the message token without retrieving the actual FIX message.

QWFIX implements proprietary in memory FIX message storage algorithm and a very efficient message caching system. QWFIX run-time is able to cache 2 million to 5 million FIX messages per gigabyte of memory for typical equity trading. The cache settings can be changed programmatically (see FIXEngine.initMessageCache()).

Developers shouldn't keep references to any instance of FIXMessage. Instead, they should just keep the message token and use FIXEngine.getMessage() to get the message on-demand. If the message is cached then getting message is no more than retrieving an object from an array.

The FIXMessage retrieved by making call to FIXEngine.getMessage() cannot be modified. First of all, it doesn't make any sense to modify a message you already sent or received. Secondly, It may not even be legal to do so.

The FIXMessage retrieved by making call to FIXEngine.getMessage() doesn't necessarily have to be identical to the original message passed to FIXSession.sendMessage(). First of all, some fields may be re-ordered. Secondly, some new fields may be added (such as MsgSeqNum). Thirdly, some float fields or data/time fields may contain a slightly different value (see "Precision Concerns" two chapters above).

 

FIX Message Sequence Number

As we mentioned above, the FIXSession.sendMessage() operation will put the message into the outgoing queue. When the message is goning to be sent "over the wire" depends on the network condition, as well the performance of the local application and the remote application. QWFIX has one of the most efficient FIX session level transport implementation. It usually takes far less than 0.1 millisecond to deliver the message to the recipient.

As long as we use a queueing mechanism (as QWFIX does), we might not be able to know the actual FIX MsgSeqNum even after the FIXSession.sendMessage() returns. In FIXSession.sendMessage(), QWFIX assign an internal sequence number to every outgoing FIX message. That number should be always different than the actual MsgSeqNum seen by the recipient if the message ever reaches to the recipient. The MsgSeqNum encoded in the FIXMessage is the internal sequence number.

In order to get the "real" MsgSeqNum assigned to the message (the number actually seen by recipient), application needs to make a call to FIXSession.getSentMessageInfo(). Note the returned "realMsgSeqNum" may be "-1" if the message has not been sent to the recipient.

Usually developer should not care about the sequence number of messages at all. QWFIX takes care of it very well so that developers only need to concentrate on the high level business logics.

 

Performance Tuning with Object Cache

Since QWFIX 2.0 a new method related to FIXMessage is added in order to improve system performance, FIXMessageComponent.free. That method can be called on an instance of both a FIXMessage or a FIXMessageRepeatingInstance. What that method does is that it will return the created FIXObject object instance into an internal object cache, so that the next time FIXSession.createMessage is called the object will be retrieved from the internal object cache and returned. The FIXMessageComponent.free method helps to improve the system performance by reducing the unnecessary memory allocation and subsequent garbage collection. If the free method is not called, the message is just discarded and will be subject to garbage collection. However, if the free method is called, the specific reference should be discarded immediately after the call returns.