B2BITS FIX Antenna HFT  1.0.16
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Basic Concepts

Table of Contents

Main components

The main FIX Antenna components are:

Engine description

The engine is represented by the Engine::FixEngine class. This class is responsible for:

Engine initialization is performed using the void Engine::FixEngine::init() method. This method must be called before any other FIX Antenna methods (although some initialization steps can be performed explicitly and before this one e.g. LogSystem::create, platform_initialize). At the end of the process void Engine::FixEngine::destroy() method must be called to deallocate the consumed resources.

For example:

#include <iostream>
#include <B2BITS_V12.h>
using namespace std;
int main(int argc, char* argv[]) {
try {
// initialization
// ... - main functionality
// destruction
}
catch(const Utils::Exception& ex) {
cout << "EXCEPTION: " << ex.what() << endl;
}
return 0;
}

The Engine::FixEngine class, as well as several other FIX Antenna C++ API classes, implements the Singleton Design Pattern. Therefore you should, first of all, get a pointer to the instance of the class, using the FixEngine* Engine::FixEngine::singleton() static method to access non-static methods of the class.

For example:

FixEngine::singleton()->createSession(...)

Sessions Manager

The SessionsManager manages the creation of unregisted acceptor sessions. When CreateUnregisteredAcceptorSession is set to true engine accepts all incoming logons and creates sessions-acceptors. It is possible though to implement custom logic and introduce more sophisticated criteria of accepting such sessions. To do this you need to create and register your own instance of SessionsManager.

Create a class derived from the Engine::SessionsManager class and override the Engine::SessionsManager::onUnregisteredAcceptor() method to control acceptor session creation. Return "true" if you agree with the creation of unregistered acceptor; otherwise return "false".

Use Engine::FixEngine::registerSessionsManager() method to register custom session manager. Only one Sessions Manager can be registered to the FIX Engine.

For example:

class SsnManager : public Engine::SessionsManager{
public:
virtual bool onUnregisteredAcceptor(Session* pSn, const FIXMessage& logonMsg) {
clog << "Incoming logon: " << *logonMsg.toString() << endl;
return true;
}
};
// The FixEngine initialization
FixEngine::init("engine.properties");
// create and register SessionsManager
SsnManager *g_mySM = new SsnManager();
FixEngine::singleton()->registerSessionsManager(g_mySM);
...
// unregister and destroy SessionsManager
FixEngine::singleton()->registerSessionsManager(NULL);
delete g_mySM;
// destruct FixEngine
FixEngine::destroy();
Warning
Do not delete the registered sessions’ manager until you unregister it.

Session description

The FIX session is represented by the Engine::Session class. This class is responsible for:

Each session encapsulates message storage that is used to store the sent and received messages. There are two types of storages:

Unregistered acceptors

When engine receives FIX logon message it extracts SenderCompID and TargetCompID fields from the message and tries to find previously created acceptor with corresponding session ID (i.e. session SenderCompID equal to TargetCompID extracted from message and session TargetCompID equal to the SenderCompID extracted from messages). If engine cannot find corresponding acceptor the incoming logon is declined and connection is not established.

Engine though can also work in "trusted mode" i.e. if corresponding session acceptor cannot be found then session is created automatically. To enable this mode set CreateUnregisteredAcceptorSession property to "true".

Application description

The Application class is responsible for:

Create a new class derived from the Application class and override the Application::process method in this class to process incoming messages. Then pass the instance of your class to the session to start receiving incoming messages.

Engine delivers incoming messages to the Application by calling Application::process method and analyzing its return value to make sure that incoming message was processed and Application is ready to process next incoming message.

Other useful methods to override are: onLogonEvent, onLogoutEvent, onSequenceGapEvent, onSessionLevelRejectEvent, etc. These are the call-back methods called to notify about the corresponding session-level events. Note that all pure virtual methods of Application must be overridden (implementation can be just empty body).

The Engine::Application::process method is called only when application-level message is received. All session-level messages (Heartbeats, Test Requests, Resend Requests, etc.) are handled by FIX Antenna. However you can modify default behavior overriding call-back methods in Application and providing your own logic.

Note
if you need to keep FIX message for future processing steal it from "work" parameter or create a copy of the message and save a copy instead of saving original message (refer to the Engine::FIXMsgProcessor::clone method).

Below is an example of custom implementation of Application interface:

class Appl : public Engine::Application{
public:
virtual bool process(Engine::FIXMessageProcessElem* work, const Engine::Session& aSn) {
clog << "LiteMsg: " << *work->elem->msg.toString() << endl;
return true;
}
virtual bool process(const FIXMessage & aMsg, const Engine::Session& aSn) {
clog << "Msg: " << *aMsg.toString() << endl;
return true;
}
virtual void onLogonEvent(const LogonEvent* apEvent, const Session& aSn) {
clog << "LogonEvent, the Logon message was received: " << apEvent->m_pLogonMsg->toString() << endl;
}
virtual void onLogoutEvent(const LogoutEvent* apEvent, const Session& aSn) {
clog << "LogoutEvent, the Logout message was received: " << apEvent->m_pLogoutMsg->toString() << endl;
}
virtual void onSequenceGapEvent(const SequenceGapEvent* apEvent, const Session& aSn) {
clog << "SequenceGapEvent" << endl;
}
virtual void onSessionLevelRejectEvent(const SessionLevelRejectEvent* apEvent, const Session& aSn) {
clog << "SessionLevelRejectEvent" << endl;
}
virtual void onMsgRejectEvent(const MsgRejectEvent* event, const Session& sn){
clog << "MsgRejectEvent" << endl;
}
virtual void onResendRequestEvent(const ResendRequestEvent &,const Session &) {
clog << "ResendRequestEvent" << endl;
}
virtual void onNewStateEvent(const NewStateEvent &,const Session &) {
clog << "NewStateEvent" << endl;
}
virtual void onUnableToRouteMessage(const UnableToRouteMessageEvent &,const Session &) {
clog << "UnableToRouteMessage" << endl;
}
virtual bool onResend(const FIXMessage &,const Session &) {
clog << "Resend" << endl;
return true;
}
virtual void onHeartbeatWithTestReqIDEvent(const HeartbeatWithTestReqIDEvent &,const Session &) {
clog << "HeartbeatWithTestReqIDEvent" << endl;
}
};
Note
It is not recommended to put lock inside the Application::process, synchronize different call-back methods unless you are absolutely confident in what you are doing. Also it is not recommended to perform time-consuming operations inside Application::process as it blocks receiving following messages.
If a pointer to the same Application is sent to several sessions, being established, synchronize the Application::process() method.
Warning
Do not delete the registered Application until you unregister it.

Message description

The FIX message (specified by the FIX protocol) is represented by the Engine::FIXMessage class.

The Engine::FIXMessage class provides the following functionality:

Repeating Groups description

Please read the FIX Protocol explanation of the Repeating group first (refer to the section About FIX messages).

The FIX message may contain repeating groups. Each group contains:

High level API

The Engine::FIXMessage class provides high level API to work with repeating groups.

Repeating groups are referred by the leading tag.

To add new group to the message you simply should add leading field to the message. In the result the new group will be added with the number of entries according to the value of leading field.

To access fields inside a repeating group you should

To resize group simply modify the value of leading tag. If the new value is greater than old one you should populate the values of the entries added.

To remove group simply remove leading tag from the message.

Below is an example of the reading MDUpdateAction, MDEntryPx, MDEntrySize from the repeating groups of Market Data - Incremental Refresh (X) message:

if(msg->type() == "X"){
if(grp){
for(int i=0; i < grp->size(); i++){
Engine::FIXFieldValue mdUpdateAction, mdEntryPx, mdEntrySize;
grp->get(FIXFields::MDUpdateAction, &mdUpdateAction, i);
grp->get(FIXFields::MDEntryPx, &mdEntryPx, i);
grp->get(FIXFields::MDEntrySize, &mdEntrySize, i);
}
}
}

Low level API

The Parser::LiteFixMessage class provides low level API to work with repeating groups.

Each field in the LiteFixMessage can be accessed via flat index. The first field among duplicates with the same tag number can be accessed via tag number.
So the leading tag index plus one gives lower bound index to search the first start tag index, the first start tag index plus one gives lower bound index to search the second start tag index and so on. Finally each repeating group tag can be found using corresponded lower bound and upper bound indexes of the repeating group.

Below is an example of the reading MDUpdateAction, MDEntryPx, MDEntrySize from the repeating groups of Market Data - Incremental Refresh (X) message:

if(msg->type() == "X"){
int noMDEntriesIndex = msg->getTagIndex(FIXFields::NoMDEntries);
if( noMDEntriesIndex != Parser::LiteFixMessage::NOTFOUND ) {
int noMDEntries = msg->getTagAsIntAtIndex(noMDEntriesIndex);
int startIndex = noMDEntriesIndex + 1;
for(int mdUpdateActionIndex = msg->getTagIndex(FIXFields::MDUpdateAction, startIndex), noMDEntriesProcessed = 0;
mdUpdateActionIndex != Parser::LiteFixMessage::NOTFOUND && noMDEntriesProcessed < noMDEntries;
startIndex = endIndex, ++noMDEntriesProcessed ){
endIndex = msg->getTagIndex(FIXFields::MDUpdateAction, startIndex + 1);
Engine::AsciiString mdUpdateAction = msg->getTagAsStringAtIndex(mdUpdateActionIndex);
int mdEntryPxIndex = msg->getTagIndex(FIXFields::MDEntryPx, startIndex, endIndex);
Engine::AsciiString mdEntryPx = (mdEntryPxIndex != Parser::LiteFixMessage::NOTFOUND)? msg->getTagAsStringAtIndex(mdEntryPxIndex): Engine::AsciiString();
int mdEntrySizeIndex = msg->getTagIndex(FIXFields::MDEntrySize, startIndex, endIndex);
Engine::AsciiString mdEntrySize = (mdEntrySizeIndex != Parser::LiteFixMessage::NOTFOUND)? msg->getTagAsStringAtIndex(mdEntrySizeIndex): Engine::AsciiString();
}
}
}

The repeating group can be truncated by removing extra data field by field. There is no way to enlarge it again except adding fields to the tail of the message. It is user responsibility to keep right fields order except CheckSum(10) field.

Below is an example of creation of Market Data - Incremental Refresh (X) message:

msg->set( FIXFields::BeginString, "FIX.4.4" );
msg->set( FIXFields::BodyLength, "000" );
msg->set( FIXFields::MsgType, "X" );
msg->set( FIXFields::SenderCompID, "000000000" );
msg->set( FIXFields::TargetCompID, "000000000" );
msg->set( FIXFields::MsgSeqNum, "000000000" );
msg->set( FIXFields::SendingTime, "20140101-01:01:01.001" );
msg->prepareContinuousBuffer();//the message is complete, spend more time now to send it faster later