OnixS C++ FIX Engine 4.14.0
Users' manual and API documentation
Loading...
Searching...
No Matches
Scheduling Sessions for Automatic Connection

In real life Sessions FIX Connections occurs at regular intervals. A lot of trading systems have public schedules which declare time frames for trading. Session class exposes members for a FIX Connections basic handling. It also provides users with an automatic reconnection facility in case of the connection failure. However, there is no way in Session functionality to maintain Connections on a systematic basis.

Sessions Scheduler

To satisfy the needs of real-life trading schedules, OnixS FIX Engine offers the Sessions Scheduler. This service will automatically connect certain FIX sessions to the counterparty at the beginning of the trading day, as well as disconnect at the end of the day. It is also possible to keep a FIX Session connected for an entire trading week and disconnect it at the end of the last trading day.

The Sessions Scheduler is available as a part of OnixS FIX Engine.

SessionScheduler class

OnixS::FIX::Scheduling::SessionScheduler is a workhorse class of the Sessions Scheduling services. This class exposes a simple, but comprehensive API, to handle connections by schedules of any complexity.

Registering a Session for Automatic Connection

OnixS::FIX::Scheduling::SessionScheduler::add member schedules a session for automatic logon and logout according to the specified session time, which can be delivered to Scheduler as an instance of the appropriate class, or as a name of the predefined preset. The parameter of the type OnixS::FIX::Scheduling::SessionConnectionSettings specifies the role of Session (either Acceptor or Initiator), and primary connection parameters like host and port, which Session will use to establish FIX Connection.

Note
If Session is being registered at the time, noted by schedule, the Scheduler will connect the Session immediately.

Removing Session from Scheduling Services

OnixS::FIX::Scheduling::SessionScheduler::remove given session from the scheduling services.

Note
OnixS::FIX::Scheduling::SessionScheduler::remove doesn't disconnect an active session from the trading system. Therefore, if the session has already been connected by the scheduler, it remains connected after unregistering.

Important Behavioral Aspects

The following steps take place while scheduling Session:

  • If a session is registered during the time covered by the specified schedule, the scheduler will connect the session immediately.
  • If a session is registered after logout and before logon time, no attempts will be made to connect the session until the next logon time.
  • If a session is registered after logout and before logon time, and the session is in an active state, it will be disconnected immediately.
  • If a scheduled initiator logon fails, the scheduler retries using OnixS::FIX::Scheduling::SessionScheduler::reconnectAttempts and OnixS::FIX::Scheduling::SessionScheduler::reconnectInterval. With multiple counterparties, it tries each host before pausing. It makes at most 1 + (number of counterparties × reconnectAttempts) logon attempts while the schedule remains active. No new logon attempt starts after scheduled logout time.
  • For a scheduled acceptor, the scheduler calls OnixS::FIX::Session::logonAsAcceptor() once to listen for an incoming connection. reconnectAttempts and reconnectInterval do not add retries. See Handling Scheduling Warnings and Errors for failed logon behavior.
  • If a network failure breaks an established initiator connection, the FIX Engine handles restoration (see Reconnection Facility). The scheduler reports no error during that process, regardless of outcome. If the connection remains down while the schedule is active, the scheduler starts regular logon attempts. Exhausting Engine reconnection attempts alone does not trigger a scheduler error.
  • After a normal Logout exchange during the scheduled activity period, the scheduler tries to log on again immediately, without waiting for an interval.

Both attempt settings default to 3, and both intervals default to 180 seconds. The scheduler and Engine settings are independent: changing the Engine values does not change scheduled logon retries.

Handling Scheduling Warnings and Errors

OnixS::FIX::Scheduling::SessionSchedulerListener reports scheduling warnings and errors, not session errors. Its OnixS::FIX::Scheduling::SessionSchedulerListener::onWarning callback reports non-critical issues, including exceptions thrown by the listener's onLoggingOut and onInitiatorConnecting callbacks. During scheduled initiator logon, the scheduler also calls it after each failed host attempt when more attempts remain.

OnixS::FIX::Scheduling::SessionSchedulerListener::onError reports scheduling errors, including a failed scheduled logout, a failed acceptor logon, or a failed final permitted initiator logon attempt. After such an error, the scheduler makes no further scheduled logon or logout attempts for that session until the next scheduled activity period.

If an attempt already in progress at scheduled logout time fails and it was the final permitted attempt, the scheduler still calls onError.

Example

using namespace OnixS::FIX;
using namespace OnixS::FIX::Scheduling;
namespace
{
class MySessionListener : public ISessionListener
{
public:
void onInboundApplicationMsg (Message&, Session*) override {}
};
};
struct SchedulingTimeSpan
{
class Seconds
{
public:
Seconds (int span)
: span_ (span)
{
}
int span() const
{
return span_;
}
private:
int span_;
};
};
TimeOfDay operator + (TimeOfDay time, SchedulingTimeSpan::Seconds seconds)
{
constexpr int SecondsPerMinute = 60;
constexpr int MinutesPerHour = 60;
constexpr int HoursPerDay = 24;
constexpr int SecondsPerHour = SecondsPerMinute * MinutesPerHour;
constexpr int SecondsPerDay = HoursPerDay * SecondsPerHour;
// Normalize both positive and negative spans into the current day.
int sinceMidnight = (time.sinceMidnight() + seconds.span()) % SecondsPerDay;
if(sinceMidnight < 0)
sinceMidnight += SecondsPerDay;
return TimeOfDay (
sinceMidnight / SecondsPerHour,
(sinceMidnight / SecondsPerMinute) % MinutesPerHour,
sinceMidnight % SecondsPerMinute);
}
class SessionStateChangeTracer : public ISessionListener
{
void onStateChange (SessionState::Enum newState, SessionState::Enum, Session* session) override
{
std::cout << static_cast<const std::string&> (*session) << " changed its state to " << SessionState::toString (newState) << '.' << std::endl;
}
void onInboundApplicationMsg (Message&, Session*) override
{
;
}
};
class SchedulingIssueDetector : public SessionSchedulerListener
{
public:
SchedulingIssueDetector()
{}
~SchedulingIssueDetector()
{}
void onLoggingOut (const SessionScheduler&, Session*, bool* /*allowLogout*/) override
{}
void onInitiatorConnecting(const SessionScheduler &, Session *, const Counterparty & /*counterparty*/) override
{}
void onWarning (const SessionScheduler&, Session* session, const std::string& warningReason) override
{
std::cout << "Scheduler reported a warning for the session " << static_cast<const std::string&> (*session) << ": " << warningReason;
}
void onError (const SessionScheduler&, Session* session, const std::string& errorReason) override
{
std::cout << "Error occurred while scheduling session " << static_cast<const std::string&> (*session) << ": " << errorReason;
}
};
class Sample
{
public:
Sample()
{
representYourself();
initializeFixEngine();
constructSessions();
constructScheduler();
}
~Sample()
{
initiator_->shutdown();
initiator_.reset();
acceptor_->shutdown();
acceptor_.reset();
scheduler_.reset();
Engine::shutdown();
}
void run()
{
acceptor_->resetLocalSequenceNumbers();
initiator_->resetLocalSequenceNumbers();
acceptor_->logonAsAcceptor();
std::cout << "Scheduling Initiator session " << static_cast<const std::string&> (*initiator_) << " for automatic connection." << std::endl;
SessionSchedule initiatorSchedule = constructShortTimeActivitySchedule();
InitiatorConnectionSettings initiatorConnectivity ("localhost", sessionPort() );
scheduler_->add (initiator_.get(), initiatorSchedule, initiatorConnectivity);
std::cout << "Waiting for activity on scheduled session " << static_cast<const std::string&> (*initiator_) << '.' << std::endl << std::endl;
// Should sleep for a while to let Scheduler connect Initiator session.
OnixS::Threading::ThisThread::sleep (1000 * sessionActivityTimeInSeconds() );
waitUntilLogout (initiator_.get() );
std::cout << std::endl << "Removing Initiator session " << static_cast<const std::string&> (*initiator_) << " from scheduling service." << std::endl;
// Session had to 'pulse' connection till this time, so scheduling can be destroyed.
scheduler_->remove (initiator_.get() );
// Clean-up.
acceptor_->logout();
waitUntilLogout (acceptor_.get() );
}
void useConfigurationFile()
{
initiator_->resetLocalSequenceNumbers();
SessionSchedulerOptions schedulerOptions;
schedulerOptions.configurationFile ("SchedulerConfiguration.xml");
SessionScheduler scheduler (schedulerOptions);
scheduler.add (initiator_.get(), "ScheduleId", "ConnectionId");
OnixS::Threading::ThisThread::sleep (1000 * sessionActivityTimeInSeconds() );
scheduler_->remove (initiator_.get() );
}
private:
MySessionListener acceptorListener_;
SessionStateChangeTracer initiatorStateChangeTracer_;
SchedulingIssueDetector schedulingIssueDetector_;
void constructScheduler()
{
SessionSchedulerOptions schedulerOptions;
schedulerOptions.eventListener (&schedulingIssueDetector_);
scheduler_.reset (new SessionScheduler (schedulerOptions) );
}
void constructSessions()
{
acceptor_.reset (new Session ( acceptorCompId(), initiatorCompId(), protocolVersion(), &acceptorListener_) );
initiator_.reset ( new Session (initiatorCompId(), acceptorCompId(), protocolVersion(), &initiatorStateChangeTracer_) );
}
static SessionSchedule constructShortTimeActivitySchedule()
{
TimeOfDay now = TimeOfDay::now();
TimeOfDay logonTime = now + SchedulingTimeSpan::Seconds (5);
TimeOfDay logoutTime = logonTime + SchedulingTimeSpan::Seconds (sessionActivityTimeInSeconds() );
return SessionSchedule (
DayOfWeek::Monday,
DayOfWeek::Sunday,
logonTime,
logoutTime,
SessionDuration::Day,
SequenceNumberResetPolicy::Never);
}
static void waitUntilLogout (Session* session)
{
const unsigned oneSecondPause = 1000;
while (session->state() != SessionState::Disconnected)
}
static void initializeFixEngine()
{
EngineSettings settings;
settings.licenseStore ("../../license")
.listenPort (sessionPort() );
Engine::init (settings);
}
static std::string initiatorCompId()
{
return "Initiator";
}
static std::string acceptorCompId()
{
return "Acceptor";
}
static ProtocolVersion::Enum protocolVersion()
{
return ProtocolVersion::FIX_42;
}
static int sessionPort()
{
return 4500;
}
static int sessionActivityTimeInSeconds()
{
return 30;
}
static void representYourself()
{
std::cout << "OnixS C++ FIX Engine Session Scheduling Sample." << std::endl << std::endl;
}
};

More Details