OnixS C++ FIX Engine  4.10.1
API Documentation
FIX Repeating Groups

Structure of FIX Repeating Groups

Sometimes fields of the same tag number can appear multiple times in the same message within a so-called repeating group.

The following diagram depicts a general structure of the repeating group in a FIX message:

fix-message-repeating-group.gif

Each repeating group starts from the field which identifies the number of repeating entries within a repeating group. Such a leading field must precede immediately the repeating group entries. In the referenced example, the leading field is defined by the NoRoutingIDs tag.

Each entry of a repeating group has a selected leading field, which in turn identifies the beginning of a new group entry. This field must occur in the raw message before any other tag from a single repeating group entry. In other words, these fields separate repeating group entries from each other. The RoutingType field is an example of such a separator.

Important aspects of FIX repeating groups structure:

Group class

OnixS C++ FIX Engine exposes the OnixS::FIX::Group class to encapsulate all aspects that are related to handling FIX repeating groups.

Since a repeating group is identified by the leading field, which defines the number of repeating instances, C++ FIX Engine follows this approach in handling repeating groups. In particular, the OnixS::FIX::Group object is accessed, using its leading (number of instances) field. Changing the value of this field affects the length of the repeating group. Removing this field from a message or other repeating group removes all entries of the repeating group.

Adding Group to Message

To create a new repeating group or modify the number of instances, the OnixS::FIX::Message::setGroup method is available.

Warning
Setting non-zero integer value for the field, which defines a number of instances of a repeating group, implicitly creates and/or changes the length of the repeating group object.

Example

// Forms 'Market Data Request' message.
using namespace OnixS::FIX;
using namespace OnixS::FIX::FIX42;
Message request(Values::MsgType::Market_Data_Request, ProtocolVersion::FIX_42);
request.set(Tags::MDReqID, "ABSD")
.set(Tags::SubscriptionRequestType, 1)
.set(Tags::MarketDepth, 1)
.set(Tags::MDUpdateType, 1)
.set(Tags::AggregatedBook, "N");
{
// Creates a repeating group NoMDEntryTypes with two instances.
Group groupMDEntryTypes = request.setGroup(Tags::NoMDEntryTypes, 2);
// Defines fields in the first instance/entry of repeating group.
groupMDEntryTypes[0].set(Tags::MDEntryType, "EntryType_1");
// .. and the same but for the second instance.
groupMDEntryTypes[1].set(Tags::MDEntryType, "EntryType_2");
}
// Creates a repeating group NoRelatedSym with two instances.
// (another way of creating and accessing repeating group).
{
request.set(Tags::NoRelatedSym, 2);
Group groupRelatedSym = request.getGroup(Tags::NoRelatedSym);
groupRelatedSym[0].set(Tags::Symbol, "EURUSD_0");
groupRelatedSym[1].set(Tags::Symbol, "EURUSD_1");
}
request.validate();
std::clog << request.toString() << std::endl;

Accessing Repeating Group Entries

To obtain a reference to the OnixS::FIX::Group object, that represents an existing repeating group of the message, the OnixS::FIX::Message::getGroup method is available.

The OnixS::FIX::Group class works with fields, as well as with embedded repeating groups, in the same manner as the OnixS::FIX::Message class. The only difference in accessing field values is the availability of an additional parameter, which defines the index of the repeating group entry whose field is being accessed. The indexing entry starts from zero. Additionally, you can use the OnixS::FIX::Group::Iterator class to iterate over all group entries of a repeating group.

Example

using namespace OnixS::FIX;
using namespace OnixS::FIX::FIX42;
// Gets group from 'Mass Quote' message.
class OutputFunctor
{
public:
void operator()(GroupInstance& quoteEntriesInstance)
{
//Gets entries of a nested QuoteEntries repeating group.
std::clog << "BidSize=" << quoteEntriesInstance.getInt32(Tags::BidSize) << std::endl
<< "OfferSize=" << quoteEntriesInstance.getInt32(Tags::OfferSize) << std::endl;
}
};
void iterateGroupEntries()
{
std::string rawMessage = "8=FIX.4.2\0019=128\00135=i\00134=2\00149=PXMD\00152=20140922-14:48:49.825\00156=Q037\001117=1\001296=1\001"
"302=123\001295=1\001299=0\001134=1000000\001135=900000\001188=1.4363\001190=1.4365\00110=084\001";
Message message;
Message::parse(rawMessage.c_str(), rawMessage.size(), MessageParsingFlag::Strict, message);
//Gets a quoteSets repeating group.
Group quoteSets = message.getGroup(Tags::NoQuoteSets);
//Iterates over all repeating group entries of the NoQuoteSets group by the index.
for(size_t quoteSetsIndex = 0; quoteSetsIndex < quoteSets.size(); ++quoteSetsIndex)
{
//Gets a nested QuoteEntries repeating group.
Group quoteEntries = quoteSets[quoteSetsIndex].getGroup(Tags::NoQuoteEntries);
//Iterates over all repeating group entries of the NoQuoteEntries group by iterators.
std::for_each(quoteEntries.begin(), quoteEntries.end(), OutputFunctor());
}
}

A group can also be traversed using range-based for loop:

void iterateGroupEntriesRangeBased()
{
std::string rawMessage = "8=FIX.4.2\0019=128\00135=i\00134=2\00149=PXMD\00152=20140922-14:48:49.825\00156=Q037\001117=1\001296=1\001"
"302=123\001295=1\001299=0\001134=1000000\001135=900000\001188=1.4363\001190=1.4365\00110=084\001";
Message message;
Message::parse(rawMessage.c_str(), rawMessage.size(), MessageParsingFlag::Strict, message);
//Gets a quoteSets repeating group.
Group quoteSets = message.getGroup(Tags::NoQuoteSets);
// Traverse the group using range-based for loop.
for(auto & entry : quoteSets)
{
std::cout << entry.toString('|') << std::endl;
}
}

Removing Repeating Group from Message

To remove an entire repeating group together with its leading tag, the OnixS::FIX::FieldSet::erase method is exposed by OnixS::FIX::Message and the OnixS::FIX::Group class.

Detecting Ill-Formed Repeating Group

Due to the design of the FIX protocol, the FIX repeating group cannot be parsed properly without the additional information that describes its structure. This is a well-known feature/flaw of the FIX protocol - and is applicable to any software that parses FIX repeating groups. If the FIX destination you work with uses non-standard (custom) fields in the repeating group, then the FIX Engine needs the corresponding XML-based FIX Dialect description file. In a well-formed repeating group, the declared number of repeated instances (defined by the number of instances field) should be equal to the actual number of instances. By default, the FIX Engine reports an error if the incoming message does not meet this requirement. This behavior could be changed via the OnixS::FIX::EngineSettings::validateRepeatingGroupEntryCount setting.

Note
It is not recommended to set the OnixS::FIX::EngineSettings::validateRepeatingGroupEntryCount setting to false because this can lead to data loss during the message parsing without any notification about an error.

By default, the FIX Engine does not check that each repeating group instance starts with the correct leading field. This behavior could be changed via the OnixS::FIX::EngineSettings::validateRepeatingGroupLeadingTag setting. It is also possible to detect the ill-formed repeating group using the OnixS::FIX::Message::validate method.

See also