Serialized FIX Message
Although Message class is designed for high-performance duty, each concept has its bounds and bottlenecks. The serialization operation, that is done by the Session each time when an instance of Message is sent to counterparty, is quite a heavy-weight operation, thus it has negative influence on a general performance. To satisfy the needs of low latency trading, OnixS FIX Engine offers the new concept of editable serialized FIX message, exposed as the SerializedMessage class. This class provides similar to the Message functionality. It allows accessing and updating FIX fields to be available in the message. However, being pre-serialized, it eliminates the necessity of converting the structured memory presentation into a 'tag=value' form, when the message is actually sent. In such way, it increases the overall performance.
Constructing SerializedMessage
SerializedMessage instance can be constructed either from a 'tag=value' form or a regular Message or regular FlatMessage class instances. The following code shows the creation of a serialized order (MsgType=D) from 'tag=value' form:
const string RawOrder = "8=FIX.4.2\u00019=00\u000135=D\u000149=Sender\u000156=Target" +
"\u000134=0\u000152=99990909-17:17:17.777\u000154=1\u000155=A001" +
"\u000111=BUY000000001\u000138=1000\u000140=2\u000144=1001.000" +
"\u000159=3\u0001117=A001\u000146=A001\u000110=000\u0001";
// Constructs the serialized message from 'tag=value' form
SerializedMessage serializedOrder = new SerializedMessage(RawOrder);
Also SerializedMessage instance can be constructed from 'tag=value' form without session-level details, in this case required fields will be added during the construction:
const string RawOrderWithoutSessionDetails =
"54=1\u000155=A001\u000111=BUY000000001\u000138=1000" +
"\u000140=2\u000144=1001.000\u000159=3\u0001117=A001\u000146=A001\u0001";
// Constructs the serialized message from 'tag=value' form without session-level details
SerializedMessage serializedOrder = new SerializedMessage(ProtocolVersion.Fix42, MsgType.NewOrderSingle, "Sender", "Target", RawOrderWithoutSessionDetails );
The following code shows the creation of a serialized order (MsgType=D) from the previously filled Message instance:
// Order as an in-memory structured FIX message.
Message order = new Message(MsgType.NewOrderSingle, ProtocolVersion.Fix44);
// Fills the order in a regular way.
order.Set(Tag.ClOrdID, "ClientOrderID");
order.Set(Tag.OrderQty, 1000);
// Constructs the serialized message from the structured instance.
SerializedMessage serializedOrder = new SerializedMessage(order);
// Now the structured instance is not needed any more.
Note
In contrast to the Message , which is blank by default when it is constructed, the SerializedMessage must be constructed from the fully defined FIX message. Also, SerializedMessage does not support adding or removing fields from the message body. The class provides only the ability to retrieve field values and change them.
Accessing FIX Fields
Once the instance of SerializedMessage class is constructed, its fields can be manipulated in a similar way, as in case of Message . SerializedMessage class interface exposes two ways of accessing fields, using temporary references and permanent keys. Temporary references, represented by SerializedFieldRef class and permanent keys, are of the SerializedFieldKey type. Whatever way is used to access a field, a temporary reference to the field must be obtained first. The SerializedMessage class exposes the Find(int) method for that purposes. If lookup succeeds, it returns a valid reference instance for a further use:
SerializedFieldRef clOrdIdRef = serializedOrder.Find(Tag.ClOrdID);
// Ensures whether the instance refers to a field.
if (!clOrdIdRef.IsValid)
throw new Exception("Cannot find ClOrdID field in given Order.");
// Once the reference is obtained, it can be used to update the field value:
serializedOrder.Set(ref clOrdIdRef, "NewClientOrderID");
Note
An important aspect of a reference is that it remains valid only while manipulating it's field value. If another field is updated, the reference may become invalid. References also are invalidated when the instance of SerializedMessage class is sent to the counterparty via Send(SerializedMessage) . For this reason, do not use multiple references at the same time.
For a continuous and non-destructive modification of a FIX field value, concept of a serialized field key is exposed. In contrast to temporary field references, keys remain constant for the entire life of a SerializedMessage instance. Changing field values does not invalidate keys, thus multiple fields can be updated without affecting keys. Keys are unique in bounds of a single serialized message. The following code demonstrates how a key can be obtained from a temporary reference:
SerializedFieldRef clOrdIdRef = serializedOrder.Find(Tag.ClOrdID);
if (!clOrdIdRef.IsValid)
throw new Exception("Cannot find ClOrdID field in given Order.");
SerializedFieldKey clOrdIdKey = serializedOrder.AllocateKey(clOrdIdRef);
// Once the key is obtained, the field value can be accessed and modified:
serializedOrder.Set(clOrdIdKey, "YetAnotherCliendOrderID");
Note
Keys are similar to the field tags with only one difference. Keys are constant in bounds of a single instance of the SerializedMessage class. However, keys may differ for the same field for different instances of the SerializedMessage class. Also, tags are statically defined, whereas keys are dynamically allocated by each particular instance of the SerializedMessage class. It is recommended to allocate only a few keys because not doing so can increase the time of setting field values.
Also the SerializedMessage class provides the ability to access fields with a same tag from different entries of a serialized repeating group. It can be performed with the overloaded Find(int, SerializedFieldRef) method. The second parameter indicates the SerializedFieldRef value after which the search will be performed. Accordingly you can find all field values with a same tag and allocate keys for the further access. The following code demonstrates the access to fields with a same tag:
// Constructs the serialized message from the structured instance which has the repeating group.
SerializedMessage massQuoteSerializedMsg = new SerializedMessage(massQuoteMsg);
List<SerializedFieldKey> partyIdFieldKeys = new List<SerializedFieldKey>();
SerializedFieldRef partyIdFieldRef = default;
//Finds all PartyID fields
while ((partyIdFieldRef = massQuoteSerializedMsg.Find(Tag.PartyID, partyIdFieldRef)).IsValid)
{
//Allocates and saves the key for the found PartyID field
partyIdFieldKeys.Add(massQuoteSerializedMsg.AllocateKey(partyIdFieldRef));
}
Console.Write("Found " + partyIdFieldKeys.Count + " party identifiers in serialized mass quote:\n");
int partyIdCount = 0;
foreach (SerializedFieldKey key in partyIdFieldKeys)
{
Console.Write("Entry #" + (++partyIdCount) + " has PartyID=" + massQuoteSerializedMsg.Get(key) + "\n");
}
Example
// Order as an in-memory structured FIX message.
Message order = new Message(MsgType.NewOrderSingle, ProtocolVersion.Fix44);
// Fills the order in a regular way.
order.Set(Tag.ClOrdID, "ClientOrderID");
order.Set(Tag.OrderQty, 1000);
// ...
// Constructs a serialized message from the structured instance.
// Since this moment, the structured instance is not used any more.
SerializedMessage serializedOrder = new SerializedMessage(order);
// Pre-fills session-level fields that are constant during the session's lifetime: SenderCompId, TargetCompId, SenderLocationId, TargetLocationID, SenderSubID, TargetSubID.
session.PreFill(serializedOrder);
SerializedFieldRef clOrdIdRef = serializedOrder.Find(Tag.ClOrdID);
SerializedFieldRef qtyRef = serializedOrder.Find(Tag.OrderQty);
// Ensures whether both fields are found.
if (!clOrdIdRef.IsValid || !qtyRef.IsValid)
{
throw new Exception("Cannot find required fields in the Order.");
}
// Once the reference is obtained, it's time to allocate the field key to secure the fast access.
// The key remains constant for the entire message instance life-time.
// However, it refers to a particular instance ONLY.
SerializedFieldKey clOrdIdKey = serializedOrder.AllocateKey(clOrdIdRef);
SerializedFieldKey qtyKey = serializedOrder.AllocateKey(qtyRef);
var iteration = 1000;
var qty = int.Parse(serializedOrder.Get(qtyKey));
while (iteration-- > 0)
{
// Assigns a new value to the ClOrdID field.
serializedOrder.Set(clOrdIdKey, "20120522-16:48:38.707");
// Assigns a new order quantity.
serializedOrder.Set(qtyKey, qty += 1000);
// Sends the updated order to the counterparty.
session.Send(serializedOrder);
// Extracts the sequence number assigned to the sent message.
string seqNumberStr = serializedOrder.Get(KnownSerializedFieldKeys.SeqNum);
Console.WriteLine($"Order sent under #{seqNumberStr}.");
}