();
if (reader.find_with_hint(hffix::tag::Price, i)) {
int mantissa, exponent;
i->value().as_decimal(mantissa, exponent);
std::cout << " @ $" << mantissa << "E" << exponent;
++i;
}
std::cout << "\n\n";
}
} catch(std::exception& ex) {
std::cerr << "Error reading fields: " << ex.what() << '\n';
}
} else {
// An invalid, corrupted FIX message. Do not try to read fields
// out of this reader. The beginning of the invalid message is
// at location reader.message_begin() in the buffer, but the
// end of the invalid message is unknown (because it's invalid).
//
// Stay in this for loop, because the
// messager_reader::next_message_reader() function will see
// that this message is invalid and it will search the
// remainder of the buffer for the text "8=FIX", to see if
// there might be a complete or partial valid message anywhere
// else in the remainder of the buffer.
//
// Set the return code non-zero to indicate that there was
// an invalid message, and print the first 64 chars of the
// invalid message.
return_code = 1;
std::cerr << "Error Invalid FIX message: ";
std::cerr.write(
reader.message_begin(),
std::min(
ssize_t(64),
buffer + buffer_length - reader.message_begin()
)
);
std::cerr << "...\n";
}
}
buffer_length = reader.buffer_end() - reader.buffer_begin();
if (buffer_length > 0)
// Then there is an incomplete message at the end of the buffer.
// Move the partial portion of the incomplete message to buffer[0].
std::memmove(buffer, reader.buffer_begin(), buffer_length);
}
return return_code;
}
~~~
### Running the Examples
The writer example can be piped to the reader example. Running these commands:
make examples
test/bin/writer01 | test/bin/reader01
Should produce output like this:
Logon message
SenderCompID = AAAA
MsgSeqNum = 1
SendingTime = 2014-Sep-26 15:27:38.789000
The next field is EncryptMethod = 0
New Order Single message
Buy OIH 100 @ $50001E-2
To examine the output from `test/bin/writer01` program, you can also use `util/bin/fixprint`, like this:
make examples
make fixprint
test/bin/writer01 | util/bin/fixprint --color
Which will produce output like this:
FIX.4.2 MsgType_35=A_Logon SenderCompID_49=AAAA TargetCompID_56=BBBB MsgSeqNum_34=1 SendingTime_52=20140928-07:12:06.000 EncryptMethod_98=0 HeartBtInt_108=10
FIX.4.2 MsgType_35=D_NewOrderSingle SenderCompID_49=AAAA TargetCompID_56=BBBB MsgSeqNum_34=2 SendingTime_52=20140928-07:12:06.000 ClOrdID_11=A1 HandlInst_21=1 Symbol_55=OIH Side_54=1 TransactTime_60=20140928-07:12:06.000 OrderQty_38=100 OrdType_40=2 Price_44=500.01 TimeInForce_59=1
## Boost Date_Time
If the Boost Date_Time library is available in your build environment, `boost::posix_time::ptime`, `boost::posix_time::time_duration`, and `boost::gregorian::date` will be automatically supported for the various FIX date and time field types.
To enable High Frequency FIX Parser support for the Boost Date_Time library types, include the Boost libraries before the hffix.hpp library, like this:
~~~cpp
#include
#include
#include
~~~
To prevent High Frequency FIX Parser support for the Boost Date_Time library, `#define HFFIX_NO_BOOST_DATETIME` before including `hffix.hpp`:
~~~cpp
#define HFFIX_NO_BOOST_DATETIME
#include
~~~
## Test
Sample data sources, discovered by Googling.
* `test/data/fix.4.1.set.1` http://fixparser.targetcompid.com/
* `test/data/fix.5.0.set.1` https://www.jse.co.za/content/JSETechnologyDocumentItems/03.%20JSE%20Indices.recv.log.txt
* `test/data/fix.5.0.set.2` http://blablastreet.com/workshop/TestSocketServer/TestSocketServer/bin/Debug/store/FIXT.1.1-ATP1CMEMY-OMSCMEMY.body
The Chicago Mercantile Exchange is also a good source of sample data files, but the files are too big to include in this repository. The script `test/curl.cme.data.sh` shows how to download them. Run `curl.cme.data.sh` in the `test/` directory.
## Cookbook
### Multi-threaded Sending
Q: I have a bunch of different threads serializing and sending FIX messages out one socket. When each message is sent it needs a *MsgSeqNum*, but at serialization time I don't know what the *MsgSeqNum* will be, I only know that at sending time.
A: That multi-threading model is not a good choice for your software. The performance penalty for that threading model is much greater than the performance advantage of this non-allocating parser library. You should consider redesigning to use a single-threaded simultaneous-wait event loop like *libev* or *Boost Asio*. If you insist on multi-threading, then you could do something like this code example.
~~~cpp
hffix::message_writer m;
m.push_back_string(hffix::tag::MsgSeqNum, "00000000"); // Make a placeholder value over which you can later paste your sequence number.
// This thread_safe_send() function will correctly sequence FIX messages if two threads are racing to call thread_safe_send().
void thread_safe_send(hffix::message_writer const& w) {
lock l(send_mutex_); // Serialize access to this function.
hffix::message_reader r(w); // Construct a reader from the writer.
hffix::message_reader::const_iterator i = std::find(r.begin(), r.end(), hffix::tag_equal(hffix::tag::MsgSeqNum)); // Find the MsgSeqNum field.
if (i != r.end()) {
std::snprintf(const_cast(i->begin()), i->size(), "%.8i", next_sequence_number++); // Overwrite the "00000000" string with the next_sequence_number.
write(fd, w.message_begin(), w.message_size()); // Send the message to the socket.
}
}
~~~
### FIX Repeating Groups
From *FIX-50_SP2_VOL-1_w_Errata_20110818.pdf* page 21:
If the repeating group is used, the first field of the repeating group is required. This allows
implementations of the protocol to use the first field as a "delimiter" indicating a new repeating group
entry. The first field listed after the NoXXX, then becomes conditionally required if the NoXXX field
is greater than zero.
The beginning of each Repeating Group is marked by a field with a “NoXXX” field. By convention, Repeating Groups are usually located at the end of the message, so the end of the message marks the end of the Repeating Group. In this example we assume that the convention holds, and the repeating group is at the end of the message. If the repeating group were not at the end of the message then we'd have to pay attention to the value of the “NoXXX” fields, which is left as an exercise for the reader.
This is an example of iterating over the nested Repeating Groups when reading a *Mass Quote* message.
The *Mass Quote* message has *QuoteSet* Repeating Groups, and nested inside those groups are *QuoteEntry* Repeating Groups, see *fix-42-with_errata_20010501.pdf* page 52.
In each repeated *QuoteSet* Group, `hffix::tag::QuoteSetID` is always the first field. In each repeated *QuoteEntry* Group, `hffix::tag::QuoteEntryID` is always the first field.
~~~cpp
hffix::message_reader r;
hffix::message_reader::const_iterator group1_begin = std::find_if(r.begin(), r.end(), hffix::tag_equal(hffix::tag::QuoteSetID));
hffix::message_reader::const_iterator group1_end;
for (; group1_begin != r.end(); group1_begin = group1_end) {
group1_end = std::find_if(group1_begin + 1, r.end(), hffix::tag_equal(hffix::tag::QuoteSetID));
// This loop body will be entered once for each QuoteSet Repeating Group.
//
// group1_begin will point to the first field in the QuoteSet group, which is always hffix::tag::QuoteSetID.
// group1_end will point past-the-end of the QuoteSet group.
hffix::message_reader::const_iterator group2_begin = std::find_if(group1_begin, group1_end, hffix::tag_equal(hffix::tag::QuoteEntryID));
hffix::message_reader::const_iterator group2_end;
for (; group2_begin != group1_end; group2_begin = group2_end) {
group2_end = std::find_if(group2_begin + 1, group1_end, hffix::tag_equal(hffix::tag::QuoteEntryID));
// This loop body will be entered once for each QuoteEntry Repeating Group.
//
// group2_begin will point to the first field of the QuoteEntry group, which is always QuoteEntryID.
// group2_end will point past-the-end of the QuoteEntry group.
}
}
~~~
## Support
Want to talk? Email me at .
## Contributing
Pull requests welcome. `make test` to run the test suite.
## Notes on the Design of FIX Protocol
### The *Logon* - *Resend Request* Race Condition
When a FIX client connects to a FIX server, the client doesn't know what sequence number to use for the *Logon* message.
Either the client can choose to reset both sequence numbers, in which case the client may miss messages, or not, in which case the client is subject to the *Resend Request* race condition.
After *Logon* response from the server, the client may begin sending messages, but the client has to wait some amount of time because the server may send *Resend Request*. If the client sends any message to the server while the server is preparing to send *Resend Request*, then the server's response is not defined by the *FIX* specification, and some servers implementations may seize up in confusion at that point.
## C++03|11|14|17
This library only depends on C++98 because it doesn't need any of the features of later C++. However, the library was designed with the intention of interacting well with C++14 features such as, for example, `auto`, or anonymous inline functions passed as the `UnaryPredicate` to `hffix::find_with_hint`.
## TODO
* `dictionary_init_mdentrytype()` function.
* More support for BlockRepeating NoXXX field types for FIX 5.0 SP2.
* Checks for buffer overflow in hffix::message_writer.
* Lexical cast validation for hffix::message_reader.