FIXMessage represents a message in FIX protocol.
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 with new transport protocol, more than one schemas may be supported. In that case application can find out the index of the schema in FIXSession.Schemas, 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 Direction property that indicates the direction of message (Incoming or Outgoing).
The following code block demonstrates how to set repeating group in a message:
-#region Collapse it // 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); #endregion
QWFIX.Net 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.
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. |
![]() |
![]() |
![]() |
![]() |
Some fields defined in a FIX message schema may be optional. QWFIX provides methods in FIXMessageComponent class that combines field retrieving together with existence test.
For example, in order to get a float value, there are two methods: GetFloatValue and TryGetFloatValue. If the field doesn't exist, the first method will throw an exception, the second method will return a "false" value (value is returned to an out parameter).
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!
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).
FIXSession.SendMessage returns a FIXMessageToken. The FIXMessageToken is used to access the message in QWFIX libraries.
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 FIXMessageToken and use FIXEngine.GetMessage(FIXMessageToken) to get the message on-demand. If the message is cached then getting message is no more than retrieving an object from an array. FIXMessageToken is an 8 byte structure on both 32 bit and 64 bit platforms.
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).
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.
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.