B2BITS FIX Antenna C++  2.27.1
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. 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:

In earlier versions of FIX Antenna (2.10.14 and earlier) Session object could be uniquely identified by SenderCompId/TargetCompId pair. Starting from version 2.10.15.5 extra string identifier - SessionQualifier has been introduced in addition to SenderCompId/TargetCompId pair. The idea is to give user ability to create several sessions with the same SenderCompId and TargetCompId and to give ability to address these sessions by unique ID. See Session identifier for more details.

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). Refer to the FIX Session Acceptor for more information about how to create session acceptor. 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" (refer to the section Common parameters for more details about property).

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.

If the Engine::Application::process method does not return "true" (i.e. returns "false" or throws an exception), the engine passes the same incoming message to the Application::process again and again until either Application::process returns "true" or number of attempts is exceeded (DelayedProcessing.MaxDeliveryTries, refer to Common parameters for more information). The interval between attempts is specified by property DelayedProcessing.DeliveryTriesInterval (refer to Common parameters for more information). If the number of unsuccessful attempts exceeds the MaxDeliveryTries, a Logout message with the reason "Application is not available" is sent to the counterparty and session is closed.

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 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(const FIXMessage & aMsg, const Engine::Session& aSn) {
clog << "aMsg: " << *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:

Repeating groups are usually 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. Pay attention that this operation re-creates the group so you should populate values again.

To remove group simply remove leading tag from the message.

Session Controller

Engine::SessionsController class introduces interface to abstract basic session management operations like start/stop/connect/disconnect. It easies sessions management implementation and can be used as general sessions owner in application.

FixAntenna provides built-in default implementation (FixEngineSessionsController) that uses FixEngine as underlying layer to control sessions. See Scheduler for samples.

Scheduler

Scheduler component is designed to be flexible and easily scalable. The following generic interfaces are defined:

System::Scheduler - is a general scheduler interface that abstracts out underlying implementation of the scheduler. FixAntenna provides Engine::Scheduler interface implementation Engine::SchedulerImpl that uses separate thread to run schedules.

System::Schedule - is a generic schedule interface. Instances implementing this interface can be registered and run on Engine::SchedulerImpl or any other instance that implements Engine::Scheduler interface.

System::ScheduleTimeline - is a generic time events generator/supplier for schedules. Scheduler retreives instance of this interface from schedule to take next event to execute.

FixAntenna as a Fix sessions management engine implements all of these interfaces in manner required to manage sessions. So the following implementations are provided:

Engine::SessionsSchedule uses Engine::SessionsController instance to manage sessions and System::ScheduleTimeline as events source. It allows to set Engine::SessionsScheduleManager instance as callback sink that is used to notify user about actions being planned for execution and modify them if required. So user has full control over what actions are being performed as reaction on particular time event. Engine::SessionsSchedule has two types of events TradePeriodBegin and TradePeriodEnd. As a reaction on each event an execution plan is created (Engine::ScheduleExecutionPlan) that is passed to Engine::SessionsScheduleManager::onTradePeriodBegin or Engine::SessionsScheduleManager::onTradePeriodEnd depending on what event has happened if SessionsScheduleManager is set on schedule. User can modify execution plan in manner his business rules require then execution plan is executed. There are four execution actions defined:

Engine::SessionsSchedule forms execution plan depending on event happaned as described below:

Note
Sessions get into execution plan exactly in the same order as they are returned from sessions controller.

Engine::CronSessionScheduleTimeline is events source for Engine::SessionsSchedule. It generates two types of events TradePeriodBegin and TradePeriodEnd. This events are differentiated with eventTag_ property of System::SchedulerTimeEvent. TradePeriodBegin returns eventTag_ equal to SessionsSchedule_TradePeriodBeginEvent and TradePeriodEnd - SessionsSchedule_TradePeriodEndEvent. Engine::CronSessionScheduleTimeline is configured with Engine::CronSessionsScheduleParameters structure, see its description for details. In addition to TradePeriodBegin and TradePeriodEnd cron expressions a day-offs expresiion can be specified as well. This expression defines when no event must happen. Day-offs has higher priority so when there is day-offs specified and one of TradePeriodBegin or TradePeriodEnd results to the same point in time as day-offs then day-offs has precedence. Cron expressions are timezone aware. So use can specify timezone within Engine::CronSessionsScheduleParameters and specify cron expression in that timezone. In order to be able to convert from timezone specified to UTC(Scheduler always works in UTC internally) Engine::CronSessionScheduleTimeline requires instance of Engine::TimezoneConverter to be provided. One that is configured via engine.properties is available through FAProperties::getTimezoneConverter() call. This instance is configured with TimezonesDB property, See the sample below.

FixAntenna - allows to configure schedules within engine.properties file

TimezonesDB = ../../../data/timezones.csv
#TradePeriodStart expression
Schedule.sch1.TradePeriodBegin = 0 0 7 ? * *
#TradePeriodEnd expression
Schedule.sch1.TradePeriodEnd = 0 30 18 ? * *
#DayOffs expression
Schedule.sch1.DayOffs = * * * ? * SUN,SAT
#Timezone
Schedule.sch1.Timezone = Europe/Warsaw
#Whatever to execute missed TradePeriodBegin event on startup
Schedule.sch1.LateExecution = true
#Set schedule ID to session. FixAntenna doesn't automatically adds this session to the schedule but setting this property easies session binding to the schedule further in code.
Session.Sender1-Target1.Schedule = sch1

FixAntenna - allows user to provide his own Scheduler implementation during startup through init parameters. If no scheduler is provided FixAntenna creates System:SchedulerImpl instance that is used to create engine.properties configured schedules on. Internal scheduler instance with already created schedules from engine.properties can be retreived with FixEngine::getScheduler() routine.

Note
FixAntenna internal scheduler instance contains all schedules configured with engine.properties after engine initialization but the schedules don't have sessions bound! User has to add sessions manually after start-up. The reason behind this is the fact that user has to specify Engine::Application instance in order to get session do anything reasonable.
Scheduler maintained by FixEngine is in stopped state after the engine initialization regardless if it is an internally created instance or the instance provided by user. User must start it explicitly!

Samples follow.

Scheduler Manager usage sample

class MySessionsScheduleManager: public SessionsScheduleManager, public Utils::ReferenceCounter
{
public:
MySessionsScheduleManager(const std::set<Engine::SessionId>& bannedSessons, const std::set<Engine::SessionId>& noTerminateSessons)
:bannedSessons_(bannedSessons)
,noTerminateSessons_(noTerminateSessons)
{
}
bool checkBanned(const std::pair<Engine::SessionId, ScheduleSessionActions>& pair)
{
return bannedSessons_.find(pair.first) != bannedSessons_.end();
}
bool checkNoTerminate(const std::pair<Engine::SessionId, ScheduleSessionActions>& pair)
{
std::set<Engine::SessionId>::const_iterator item = noTerminateSessons_.find(pair.first);
return noTerminateSessons_.find(pair.first)!= noTerminateSessons_.end() && pair.second == ScheduleSessionActions_Terminate;
}
virtual void onTradePeriodBegin(ScheduleExecutionPlan& executionPlan)
{
std::remove_if(executionPlan.begin(), executionPlan.end(), boost::bind(&MySessionsScheduleManager::checkBanned, this, _1));
};
virtual void onTradePeriodEnd(ScheduleExecutionPlan& executionPlan)
{
std::remove_if(executionPlan.begin(), executionPlan.end(), boost::bind(&MySessionsScheduleManager::checkBanned, this,_1));
std::remove_if(executionPlan.begin(), executionPlan.end(),boost::bind(&MySessionsScheduleManager::checkNoTerminate, this,_1));
};
private:
std::set<Engine::SessionId> bannedSessons_;
std::set<Engine::SessionId> noTerminateSessons_;
};
void usage(std::string const& aPropertiesFileName)
{
SchedulerImpl scheduler;
// Initialize FE
FixEngine::init( aPropertiesFileName );
SessionsControllerPtr pSessionController(new FixEngineSessionsController());
ScheduleTimelinePtr pTimeLine(new CronSessionScheduleTimeline(CronSessionsScheduleParameters("0 0 7 * * *", "0 0 18 * * *", "", "Europe/Warsaw", true, FixEngine::singleton()->getProperties()->getTimezoneConverter())));
SessionsSchedule* pSessionsSchedule = new SessionsSchedule("sch1", pTimeLine, pSessionController);
std::set<Engine::SessionId> banned;
banned.insert(Engine::SessionId("Sender3", "Target3"));
banned.insert(Engine::SessionId("Sender4", "Target4"));
std::set<Engine::SessionId> noTerminate;
noTerminate.insert(Engine::SessionId("Sender5", "Target5"));
noTerminate.insert(Engine::SessionId("Sender6", "Target6"));
pMyManager.reset(new MySessionsScheduleManager(banned, noTerminate));
pSessionsSchedule->attachScheduleManager(pMyManager.get());
scheduler.registerSchedule(SchedulePtr(pSessionsSchedule));
}

Register session to FixEngine's internal scheduler

class MyApplication: public Engine::Application
{
private: // Engine::Application contract implementation
virtual bool process( const Engine::FIXMessage&, const Engine::Session& aSn );
virtual void onLogonEvent( const Engine::LogonEvent* apEvent, const Engine::Session& aSn );
virtual void onLogoutEvent( const Engine::LogoutEvent* /*apEvent*/, const Engine::Session& /*aSn*/ ) {}
virtual void onMsgRejectEvent( const Engine::MsgRejectEvent* /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onSequenceGapEvent( const Engine::SequenceGapEvent* apEvent, const Engine::Session& aSn );
virtual void onHeartbeatWithTestReqIDEvent( const Engine::HeartbeatWithTestReqIDEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onResendRequestEvent( const Engine::ResendRequestEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onNewStateEvent( const Engine::NewStateEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onUnableToRouteMessage( const Engine::UnableToRouteMessageEvent& /*event*/, const Engine::Session& /*sn*/ );
virtual bool onResend( const Engine::FIXMessage& /*msg*/, const Engine::Session& /*sn*/ ) {
return true;
}
};
MyApplication myApp;
void usage(std::string const& aPropertiesFileName)
{
// Initialize FE
FixEngine::init( aPropertiesFileName );
SchedulerPtr pScheduler = FixEngine::singleton()->getScheduler();
pSchedule = SessionsSchedulePtr(pScheduler->getSchedule("sch1"));
if(!pSchedule)
{
// Not found or the schedule is not Sessions schedule
return;
}
SessionExtraParameters sessionParams;
FixEngineSessionsControllerPtr pSessionController = FixEngineSessionsControllerPtr(pSchedule->getSessionsController());
if(!pSessionController)
{
// SessionsController is not FixEngineSessionsController. So we can't use it.
return;
}
pSessionController->registerSession(Engine::SessionId("Sender1","Target1"), myApp, sessionParams);
}

Add pre-configured sessions to FixEngine created schedule

class MyApplication: public Engine::Application
{
private: // Engine::Application contract implementation
virtual bool process( const Engine::FIXMessage&, const Engine::Session& aSn );
virtual void onLogonEvent( const Engine::LogonEvent* apEvent, const Engine::Session& aSn );
virtual void onLogoutEvent( const Engine::LogoutEvent* /*apEvent*/, const Engine::Session& /*aSn*/ ) {}
virtual void onMsgRejectEvent( const Engine::MsgRejectEvent* /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onSequenceGapEvent( const Engine::SequenceGapEvent* apEvent, const Engine::Session& aSn );
virtual void onHeartbeatWithTestReqIDEvent( const Engine::HeartbeatWithTestReqIDEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onResendRequestEvent( const Engine::ResendRequestEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onNewStateEvent( const Engine::NewStateEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onUnableToRouteMessage( const Engine::UnableToRouteMessageEvent& /*event*/, const Engine::Session& /*sn*/ );
virtual bool onResend( const Engine::FIXMessage& /*msg*/, const Engine::Session& /*sn*/ ) {
return true;
}
};
MyApplication myApp;
void usage(std::string const& aPropertiesFileName)
{
// Initialize FE
FixEngine::init( aPropertiesFileName );
SchedulerPtr pScheduler = FixEngine::singleton()->getScheduler();
pSchedule = SessionsSchedulePtr(pScheduler->getSchedule("sch1"));
if(!pSchedule)
{
// Not found ot the schedule is not Sessions schedule
return;
}
FixEngineSessionsControllerPtr pSessionController = FixEngineSessionsControllerPtr(pSchedule->getSessionsController());
if(!pSessionController)
{
// SessionsController is not FixEngineSessionsController. So we can't use it.
return;
}
Engine::FAProperties::ConfiguredSessionsMap sessions = FixEngine::singleton()->getProperties()->getConfiguredSessions();
for(Engine::FAProperties::ConfiguredSessionsMap::const_iterator it = sessions.begin(); it != sessions.end(); ++it)
{
pSessionController->registerSession(it->first, myApp, it->second);
}
}

Add pre-configured sessions to FixEngine Scheduler using properties configuration

class MyApplication: public Engine::Application
{
private: // Engine::Application contract implementation
virtual bool process( const Engine::FIXMessage&, const Engine::Session& aSn );
virtual void onLogonEvent( const Engine::LogonEvent* apEvent, const Engine::Session& aSn );
virtual void onLogoutEvent( const Engine::LogoutEvent* /*apEvent*/, const Engine::Session& /*aSn*/ ) {}
virtual void onMsgRejectEvent( const Engine::MsgRejectEvent* /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onSequenceGapEvent( const Engine::SequenceGapEvent* apEvent, const Engine::Session& aSn );
virtual void onHeartbeatWithTestReqIDEvent( const Engine::HeartbeatWithTestReqIDEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onResendRequestEvent( const Engine::ResendRequestEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onNewStateEvent( const Engine::NewStateEvent& /*event*/, const Engine::Session& /*sn*/ ) {}
virtual void onUnableToRouteMessage( const Engine::UnableToRouteMessageEvent& /*event*/, const Engine::Session& /*sn*/ );
virtual bool onResend( const Engine::FIXMessage& /*msg*/, const Engine::Session& /*sn*/ ) {
return true;
}
};
MyApplication myApp;
void usage(std::string const& aPropertiesFileName)
{
// Initialize FE
FixEngine::init( aPropertiesFileName );
SchedulerPtr pScheduler = FixEngine::singleton()->getScheduler();
Engine::FAProperties::ConfiguredSessionsMap sessions = FixEngine::singleton()->getProperties()->getConfiguredSessions();
for(Engine::FAProperties::ConfiguredSessionsMap::const_iterator it = sessions.begin(); it != sessions.end(); ++it)
{
// Find Schedule
pSchedule = SessionsSchedulePtr(pScheduler->getSchedule(it->second.schedule_));
if(!pSchedule)
{
// Not found ot the schedule is not Sessions schedule
continue;
}
//Get session controller
FixEngineSessionsControllerPtr pSessionController = FixEngineSessionsControllerPtr(pSchedule->getSessionsController());
if(!pSessionController)
{
// SessionsController is not FixEngineSessionsController. So we can't use it.
continue;
}
// Register the session within controller
pSessionController->registerSession(it->first, myApp, it->second);
}
}