Skip to main content

Repeating Groups

The FIX Protocol heavily relies on repeating groups for data like market data entries, order legs, and allocations. QuickFIX/J provides type-safe nested classes to easily create, append, and read these repeating groups.

Creating Messages with Repeating Groups

QuickFIX/J automatically manages the count field (the field indicating the number of groups, such as NoMDEntries in market data). You create a group instance from the inner class of the parent message, set its fields, and add it to the message.

Example: Adding Market Data Entries

import quickfix.field.*;
import quickfix.fix42.MarketDataSnapshotFullRefresh;

// 1. Create the parent message
MarketDataSnapshotFullRefresh message = new MarketDataSnapshotFullRefresh(new Symbol("QF"));

// 2. Create a group instance (NoMDEntries is the count field)
MarketDataSnapshotFullRefresh.NoMDEntries group = new MarketDataSnapshotFullRefresh.NoMDEntries();

// 3. Populate and add the first group
group.set(new MDEntryType(MDEntryType.BID));
group.set(new MDEntryPx(12.32));
group.set(new MDEntrySize(100));
message.addGroup(group); // QuickFIX/J automatically sets NoMDEntries = 1

// 4. Populate and add the second group
group.set(new MDEntryType(MDEntryType.OFFER));
group.set(new MDEntryPx(12.45));
group.set(new MDEntrySize(200));
message.addGroup(group); // QuickFIX/J automatically sets NoMDEntries = 2

Reading Messages with Repeating Groups

To read groups from an incoming message, you first retrieve the count field to determine how many entries exist, then iterate through them using getGroup(index, group).

Important: Group indexing in QuickFIX/J is 1-based.

Example: Reading Market Data Entries

import quickfix.field.*;
import quickfix.fix42.MarketDataSnapshotFullRefresh;

public void onMessage(MarketDataSnapshotFullRefresh message, SessionID sessionID) throws quickfix.FieldNotFound {
// 1. Get the number of entries
NoMDEntries noMDEntries = new NoMDEntries();
message.get(noMDEntries);
int count = noMDEntries.getValue();

System.out.println("Received " + count + " market data entries.");

// 2. Initialize the group structure and field objects
MarketDataSnapshotFullRefresh.NoMDEntries group = new MarketDataSnapshotFullRefresh.NoMDEntries();
MDEntryType type = new MDEntryType();
MDEntryPx px = new MDEntryPx();
MDEntrySize size = new MDEntrySize();

// 3. Iterate through the groups (1-based index)
for (int i = 1; i <= count; i++) {
message.getGroup(i, group); // Loads the group at index i

group.get(type);
group.get(px);

// Check for optional fields inside the group
if (group.isSetField(size)) {
group.get(size);
}

System.out.println("Entry " + i + ": Type=" + type.getValue() + ", Px=" + px.getValue());
}
}

Key Points

  • Automatic Counting: You do not need to manually instantiate and set the value of the count field (e.g., new NoMDEntries(2)). Calling addGroup() handles this automatically to ensure consistency.
  • 1-Based Indexing: When retrieving groups via getGroup(int index, Group group), the first entry is at index 1.
  • Type Safety: Using the generated message and group classes provides type-safe setters and getters for FIX fields, reducing runtime errors.