OnixS C++ FIX Engine  3.24.0
API Documentation
Serialized FIX Message

Although OnixS::FIX::Message class is designed for high-performance duty, each concept has its bounds and bottlenecks. The serialization operation, that is done by the OnixS::FIX::Session each time when an instance of OnixS::FIX::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 ultra low latency trading, OnixS FIX Engine offers a new concept of an editable serialized FIX message, exposed as the OnixS::FIX::SerializedMessage class. This class provides functionality that is similar to the OnixS::FIX::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.

Constructing Serialized FIX Message

OnixS::FIX::SerializedMessage instance can be constructed either from a 'tag=value' form or a regular OnixS::FIX::Message class instance. The following code shows the creation of a serialized order (MsgType=D) from a 'tag=value' form:

const std::string RawOrder(
"8=FIX.4.2\0019=00\00135=D\00149=Sender\00156=Target\001"
"34=0\00152=99990909-17:17:17.777\00154=1\00155=A001\001"
"11=BUY000000001\00138=1000\00140=2\00144=1001.000\001"
"59=3\001117=A001\00146=A001\00110=000\001");
// Constructs the serialized message from the given 'tag=value' form
SerializedMessage order(RawOrder.data(), RawOrder.size());

Also OnixS::FIX::SerializedMessage instance can be constructed from a 'tag=value' form without session-level fields, in this case the required session-level fields will be added during the construction:

const std::string RawOrderWithoutSessionDetails(
"54=1\00155=A001\00111=BUY000000001\00138=1000\00140=2\00144=1001.000\00159=3\001117=A001\00146=A001\001");
// Constructs the serialized message from a 'tag=value' form without session-level fields
SerializedMessage serializedOrder(ProtocolVersion::FIX_42, "D", "Sender", "Target", RawOrderWithoutSessionDetails.data(), RawOrderWithoutSessionDetails.size());

The following code shows the creation of a serialized order (MsgType=D) from the previously filled OnixS::FIX::Message instance:

// Order as a FIX Message object.
Message order(FIX42::Values::MsgType::Order_Single, ProtocolVersion::FIX_42);
// Fills the order in a regular way.
order.set(FIX42::Tags::ClOrdID, "ClientOrderID");
order.set(FIX42::Tags::OrderQty, 1000);
// ...
// Constructs a SerializedMessage from the given Message instance.
// Since this moment the soruce Message instance is not needed any more.
SerializedMessage serializedOrder(order);
Attention
In contrast to the OnixS::FIX::Message, which is blank by default when it is constructed, the OnixS::FIX::SerializedMessage must be constructed from the fully defined FIX message. Also, manipulating OnixS::FIX::SerializedMessage does not suppose adding or removing fields from the message body. The class provides ability to retrieve field values and change them only.

Accessing FIX Fields

Once the instance of OnixS::FIX::SerializedMessage class is constructed, its fields can be manipulated in a similar way, as in case of OnixS::FIX::Message.

OnixS::FIX::SerializedMessage class interface exposes two ways of accessing fields: using temporary references and using permanent keys. Temporary references are represented by OnixS::FIX::SerializedFieldRef class and permanent keys are of the OnixS::FIX::SerializedFieldKey type.

Whatever way is used to access a field, a temporary reference to the field must be obtained first. The OnixS::FIX::SerializedMessage class exposes the OnixS::FIX::SerializedMessage::find method for that purposes. If lookup succeeds, it returns a valid reference instance for a further use:

SerializedFieldRef clOrdIdRef = serializedOrder.find(FIX42::Tags::ClOrdID);
// Make sure that the found instance refers to a field.
if (!clOrdIdRef)
{
throw domain_error("Cannot find the ClOrdID field in the given Order.");
}

Once a reference is obtained, it can be used to update the field value:

serializedOrder.set(clOrdIdRef, StringRef("NewClientOrderID"));
Attention
An important aspect of a reference is that it remains valid only when a manipulating value of the field reference was obtained. If another field is updated the reference may become invalid. References also invalidate when the instance of OnixS::FIX::SerializedMessage class is sent to counterparty via OnixS::FIX::Session::send. For this reason, do not use multiple references at the same type.

For a continuous and non-destructive modification of a message field value, concept of a serialized field key is exposed. In contrast to a temporary field references, keys remain constant for the entire life of the particular OnixS::FIX::SerializedMessage instance. Changing field values does not invalidate keys and, 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(FIX42::Tags::ClOrdID);
assert(static_cast<bool>(clOrdIdRef));
const SerializedFieldKey clOrdIdKey = serializedOrder.allocateKey(clOrdIdRef);

Once a key is obtained, the field value can be accessed and modified, using the same way as using temporary references:

serializedOrder.set(clOrdIdKey, StringRef("YetAnotherCliendOrderID"));
Attention
Keys are similar to the field tags with the only difference. Keys are constant in bounds of a single instance of the OnixS::FIX::SerializedMessage class. However, keys may differ for the same field for different instances of the OnixS::FIX::SerializedMessage class. Also, tags are statically defined, whereas keys are dynamically allocated by each particular instance of the OnixS::FIX::SerializedMessage class.

Also the OnixS::FIX::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 OnixS::FIX::SerializedMessage::find(OnixS::FIX::Tag, const OnixS::FIX::SerializedFieldRef&) const method. The second parameter indicates the OnixS::FIX::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(massQuoteMsg);
std::vector<SerializedFieldKey> partyIdFieldKeys;
SerializedFieldRef partyIdFieldRef;
//Finds all PartyID fields
while((partyIdFieldRef = massQuoteSerializedMsg.find(FIX44::Tags::PartyID, partyIdFieldRef))
{
//Allocates and saves the key for the found PartyID field
partyIdFieldKeys.push_back(massQuoteSerializedMsg.allocateKey(partyIdFieldRef));
}
clog << "Found " << partyIdFieldKeys.size() << " party identifiers in serialized mass quote:" << std::endl;
for (int partyIdCount = 0; partyIdCount < partyIdFieldKeys.size(); ++partyIdCount)
{
clog << "Entry #" << partyIdCount << " has PartyID=" << massQuoteSerializedMsg[partyIdFieldKeys[partyIdCount]].toString() << std::endl;
}

Example

// Order as a Message object.
Message order(FIX42::Values::MsgType::Order_Single, ProtocolVersion::FIX_42);
// Fills the order in a regular way.
order.set(FIX42::Tags::ClOrdID, "ClientOrderID");
order.set(FIX42::Tags::OrderQty, 1000);
// ...
// Constructs a serialized message from the structured instance.
// Since this moment, the structured instance is not used any more.
SerializedMessage serializedOrder(order);
SerializedFieldRef clOrdIdRef = serializedOrder.find(FIX42::Tags::ClOrdID);
SerializedFieldRef qtyRef = serializedOrder.find(FIX42::Tags::OrderQty);
// Ensures whether both fields are found.
if (!clOrdIdRef || !qtyRef)
{
throw domain_error("Cannot find the required fields in the Order.");
}
// Once a reference is obtained, it's time to allocate the field key
// to have fast access. The key remains constant for the message
// life-time. However, it refers to a particular instance ONLY.
const SerializedFieldKey clOrdIdKey = serializedOrder.allocateKey(clOrdIdRef);
const SerializedFieldKey qtyKey = serializedOrder.allocateKey(qtyRef);
// Retrieves initial order qty value for further incremental update.
UInt32 qty;
if (!serializedOrder[qtyKey].toNumber(qty))
{
throw domain_error("Cannot transform order qty to integer value. ");
}
size_t iteration = 1000;
while (iteration--)
{
// Assigns a new value to the ClOrdID field.
serializedOrder.set(clOrdIdKey, Timestamp::utc(), TimestampFormat::YYYYMMDDHHMMSSMsec);
// Assigns the new order qty.
serializedOrder.set(qtyKey, qty += 1000);
// Sends the updated order to the counterparty.
session.send(serializedOrder);
// Extracts the sequence number assigned to the sent message.
const StringRef seqNumberStr = serializedOrder[KnownSerializedFieldKeys::SeqNumber];
SequenceNumber seqNumber;
if (seqNumberStr.toNumber(seqNumber))
{
cout << "Order sent under #" << seqNumber << '.' << endl;
}
else
{
throw domain_error("Cannot cast sequence number to an integer number. ");
}
}