The Fix Gateway is a FIX Engine with a simple Application
Programming Interface (API). It is very easy to use and requires the
programmer to use only a few methods of a C++ FixClient
class. Their are versions available for Linux and WIN32 systems. The system
includes a sample fix server program that can
read either journal files or files written in a simple scripting language
that makes testing your application very easy. (For Windows users only,
the C++ Fix Class Library is further encapsulated as an ActiveX
control.)
The FixGateway can be configured to create its Fix Sessions over Secure
Sockets (SSL), but in order to do that, the
FixGateway/SSL
Integration Kit is required.
This package includes a C++ class library API and ActiveX
control. The API is also separately available as a C#
(.NET CLR) assembly The 3 versions of the API are,
as near as possible, identical.
The API allows for easy switching of i/o from on line data to files,
and, of course, provides a utility class for building
and interrogating fix messages.
For more information about the FIX Protocol see http://www.fixprotocol.org
Contents
System Components
There are 2 system components, a FixGateway process (or Fix Engine) that
is started automatically when needed, and an API Library that you must
link to your application in order to access the fix gateway.
Note: the fix process is not a daemon (or Windows service, or COM object)
it is started automatically by the fixgwlib when needed. The fixgwlib
creates a fix gateway session passing in the name of a configuration file.
This causes the fix gateway process to start up and read the config.
using information it find in the config file, it will connect, using tcp/ip
sockets to an external Fix Engine. It will communicate with the User Application
via a local socket. The User Application enters a Fix Message loop receiving
and sending messages.
Windows users note: no DLLs are added or removed from your system during
installation.
There is a separate package available that supplies a C#
(.NET CLR) Assembly that can be used as an alternative to the C++ library.
Getting Started
You must include the fix client header, #include <fixcli.h> and
link the fix gateway static library libFixGW.a on unix systems or
fixgw.lib
on WIN32 systems - you must also ensure that any libraries required to
access the socket interface are also linked in. You are now ready
to write your fix application. The standard pattern of a fix gateway application
is a message loop which sends and receives fix messages. The following
is the basic structure of the fixtest sample application
// FixTest...
#include <fixcli.h>
LPCTSTR configName = "loopback";
// loopback is a standard
config file supplied with the distribution, it
// allows a fix client
(namely fixtest) to connect to the fixgwsrv
application
FixClient fixSession;
if( !fixSession.Open(configName) )
// report error
if( !fixSession.SynchronizeOutputBuffer() ) report error
// you only need to make
this call if your application
// uses the BufferedOutput
methods; this ensures that
// if your app crashed
while processing BuffereOutput,
// the messages are not
lost - if the remote fix gateway
// is not yet connected,
the messages will be stored
// internally until a
connection is made
// you could make this call here if you wished,
// but there is no requirement to...
// if( !fixSession.WaitForConnection()
) report error
// this is probably of
more use when running
// the fix gateway as
a server
FixString sfix;
for(;;)
{
FixError ok = fixSession.GetFixMsg(sfix);
if( !ok )
break
int msgType = sfix.GetType();
swicth(msgType)
{
// process the message
...
}
fixSession.SetComplete();
// must call this once you are happy that a
// fix message has been fully processed
}
Your application will be in a fix message loop, if your application needs
to process WIN32 or X/Windows messages, the best thing to do is put the
fix message loop in a worker thread, leaving the User Interface
thread free. If you need your User Interface thread to communicate with
your Fix Message thread, e.g. to close the fix session, you can create
a CntrlInterface object in the User Interface thread and pass it
to the FixClient object in the worker thread. You can then pass messages
to the worker thread by using the CntrlInterface object in in the User
Interface thread. The fixgwsrv sample application
uses this technique and implements a Simple (character based) User
Interface thread and a Fix Worker Thread. The following code fragment demonstrates
the technique.
// fixgwsrv
#include <fixcli.h>
#include <cntrlif.h>
/*
**user interface thread
*/
CntrlInterface *pctrl = new CntrlInterface;
pctrl->InitControlSide();
// create worker thread here, passing
in pctrl
// E.G CreateThread(...pctrl...) or pthread_create(...pctrl...)
// NB: if you have more than one worker thread,
each muts have a
// different control interface, they cannot
share a common control
// interface!
pctrl->WaitForActivation(); // still in client
thread
// this returns when the worker thread is active, i.e. before
// a connection to another fix engine has been established
/*
**worker thread
*/
LPCTSTR configName = "testsrvr";
// testsrvr is a standard
configuration file supplied with the distribution, if
// the fixgwsrv application
loads the config, it will run as a Fix Server, and
// can be connected to
by any fix client, in particular, the fixtest sample
// application
fixSession.DisableAutoRecovery();
// calling this avoids
the need for calling SetComplete(), but
// if the app crashes
while processing a message, the message
// will be lost - it MUST
be called before OPEN if it is to be effective
FixClient fixSession;
FixError fixResult = fixSession.Open(configName, NULL,
pctrl);
//
pctrl
is created in the UI thread, and passed to the worker,
// it is the UI threads responsibility to delete the objet when
// it no longer needs it - the pointer is not stored by the
// worker thread, instead, parameters are copied from it to allow
// communication
between the 2 threads, so deleting the object
// in the UI thread will never leave the worker thread with an
// invalid pointer
//
// NB: if configName is already open in this process or any other
// process, an error will be returened here - multiple fix sessions
// are permitted in one process or in multiple process but not multiple
// sessions using the same config!
if( !fixResult )
// report error
// the session is now open, i.e. the gateway
procss has been created without
// errors, and, if running as a server, (as
it is in the fixgwsrv sample),
// it is waiting for another Fix Engine to
conect to it
fixResult = fixSession.WaitForConnection();
// this is an optional
call introduced in reales 5, it returns
// only if a connection
has been made with another Fix Engine,
// or if an error occurs,
or if a control message has been sent
if( fixResult.IsControlMsg() )
// procss control message
else if( !fixResult )
// report error
FixString sfix;
for(bool loop=true;loop;)
{
fixResult = fixSession.GetFixMsg(sfix);
if( fixResult.IsControlMsg() )
{
long ctlMsg = fixSession.GetControlMsg();
//
process control message
}
else if( !fixResult )
{
//
fix session terminated
}
else
{
int fixMsgType = sfix.GetType()
//
process fix message
}
}
Now imagine the situation where you do not have an online fix connection.
Instead, a 3rd party delivers a file of fix messages to you, you then process
the file and
generate an output file of fix messages which you return to the 3rd
party. How can this be achieved? the answer is very easily using the FixFileClient
class.
The following code excerpt demonstrates this, and is almost identical
to the 1st excerpt above...
// FixTest with input/output from/to
an on-line connection or files
#include <fixcli.h>
LPCTSTR configName = "loopback";
// loopback is a standard
config file supplied with the distribution, it
// allows a fix client
(namely fixtest) to connect to the fixgwsrv
application
FixClient *pFixSession=NULL;
if( use_file_io_flag )
pFixSession = new FixFileClient(inputFileName,
outputFileName);
else
pFixSession = new FixClient;
if( !pFixSession->Open(configName) )
// report error
FixString sfix;
for(;;)
{
FixError ok = pFixSession->GetFixMsg(sfix);
if( !ok )
break
int msgType = sfix.GetType();
swicth(msgType)
{
// process the message
...
}
pFixSession->SetComplete();
// must call this once you are happy that a
// fix message has been fully processed
}
Configuration File Format
The configuration file describes the remote Fix Engine and local Fix Gateway
optional settings. By default, configuration files reside in /Octatec/fixgw/Home/configs
on UNIX/Linux systems and C:\Program Files\Octatec\fixgw\configs
(or under whatever directory you chose during the installation) on WIN32
systems. Configuration files should have a file extension of .cfg. When
you call FixClient::Open() you must pass
in the name of config file - you may pass in a full pathname, but more
usually you will pass in the file name only, without the extension, in
which case the file must reside in the fixgw config directory.
WIN32 users may have expected such configurations to be in the system
registry, but to keep differences to a minimum between WIN32 and Linux/UNIX
implementations, a configuration file is used in preference to theWIN32
registry.
The config file contains a list of names followed by values
as follows.
The following values define the fix session
LocalId local
This is the name of the local fix session and is used in the outgoing
fix header
LocalSubId local_sub
This is the sub-name of the local fix session and is used in the outgoing
fix header
TargetId testsrvr
This is the expected name of the remote fix engine
TargetSubId testsrvr_sub
This is the expected sub-name of the remote fix engine
NB: these 4 IDs are not hostnames, they just strings chosen by the
2 sides of the fix conversation. Both sides must each know the others IDs
and specify them correctly in the config file. If SubId's are not
being used, they can be left out of the config file, or given values starting
with a '#' character. If the gateway detects a mismatch of IDs during logon,
it will logout from the remote, unless CheckLogonSubIDsis
set to 0
Host hostname
The host name to which the Fix Gateway will connect. The FixGateway
will attempt to connect to hostname as a client. If the
name is local, the local system is used. If the hostname
is specified as an asterisk, *, the Fix Gateway will act as a server,
i.e. it will listen for incoming calls and accept the first one. Only the
first incoming call will be accepted - multiple connections to clients
is not supported within a single gateway process, however multiple gateway
process are permitted. For added security, you can specify a server using
*N.N.N.N,
in this case, the server will only accept calls from IPaddress N.N.N.N
(note:
you must use the actual IPaddress and not a hostname when restricting clients
in this way.)
Port n
This is the port to used by the Fix Gateway.
FixVersion MAJ.MIN
This can be used to set the fix version, the default is 4.0. Currently
MAJmust
be 4. As of writing Fix 4.2 is the latest release release, so MIN
should only be 0, 1 or 2.
GmtOffset n
This parameter allows you to set a ‘virtual’ time zone for the fix
gateway. The client application will still see the local time-zone as normal
but the FixGateway process will see the timezone as GMT+n
(n can be +ve or –ve). The real implication of this
is that Fix Message headers will leave with the Virtual Timezone in the
time-stamp and midnight will also be dictated by the Virtual Timezone;
midnight is important for FIX 4.0 and FIX 4.1 (and even FIX 4.2, when sessions
are being re-set automatically) since the Fix Session is automatically
reset when a session is restarted after midnight. The default value is
99, if GmtOffset is 99, the Virtual Timezone is the same as the system
timezone.
BufferSize n
By default, buffer size is 1024, and the system will not let
you set it below 512. If you expect to receive particularly long
Fix Messages (larger than 1012 bytes) you can increase the size
of the buffer used to read incoming messages here.
FIXML_addhdr n
If this is 1, a <Header>…</Header> section is automatically
added to your outgoing XML. This duplicates the information in the standard
FIX header, and is part of the FIXML specification. If you set this flag
true, you don’t have to worry about adding te FIXML header yourself. If
you set this flag to 0, you can either add FIXML header information yourself
or leave it out if that is what has been agreed locally. In all cases the
standard FIX header is automatically included by the FixGateway process
and you shold not attempt to add header fields using FixString::AddField()
.The default value of this field is 0.
FIXML_addroot n
If you set this to 1, your outgoing XML will be automatically enclosed
in <FIXML><FIXMLMessage> … </FIXML></FIXMLMessage>
tags, so you don’t have to worry about adding them yourselves. (Of course
this is only appropriate if your XML payload is actually FIXML) The default
value of this field is 0.
The following values control optional behaviour of the fix gateway
NB: there are quite a few parameters here, but mostly, the deault values
will be what you want.
ConfrimResend List_of_Msg_Types
This setting allows your application program to decide is a message
should be resent or not. List_of_Msg_Types is a
colon-separated list of message type that you do not want to be resent
automatically if a resend is requested by the remote side, e.g. D:E.
When a message that appears in this list needs to be resent, your application
is asked is the message is to be resent, if you
say no, a gap fill is sent instead. List_of_Msg_Types
can be a single asterisk (*), in which case, resend confirmation
is requested for all non-admin messages. By default all messages are always
resent automatically (except admin messages, of course, which are always
replaced with GapFills)
ForwardHeartbeat n
If n is 1, heartbeats
will be received by the user application through the FixClient::GetFixMsg()
method, if 0, heartbeats are not delivered to the user
application. This might be useful if your application needs to perform
housekeeping actions at regular intervals. It is also a useful way for
the gateway to detect if your application has crashed or not - the gateway
will only detect that your application has crashed when it tries to send
data to it. The default value is 1.
ForwardLogon n
If n is 1, logon messages will
be received by the user application through the FixClient::GetFixMsg()
method, if 0, logon messages are not delivered to the user
application. The default value is 0.
ForwardRejection n
If n is 1, rejection messages
will be received by the user application through the FixClient::GetFixMsg()
method, if 0, rejections are not delivered to the user
application. The default value is 0.
TolerateRejection n
If n is 0 rejections are always
allowed, if n is > 0, that number defines
how many rejections will be received before the Fix Gateway automatically
closes. The default value is 1, i.e. the gateway automatically closes after
receiving one session level rejection.
HeartBtInt n
The heart beat interval, in seconds, passed in the login message, this
is 15 by default
AlwaysUseOpenEndedResend n
If the value of n is 1, resend
requests are always open-ended, if the value is 0,
resend requests are terminated with the current required sequence number.
the default value is 1.
SupressNestedResendRequests n
If n is 1, the sending of resend requests after just
issuing a resend are suppressed for a maximum of ResendSupressionTimeout
seconds. This parameter is designed to avoid the situation where the sender
has buffered up a lot of messages, but the first message triggers a resend.
In this situation, the 2nd message would also trigger a resend, as would
all the messages in the buffer, resulting in a lot of resend requests arriving
at the sender. After ResendSupressionTimeout seconds have
elapsed, normal resend requesting behaviour is resumed. The default value
is 1.
ResendSupressionTimeout n
This parameter controls the number of seconds for which the special
behaviour is performed. The default is 10 seconds.
HonourOutOfSeqResend n
This flag allows special action to be taken when a deadlock situation
arises. When both fix engines start up, and both discover the Logon
message contains an invalid sequence number, both sides will issue resend
requests, but both sides will ignore the others resend because it contains
an invalid sequence number. If this flag is 0, no special
action is performed, if it is 1, a resend request with
an invalid sequence number is still processed, and the requested messages
are resent, if it is -1, a resend request with an invalid
sequence number is rejected and a logout is initiated. The default value
is 1.
JournalIncoming n
If n is 1, incoming messages
are saved in a journal, if 0, incoming messages are not
journaled (The default value is 1). Outgoing messages are always journaled.
The Incoming (Receive Journal) plays a part in ensuring messages do not
get lost, so read the section abount SetComplete()
before switching this off.
FlushJournalAlways n
This defines how the journal file and other logs are managed. The default
is 0, under When the value is 1, the journal
and log files are closed after each write, and reopened for the next write
- doing this is more secure than keeping the log open all the time, however,
it is not done by default because the application detects if anything goes
wrong, such as a memory access error, (or a power failure under UNIX),
allowing the logs to be flushed and saved. If this value is 0,
performance will be improved, but on WIN32 system, there is a greater risk
that messages will be lost if the system looses power, since Windows does
not inform applications of a loss of poweer as UNIX does - a further consideration
when running under WIN32 with NTFS is the file-system buffering strategy
- this may need to be switched off to have a fully resilient
system - you should take advice from your system supplier or technical
support department if you require a fault tolerant configuration.
LogDir path
This value defined the log directory. The first
character may be a
% as in %/logs, in which case the %
is replaced by the Installation Directory under WIN32 and by /Octatec/fixgw/Home
under UNIX/Linux.
NB: the directory must exist. A sub directory
with the name
YYYYMMDD is created in the log directory, and logs
and journal files are saved here. The fix gateway should close down at
least once every 24 hours so that a new log directory is allocated. Ideally,
the fix gateway will be closed sometime before midnight, and restarted
sometime after; however, if you plan to use manual session resets, as permitted
under FIX 4.2, this is not required. NOTE: the path cannot contain spaces,
if you are on a WIN32 system and youre path does contain spaces, use the
short-form of the path, if you are on a UNIX system, you could create a
symbolic link (that doesn't contain spaces) to the directory. NOTE:path
must be less than 128 characters.
CliLogDir path
This entry defines where the client logsare
placed. Client logs are less important than server logs, but still useful
if you encounter problems. If this entry is not present, the environment
variable FIXGWLOG is used, and if this is not set, %TEMP%
(or $TEMP or /tmp under UNIX) is used, Note: this parameter
always takes precedence over FIXGWLOG. If this directory doesn’t
exists, the application will display an error message and exit – it is
better to know straight away that there is a problem rather than try and
search for the non-existent log at some later date
LogLevel n
This defined the level of logging, 0 is the least verbose
and 2 is the most verbose. The default value is 2.
CheckLogonSubIDs n
If any of the IDs (TargetSubID etc) in the config file are blank or
start with a hash (#), then the ID is not added to the Fix message
that is sent. When we process a logon, the TargetID must match the in-coming
SenderID, and the target SubID must match the incoming sender SubID unless
the TargetSubID is blank or starts with a hash (#), in which case,
we are 'not' processing SubIDs. If you want to send a TargetSubID,
but not validate the returned SubID in the logon message, you must set
this parameter to 0. The default value is 1.
ManualSessionSwitch n
This flag must be set to 1 if you are
using fix version 4.2 or higher, and you want to use the
ResetSession()
method. The default value is 0. When this flag is 1,
the log directories will have a 'version number'
attached to the end of their name. Note: if this flag is set, the session
will run until one side issues a ResetSession().
With this flag set to 0, a session is re-established at
midnight local time (or local 'virtual' time)
- but further note: this only occurs when the fix gateway re-starts, hence,
to cause a session reset when this flag is set to
0,
you must close the fix gateway sometime before midnight, and re-start it
after midnight.
CliSndBufSize n
CliRecvBufSize n
These parameters specify the send and receive tcp/ip socket buffer
size used within the client library when communicating with the FixGateway
process, the default value is 0 which indicates the system-default is to
be used.
SrvInSndBufSize n
SrvInRecvBufSize n
These parameters specify the send and receive tcp/ip socket buffer
size used within the FixGateway process when communicating with the client
application, the default value is 0 which indicates the system-default
is to be used.
SrvOutSndBufSize n
SrvOutRecvBufSize n
These parameters specify the send and receive tcp/ip socket buffer
size used within the FixGateway process when communicating with the remote
Fix Engine, the default value is 0 which indicates the system-default is
to be used.
UseLRCP n
See the implementation note at the end of this
document. (The default value is 1)
String values can contain embedded spaces if they are surrounded by
double or single quotes. Lines beginning with # are comments.
LogFiles and Journals
FixGateway's logfiles and journals
The location of these files is very important, and is controlled by the
configuration
file's LogDir entry. These are created in a directory named
YYYYMMDD_0 located in the Log Directory specified in the config
file. Note: if ManualSessionSwitch is set to 1
in the config file, the directories are
named YYYYMMDD_N, where N is a version number that is
incremented whenever a session is reset within
any 24-hour period - if the reset takes place in a different 24 hour period,
the YYYYMMDD part of the name will change and the N part
will be 0.
One way of reseting the sequence number back to zero is simpley to delete
these files.
The files created are as follows.
FixGateway.log
The general log file of the fix gateway, the LogLevel entry in the
configuration file applies to this file only
FixJnl-TargetId.txt
The journal file - all outgoing messages are recorded here to enable
resends of messages missed by the remote system.
FixRcv-TargetId.txt
The incoming journal - this is an optional file, and does not have
any particular use.
SeqNo-TargetId.txt
A record of the current incoming and outgoing sequence numbers.
As already mentioned, you should endeavour to ensure that the fix gateway
is closed down some time before midnight and started sometime after, so
that the fix sequence numbers will restart at 0. It is the act of starting
the fix gateway with an empty log directory that resets the sequence numbers
to 0.
Client Library Log
The client library also creates a log file, FixGatewayClnt.log.
The
location of this log is controlled by the config
paramater CliLogDir. If this entry is not present,
the environment variable FIXGWLOG is used, and if this is not
set, %TEMP% (or $TEMP or /tmp under UNIX) is
used. Once the base directory has been decided, and directory
with the same name as the config file is created, and then a sub directory
named YYYYMMDD_c is also created, finally the logfile is
created in that directory. To summarize, the client library logfile is
created as follows...
$FIXGWLOG/ConfigName/YYYYMMDD_c/FixGatewayClnt.log.
The sequence number of the last successfully processed fix message (
as defined by the FixClient::SetComplete()
method) is saved in $FIXGWLOG/ConfigName/YYYYMMDD_c/setcomplete.log
If the client application is using the output buffering mechanism, a
buffer file is created in $FIXGWLOG/ConfigName/buff.txt, this
stores unflushed messages, when the buffer is flushed, it is renamed, $FIXGWLOG/ConfigName/buff.cmt,
thus should the application fail during flushing, the flushed buffer will
be processed by the FixClient::SynchronizeOutputBuffer()
call, and messages will not be lost.
FIXML Support
From release 18, support for FIXML was introduced. This support allows
you to build ApplicationMessage FIXML sections using your favorite
XML DOM implementation and pass these to the gateway, the gateway will
add all the required FIXML headers around your ApplicationMessage.
To activate this functaionity, you need to set FIXML_addhdr
and FIXML_addroot config parameters.
To make setting the XML payload of a standard FIX message slightly easier,
the FixString::SetXmlPayload()method
is provided.
You can find more details about FIXML support
here.
The FixGW Class Library
To use the FixGW class library your system must support the sockets API
and you must link to libFixGW.a on UNIX/Linux systems or fixgw.lib
on Windows systems.
NB: many methods in this object return a FixError
object to indicate their success or failure. Note: there is no reason why
you shouldn't instantiate multiple FixClient objects in the same
application, if that's what you want.
#include <fixcli.h>
FixClient(bool noIntHndlr=false)
Create and initialize a FixClient object. The object is not connected
to the fix gateway until Open() is called. (Note: if you want
to install your own SIGINT [Ctl-C] Interrupt Handler,
either
pass in true to the constructor or install it after
the construction of the FixClient , as this object installs its own Ctl-C
handler. Probably your handler will close the fix session)
~FixClient()
Close any open session before destroying the object.
FixError Open(LPCTSTR configFile, LPCTSTR gatewayHost=NULL, CntrlInterface
*pCtrl=NULL)
Open a fix session. The parameters of the fix session, e.g. IP address,
port etc., are held in the configFile. The config file can be a full pathname,
but more usually, it is just the filename, without extension - given such
a value, the fix gateway will look (under UNIX) in /Octatec/fixgw/Home/configs(and
under WIN32) in C:/Program File/Octatec/fixgw/configs for
a file of the same name and a .cfg extension. NOTE: gatewayHost
must
currently be NULL, and is for future use. If you choose to use the full
path specification for config files, the path length must be less than
128 characters. (You should try and keep config names (not
path names) less than 32 characters, as the config file name will be
truncated to 32 when the log-directory is created).
The same application can have many FixClient objects, and open multiple
fix sessions if it wishes, however, it should only Open
a configFile once, i.e. if you wish to open 2 or more
sessions to the same client, you must create separate config files for
each session, the files can just be copies but must have different names,
e.g. MyBroker_1,
MyBroker_2 etc. The reason for this
is that the configFile name defines where the fix log files are
created. Attempts to open the same configFile more than once in the same
process or even in a different process will fail.
FixError Open(DynamicFixCfg &cfg, LPCTSTR gatewayHost=NULL,
CntrlInterface *pCtrl=NULL)
This method works just like the previous method, except it takes a
DynamicFixCfg
object rather than the name of a config file - this allows you to create
a configuartion at runtime rather than relying on a pre-created config
file.
FixError WaitForConnection()
You may use this method to wait until a connection has been established
with the remote fix engine. The method only returns when (a) the Local
Fix Gateway makes a connection to a remote Fix Engine, or (b) if an error
occurs, or (c) if the CntrlInterface is in use and a Control Message has
been sent by another thread. You do not need to make this call, you can
go straight into reading messages or sending messages, (if messages are
sent and no connection is ever established, they are not lost, they are
stored in the local Fix Gateway, if you try to read before the connection
is established you will just wait until the connection is made). This method
is, perhaps, of most use when the local Fix Gateway is acting as a server.
bool IsConnected()
This method returns true if the local Fix Gateway is connected to a
remote Fix Engine.
FixError GetFixMsg(FixString &str)
This is the main method of the object. Your program should enter a
loop calling GetFixMsg() until it returns an error. When the method
returns without an error, the str parameter contains a
fix message. You should query the returned FixError object to
see if the message returned was a Fix Message, or a Control
Message. Mostly, it will be a Fix Message. If it is not
a fix message, the str parameter is meaningless and you
must use GetControlMsg() to get the ControlMessage
information.
FixError SendFixMsg(int type, FixString &str)
This method is used to send a FixMessage to the remote fix engine.
You should set the fix-fields of the str parameter and
then send the message. The type parameter should be one
of the
FIX_MSG_ values found in fixconst.h, or
any other value denoting a locally agreed fix message type.
void GetProtocolVersion(long &maj, long &min)
Get the protocol version currently active. NB: this is defined
by the config file.
FixError ResetSession()
This resets sequences numbers back to 1 on both sides of the conversation,
using the Reset Session mechanism introduced in protocol version 4.2. If
the protocol version is bellow 4.2, this method returns an error. When
the session is reset, a new log directory is created.
For this method to work, ManualSessionSwitch must be set
to 1 in the config file.
FixError ResetLogLevel(int n, bool gatewayOnly=true)
This method can be used once a connection is established to reset the
log level, valid values are 1 2 or 3. If gatewayOnly
if
false, then the client log level is also reset.
FixError ResetForwardHtBtFlag(bool flag)
This method can be used once a connection is established to reset the
heartbeat forwarding strategy, if flag is true,
heartbeat messages are passd to the client app.
FixError ResetRejectionTolerationLevel(long value)
This method can be used once a connection is established to reset the
rejection toleration level. If the value is 0, rejections
are always allowed, otherwise, the gateway will close down after receiving
the specified number of rejections. A value of 1causes
the gateway to close immediately it receives a rejection from the remote
Fix Engine.
FixError ResetForwardRejectionFlag(bool flag)
This method can be used once a connection is established to reset the
rejection forwarding strategy, if flag is true,
fix-protocol rejection messages are passd to the client app.
FixError ResetManualSessionSwitchFlag(bool flag)
This method can be used once a connection is established to reset the
ManulSessionReset
flag (see the config file for more details
about this flag.) If flag istrue, and
the fix version is 4.2 or above, a new fix session will only be
started if either side initiates a new session (using ResetSession).
FixError BeginBufferedOutput()
After this method has been called, SendFixMsg() merely
records the message in a local persistent buffer.
FixError AbortOutputBuffer()
This method closes and discards the local buffer.
FixError FlushOutputBuffer()
This method closes the local buffer, and then sends all messages to
the fix gateway. As each message is sent, it is marked as processed,
so that if your application fails or is killed, when it restarts, you can
use SynchronizeOutputBuffer() to send any pending messages
to the gateway. Once FlushOutputBuffer() or AbortOutputBuffer()
is
called subsequent calls to SendFixMsg() sends messages
directly to the gateway, until BeginBufferedOutput()
is
called again.
The fixtest sample demonstrates the use of output
buffering and SetComplete().
FixError SynchronizeOutputBuffer()
You can use this method when your application starts, it will send
any pending messages in the local buffer to the gateway. You should only
have pending messages if your application failed or was killed. If
you make use of the buffering mechanism, you should call this method immediately
after Open() returns success.
bool SetComplete()
This method tells the system that all messages received since the last
call to SetComplete() (or since the 1st message was received
if SetComplete() has not yet been called) are fully processed.
Until SetComplete() is called, all messages currently received
will be received again should your application crash or be killed. Depending
on how your application is structured, you may call SetComplete()
after
each message has been processed or wait until a group of messages, i.e.
a list, have been processed. SetComplete() is implemented
by saving the sequence number of the last completed message, and then passing
this to the gateway at startup - the gateway compares this to the last
application-level message received, and if it is lower, the gateway requests
a resend from the remote fix engine. If you call DisableAutoRecovery()
there is then no need to use Setcomplete(), but this
is not recommended.
bool SetComplete(long lastProcesedSeqNum)
This form of SetComplete() allows you to set which
sequence number you are 'happy' with. You can extract the sequence number
from the Fix Message.
void DisableAutoRecovery(bool mode=true)
This method disables the SetComplete() mechanism. However,
doing this is dangerous, since if your application (not the fix gateway)
crashed or was kill after it received the message but before it was processed,
the message would be lost, as the fix gate will have received it successfully
and passed it to your application successfully as well. It must
be called before Open() if it is to be effective.
FixError Close(int grace=15)
Close the fix session. If you do not Close an open session, it will
be closed automatically in the destructor. If you open a 'server session',
and try to close it before a client has connected to it, you will hit a
snag - the gateway is 'busy' waiting for a connection, and will not see
the 'close' command, consequently, the Close() method will
wait up to grace seconds before forcibly closing
a 'server' GateWay if it doesn't close naturally.
FixError Reopen()
If you get an error from GetFixMsg() or WaitForConnection()
you can either call Close() and Open()
or just call Reopen(). If you get an error from Reopen(),
then you should call Close() and Open().If
you call Close(), Reopen() will not
work and you must call Open(). Any additional login
or header tags you have set will still be active after a Reopen(),
but will not be active after a Close(). NB: Reopen()
will
return an error if no connection has ever been established, i.e. it should
be used after a connection has been established and then closed by the
other side.
FixError RunScript(LPCTSTR path, long &numberOfMessages,
int reconnect=0, int maxretries=0)
Run a script, the number of messages processed is returned in numberOfMessages.
See
bellow for the format of a script file. If
reconnect is specified, the fix session is automatically reconnected
after waiting reconnect seconds - this process is repeated
for maxretries times (or forever if maxretries is
0). Note: if the configuration specifies the fix gateway is in server mode,
the reconnect will cause the gateway to wait for another connection. You
may pass -1 as the reconnect parameter, in which case their is no delay
before the reconnection attempt - you may want this if your fix gateway
is acting as a server. (see the configuration
file section for how to configure the gateway as a server)
FixError RunJournal(LPCTSTR path, long &numberOfMessages)
Process and send the messages in a journal file.
FixError FixClient::OverrideTargetSubId(LPCTSTR newId, bool permanent=true)
You can use this method once a connection has been established to override
the TargetSubID defined in the config file. If permanent
is false, the change is only applied to the next message
you send, otherwise it is applied to all subsequent messages (resent messages
are, of course, resent with their original TargetSubID)
FixError FixClient::SetAddionalLoginTags(const FixString &tags)
This method can be used before the FixClient::Open() call to
define some additional fields to be sent in the Login message - i.e. this
can be used to support non-standard login tags.
FixError FixClient::SetAddionalMsgTags(const FixString &tags)
This method can be used before the FixClient::Open() call to
define some additional fields to be sent in all outgoing messages
- i.e. this can be used to support non-standard header/trailer tags (actually
the tags are always added to the end of the message, but before the standard
fix trailer).
FixError ResetSessionAtLogon();
This method van only be used before a connection is established, i.e.
before the Open() call has been made. This instructs the
system to include a reset-sequence-number directive with the initial logon.
The sequence numbers will be reset to zero on both sides. This can only
be used, of course, if the sequence numbers are already ‘in-sync’ to start
with. This method can only be used with Fix 4.1 and higher.
FixError ResetSequenceNumber(long num)
This method provides an interface to the Fix Protocol RESET SEQUENCE
NUMBER message, sequence numbers can only be increased in value, so
the num parameter is added to the current value of the
outgoing sequence number. NOTE: you shoukld not attempt to send this
FIX message yourself, if you want to send the message, use this method,
however you are very very unlikely to actually want to do
this, you are very very unlikely to find this method useful
for any purpose!
void GetControlMsg(short &msg, StringType &s)
void GetControlMsgString(StringType &s)
short GetControlMsg()
These methods return the value of the control
message (and it's string parameter, if one was present)
SOCKET GetControlInterface(bool detatch=false)
This can be used if a ControlInterface is in use and
you wish to send a response back using CntrlInterface::SendResponse()
- the control messages values/contents are chosen entirely by you,
but the intention is that they be used for simple queries and
shutdown commands. If detatch=true, the ‘raw’ Control Interface
socket is ‘detached’ from the FixClient Object. The
purpose of doing this is so that the socket is not closed by the FixClient::Close()
method. You can then close the FixClient and the Control
Interface held by the UI thread will remain valid. Thus if you decide to
open another FixClient object, you can ‘reuse’ the Control
Interface without having to inform the GUI client. The fixgwsrv
sample shows the usage of this call.
bool FixClient::AttachControlInterface(SOCKET csd)
Attach a previous detatched ‘raw’ Control Interface. This call MUST
be made before FixClient::Open() is called, otherwise it
will fail. (If you pass a Control Interface Object in the Open()call,
then that is the interface that will be used, and the previously attached
‘raw’ interface will be ignored.)
FixError FixClient::ExpungeSessionData(LPCTSTR configFile)
This method can be used to discard all session information effectively
setting the sequence numbers back to zero for the local Fix Gateway. The
method can only be used when the session is not active, i.e. call
Close() first then call this method. This method is just a convenient way
for you to reset the sequence numbers if you want to, doing this discards
session data resting everything. In fact the session data is saved in a
director named “-N-{log directory}”, where
N
is a number that will always increase.
QueryOutSeqNum(int outwardSeqNum, FixString &s)
This method allows you to lookup a sequnce number in the outgoing message
journal. It might be used if you receive a message indicating an order
has been rejected and want to examine the information in the order-message
you sent.
FixError SetRecoverySeqNos(long
out, long in = -1)
This method can be used to recover a connection after complete
sequence number failure. To achieve this you must call this method
before
the FixClient::Open(), probably with out = 899999
and the default in parameter. After the connection has been established,
the session will be automatically reset, and both sides will have sequence
numbers of 0. IMPORTANT: do not use this method
as a 'matter -of-course' - only use it to recover the situation described
here.
This method can only be used with Fix 4.2 and higher.
bool FixClient::GetGmtOffset(int &offset)
If a Virtual Timezone is being used in the Gateway
process, this method returns true and sets offset
to the offset from GMT of the Virtual Timezone. To find the time
as viewed by the gateway process, use code like…
time_t t;
time(&t);
int offset;
if( fixcli.GetGmtOffset(offset) )
t += offset;
FixError ConfirmResend(bool okToResend)
Call this method in response to a Resend Confirmation
Request. You can configure the system to ask
for confirmation when resending time-sensitive messages.
FixFileClient is derived from FixClient, and so supports
all the methods on the above class. The following documented methods
are the ones that are overridden and have different behaviour. No gateway
process is created, instead, messages are read from an input file and written
to an output file. The format of the files is simply a sequence of fix
strings, separated by new-lines.
#include <fixcli.h>
FixFileClient(LPCTSTR infile, LPCTSTR outfile, bool simulateHeartbeats=false)
Create and initialize a FixClient object. The object is not connected
to the fix gateway until Open() is called.
~FixFileClient()
Close any open session before destroying the object.
FixError Open(LPCTSTR configFile, LPCTSTR gatewayHost=NULL, CntrlInterface
*pCtrl=NULL)
Open a fix session. The parameters of the fix session, are held in
the configFile. The config file can be a full pathname, but more usually,
it is just the filename, without extension - given such a value, the fix
gateway will look (under UNIX) in /Octatec/fixgw/Home/configs (and
under WIN32) in C:/Program File/Octatec/fixgw/configs for
a file of the same name and a .cfg extension.
FixError GetFixMsg(FixString &str)
This is the main method of the object. Your program should enter a
loop calling GetFixMsg() until it returns an error. When the method
returns without an error, the str parameter contains a
fix message. You should query the returned FixError object to
see if the message returned was a Fix Message, or a Control
Message. Mostly, it will be a Fix Message. If it is not
a fix message, the str parameter is meaningless and you
must use GetControlMsg() to get the ControlMessage
information.
This
method reads fix messages from the infile specified
in the constructor. If the simulateHeartbeats parameter
in the constructor is true and if the config
file specifies that heartbeats are to be passed to the clients, heartbeats
are generated and passed to the client at appropriate intervals.
FixError SendFixMsg(int type, FixString &str)
This method is used to send a FixMessage, it merely writes the message
to the outfile specified in the constructor. You should
set the fix-fields of the str parameter and then send the
message. The type parameter should be one of the FIX_MSG_
values found in fixconst.h, or any other value denoting a locally
agreed fix message type.
FixError ResetSequenceNumber(long num)
FixError ResetSession()
FixError Close()
FixError SendHeartbeat()
FixError SendTestRequest()
These methods all return FIX_OK, but perform no action !
FixError Close()
Close the fix session. If you do dot Close an open session, it will
be closed automatically in the destructor.
bool FilesOpenOk()
This method can be called after the constructor to check that the files
were opened without errors.
This object can be used instead of a static configuration file. Actually,
it allows you to programmatically create a config file; ultimately, a config
file is still used. One of these objects can be passed to the FixClient::Open()
call instead of the name of a Config file. If you use this facility,
don't be tempted to re-create a config with the same name, but with
a different host name as this will cause the fix journals to become mixed
up/corrupted.
DynamicFixCfg(LPCTSTR name)
Construct a DynamicFixCfg with the name of the configuration. Do not
use path-names or file extensions in the name, just use simple names.
LPCTSTR GetConfigName()
Get the name of the config, this will be the parameter passed into
constructor.
bool Set(ParamName param, LPCTSTR value)
Set the value of a parameter. All the possible values of param
are contained in the ParamName enumeration witin the class.
The values must always be strings. All the possible parameters of the config
file can be set using this method. The config parameters LocalId,
TargetId, Host and Port must all be set, all other
parameters have valid defaults. Note: if you don't specify a log directory,
%/logs/configName
is used, where % represents the installation directory - this directory,
or any you specify yourself, must already exist - it is not
created for you.
bool Save()
Save the configuration. This creates an actual config file. If you
pass the object to FixClient::Open(), this method is automatically
called, and the resulting config file is then used to actually perform
the open.
#include <fixcli.h>
All the fix methods return a FixError object to indicate their
success or failure. A FixError has one member, int m_err,
a value of 0 (FIX_OK) or higher indicates success. FixError
objects can be tested for success or failure using boolean operators.
FixError Values
FIX_OK (0)
no error
FIX_LOGOUT (-1)
the remote side logged out
FIX_DROPPED (-2)
the remote side droppend the line
FIX_CANT_CONNECT (-3)
can't connect to the remote
FIX_NO_CFG (-4)
the config file specified does not exist
FIX_NO_GTWY (-5)
the gateway process cannot be started
FILE_OPEN_ERROR(-6)
couldn't open the config file or an
inpuit/output file
FIX_IN_USE(-7)
the configFile specified in
the open call is already opened in this or another process.
FIX_BAD_DYN_CFG (-8)
you passed a DynamicFixCfg
object to FixClient::Open(), and DynamicFixCfg::Save()
failed.
FIX_BAD_ID (-9)
the other side of the
Fix Conversation has an unexpected ID or SubID, the IDs of the sender are
specified in the config file, and are
called
the TaregetID and TargetSubID,
these must match what the Sender actually sends, similarly our IDs may
well be checked by the Sender,
these are called the
LocalID and the LocalSubID in the config
file.
FIX_OUT_OF_SYNC (-10)
The gateway process is
issuing resend requests but the remote fix engine is rejecting these requests
FIX_BAD_FIX_VER (-11)
The gateway process detected
an incompatible Fix Version specified in the config files.
NO_ADMIN_INTERFACE (-1000)
this error can only occur when trying
to 'run' a script
or journal file, and
the session is not correctly initialized
NO_FILE (-1001)
the script or journal file you are trying
to run does not exits
WRONG_PROTOCOL_VERSION (-1003)
a session reset was attempted but this
is not allowed in the current protocol version
FIX_CLT_IF_ERROR (-992)
FIX_SELECT_ERROR0 (-993)
FIX_SELECT_ERROR1 (-994)
FIX_INIT (-996)
internal errors
FIX_ERROR (-999)
a general error
FIX_CONTROL_MSG (1)
not an error, and indication that the
message is a control message, not a FIX message
Methods
bool IsControlMsg()
bool IsFixMsg()
bool IsError()
After FixClient::GetFixMsg() returns a FixError object,
you can use the above methods to see what kind of message has been returned.
Equally, you can use the ! operator, to see if the object indicates
an error.
#include "cntrlif.h"
This class allows you to pass messages from a controlling thread, e.g.
a user interface thread into the fix message loop of a fix worker thread.
Once in a Fix Message Loop, the thread is waiting for messages from the
Fix Gateway and/or messages from the Control Interface. Unless you use
the Control Interface, their will be no way for you to control the Fix
Message Loop except when a Fix Message arrives. The main reason for implementing
the Control Interface object is so that a GUI thread can tell the FIX thread
to shut down. The fixgwsrv program shows how
the Control Interface can be used.
CntrlInterface(int remotePort = -1, LPCTSTR remoteHost = NULL)
The remotePort and remoteHost parameters are currently ignored.
~CntrlInterface();
bool InitControlSide();
Call this method first in the control (GUI) thread
bool WaitForActivation(bool sendStartMsg=true);
Call this method in the Control (GUI) thread after the worker thread
has been created, it will wait for the worker (FIX) thread to become
'ready'. Once the worker thread is ready, provided sendStartMsg
was true, WaitForActivation() will send a start
message allowing the worker thread to continue and connect to the remote
fix engine. If sendStartMsg was false, you must manually
send a start message when the time is right.
bool SendStartMsg()
You only need send a Start Message after if you passed in false
to WaitForActivation(), if you did, you must call this
method at some point after WaitForActivation() returns
true.
This allows you to control when the Fix Message Loop actually begins.
bool SendMsg(short iparam, LPCTSTR sparam=NULL);
You can use this method to send messages to a thread in a Fix Message
Loop, the messages are entirely under your own control but should all have
values above 100, values below 100 are reserve for internal use. It is
intended that this mechanism be used for simple queries and commands,
e.g. to shutdown the gateway. The messages will be received in the Fix
Message Loop via FixClient::GetFixMessage(), areturn of
FixError::FIX_CONTROL_MSG
from FixClient::GetFixMessage()
indicates that
you should call FixClient::GetControlMsg() and optionally
FixClient::GetControlMsgString()
to examine the message and any parameter. Normally, SendMsg()
will be called from the Control (GUI) thread to send a message to the worker
(FIX) thread, possibly to shut down, it is unlikely, but possible, you
may want to send a response to the message, to do this, use the SendResponse()
method
static bool SendResponse(SOCKET sd, short iparam, LPCTSTR sparam=NULL)
Call this method in the worker (FIX) thread to send a response back
to the Control (GUI) thread if you know one is required.
bool GetResponse(short &iparam, StringType &s)
If you are expecting a response to the message, you can use this method
in the Control (GUI) thread to wait for it. Mostly, messages you send to
the fix thread will not give rise to a response.
int GetLastError()
Return the last error value.
#include <fixstr.h>
This class provides the functionality for building fix messages and
accessing all the fields of the message. It is used extensively in the
Fix Gateway process, and in the Fix Gateway lib, but probably, you will
only need to use the Access methods, GetField(), AddField(),
and to a lesser extent UpdateOrAddField() and RemoveField().
The
fixconst.h
file
provides you with a list of current Fix Fields and Message Types. You can,
however, use any value as a Fix Field ID, and any value for a
Fix
Field Type.
FixString()
FixString(LPCTSTR s)
Construct a fix message string, either empty, or from a string
containing fix fields. Note: their is no validation of the string passed
into the constructor.
void Set(LPCTSTR s)
Set the contents of the fix string to the specified value. Note:
their is no validation of the string passed in.
int GetType()
Return the type of a fix message. This will be one of the FIX_MSG_*
values defined in <fixconst.h>
bool GetField(int name, StringType &value)
bool GetField(int name, int &value)
void AddField(int name, int value)
void AddField(int name, LPCTSTR value)
void UpdateOrAddField(int name, int value)
void UpdateOrAddField(int name, LPCTSTR value)
bool RemoveField(int name)
The above set of methods are likely to be the most useful. In all cases
name is just the numerical value of the field (tag). It is most likely
to be one of the FIX_FLD_* values in <fixconst.h> but
could be any value. The methods treat the contents of fields as either
strings or integers, if the field has a different type, e.g. date/time
or floating point, you must extract it as a string and convert it. (Their
are some helper methods for converting dates).
void SetXmlPayload(LPCTSTR xmlText)
This method discards any tags you ay have already set, just sets the
XmlDataLen
and XmlData tags based on the xmlText parameter.
You probably shoudn’t add any more tags to the message after this one,
although you can if you want. You can then send the message using any ‘type’
you wish, it is up to the receiving end to be prepared to decode your XML
payload if you use a traditional FIX-MSG-TYPE, or to understand any other
type filed you may choose to use, such as “XML” or “FIXML”. It is up to
you to serialize your XML into a flat sting before passing it to this method,
this might be as easy as reading it into a buffer from an xml file. Note:
if you are sending FIXML, there are a couple of config parameters to help
you, and a further discussion here.
bool GetXmlPayload(StringType &s)
This method is realy the equivalent of calling GetField(FIX_FLD_XmlData,
s) If there is no XML payload present, false is returned.
static void FormatDate(StringType &s, time_t
tim=0, bool localTime=true);
static void FormatTime(StringType &s, time_t
tim=0, bool localTime=true, int ms = -1);
Convert the date in tim into a fix format string. If
tim
is 0, then the current date/time is used. If
localTime
is false, the value of tim is treated as GMT. The output
is written into the string s. NB: if the fix protocol version
is 4.2 (or higher) and if ms > -1,
milliseconds are added to the formatted string. Note: if you are
passing in a virtual-time-zone adjusted time_t
value,
pass localTime=false.
static time_t ParseDate(LPCTSTR s, int isdst = -1); // YYYYMMDD
static time_t ParseTime(LPCTSTR s, int isdst = -1, int *pms
= NULL); // YYYYMMDD-HH:MM:SS.sss
Convert the date or date-time in s to a time_t.
The isdst parameter copied into a struct tm parameter
which is passed directly into the C library function mktime().
NB:
if the fix protocol version is 4.2 (or higher) and if pms
is
not NULL, *pms is assigned the milliseconds in the
string (or 0 if none).
void InitEnum()
bool EnumNextField(int &name, StringType &value);
These 2 methods enable the enumeration of all fields in a fix string.
You should call InitEnum(), once then repeatedly call
EnumNextField() until it returns false.
bool VerifyChecksum();
bool VerifyLength();
Methods to verify the value of a complete fix string. It is unlikely
you will need these methods
void EmptyString()
Clear the contents of a fix string
LPCTSTR GetString()
Get a pointer to the actual string
FixString(LPCTSTR myId, LPCTSTR mySubId, LPCTSTR targetId, LPCTSTR
targetSubId)
void SetIDs(LPCTSTR myId, LPCTSTR mySubId, LPCTSTR targetId,
LPCTSTR targetSubId)
LPCTSTR Compile(char msgType, int seqNum )
static bool SetFixVersion(int maj, int min)
The above methods are for advanced users and used by the Fix Gateway
Engine - it is unlikely you will need these methods, they build the actually
fix message header/trailer before sending it. Basically the way it
works is, you create your fix string with the 4 ID values, then add fields
that you need. Finally, you 'compile' your string, passing in
the type of the fix message and a sequence number. Note when setting the
fix version,
maj must be 4! Further note, the protocol
version is set in the config file, and
is a property of the gateway, not the client application.
#include <strtype.h>
This is a utility class for string handling. It is used extensively
in the Fix Gateway and Fix Gateway Library - you may use it in your application
if you wish. It has the following methods. It has much in common with the
MFC String Class.
StringType()
StringType(const StringType &s)
StringType(LPCTSTR s)
virtual ~StringType()
void Attach(char *s)
If you attach a pointer, the String object will delete it in its destructor
do not delete it yourself, - only attach pointers that have been allocated
using the new char[] operator, do not attach pointers
that have been created in any other way, e.g. using malloc().
StringType &operator=(const char *s)
StringType &operator=(const StringType &s)
StringType &operator +=(LPCTSTR s)
StringType &operator +=(const char ch)
StringType &operator +=(const StringType &s)
char GetAt(int i)const
char operator[](int i) const
void SetAt(int i, char c)
int Find(char c);
int Find(LPCTSTR s, int startAt=0)
Find the first occurrence of the specified string of character.
void Empty();
bool IsEmpty()
int GetLength()
bool operator==(const char *s)
const
bool operator==(const StringType s) const
bool operator!=(const char *s)
const
bool operator!=(const StringType s) const
operator LPCTSTR () const
void Format(const char * lpszFormat, ...)
This methods works like sprintf().
char *GetBuffer(int)
NB: the int parameter is never used. This method only returns a pointer
to the string, and never allocates memory! The pointer may be NULL.
Resend Confirmation
This facility was introduced in release 16. You can specify a list of messge-types
in the config file, for which confirmation will be sought if a message
of that type needs to be resent.
E.G. if you place the following entry in the config
file
ConfrimResend B
If a B (News) message needs to be re-sent because the remote
Fix Engine has requested a resend, your application will be notified of
this and you must respond with either true or false (if
you say false a gap-fill will be sent instead).
NB: under normal circumstances all resends occur transparently to the
application, but in some circumstances, you may not want to resend a message
if market conditions have changed significantly since the time that the
message was originaly sent.
The way to implement this in code is as follows…
FixString sfix;
for(;;)
{
FixError ok = pfix->GetFixMsg(sfix);
if( ok.IsResendConfirm() )
{
// sfix contains
the message that will be resent,
// examine it and call
ConfirmResend()
with true or false
if( !pfix->ConfirmResend(true) )
//
report error
continue;
// continue
listening for messages
}
else if( !ok )
// report
error
else
// process
fix message
}
Fix GateWay ActiveX Control (FixGWctl)
NOTE: the FixGW ActiveX Control is only included in the WIN32 version
of Fix GateWay.
The functionality of the FixGW class library is encapsulated (and further
simplified) by an ActiveX control. This control can be used from VB or
C++ applications -
both a VB and MFC sample is included with the distribution. It is even
easier to use the ActiveX control that the C++ library, since the complexity
of maintaining a User Interface and waiting for FIX messages is automatically
handled by the control. Note: there is no reason why you shouldn't have
multiple FixGWctl components in the same application, if that's what you
want.
The following VB code fragments demonstrate how the FixGWctl control
is used. Firstly, you will to insert a FixGWctl component from the
VB Project/Components menu, then use following calls...
Private Sub Connect_Click() '//
imagine a button that initiates a connection
'// to a 3rd party remote FIX Engine
FixGW1.DisableAutoRecovery True
'//
so we don't have to call SetComplete
'// after each send or group of sends
FixGW1.Open "loopback"
'//
a standard configuration that
'// defines the port and IPaddress
FixGW1.AsyncGetMsg
'// tell the control we are ready
'// to receive message notifications
End Sub
Private Sub SendMsg_Click() '//
imagine a button that sends a test
'// FIX News message to the remote
'// FIX engine
Static n As Integer
Dim text As String
text = "Test News " & n
FixGW1.InitMsg Asc("B")
'//
Initialize a FIX news message
'//
FIX_MSG_News
FixGW1.AddField 58, text
'//
Add a field to the message
'//
FIX_FLD_Text = 58
FixGW1.SendMsg
'//
if an error occurs you will be notified via
'// an error event
n
= n + 1
End Sub
Private Sub FixGW1_OnFixMsg(ByVal msgType As Long)
'//
this event is fired when a FIX message arrives
Dim text As String
Static count As Long
If (Chr(msgType) = "0") Then '//
FIX_MSG_Heartbeat
text = count & ":
GotFixMsg <HEARTBEAT> " & msgType
ElseIf (Chr(msgType) = "B") Then '//
FIX_MSG_News
Dim s As String
s = FixGW1.GetField(58)
'//
FIX_FLD_Text
'// NOTE - this method gets fields from the INCOMMING message
'// not the outgoing message that we have just sent
text = count & ":
GotFixMsg NEWS<" & s & "> " & msgType
Else
text = count & ":
GotFixMsg " & msgType
End If
count = count + 1
FixGW1.AsyncGetMsg
'//
tell the controll we are
'// ready to recieve fix messages again
End Sub
Private Sub FixGW1_OnFixError()
'//
this event is fired if any errors occur
Dim msg As String
msg = "FIX SESSION ERROR " & code
MsgBox msg
End Sub
And that's all there is to it. Very easy indeed. Equally as easy in C++.
For those interested in such things, the FixGWctl is a C++
control written using the ATL. It uses the FixGW
class library above to actually make Fix connections and send messages.
Method List
The full set of methods on the control are listed below...
[id(1)] HRESULT Open(BSTR
configName);
Use this method to connect to a remote FIX Engine. The configName parameter
is the name of a configuration file,
that, amongst other things, defines the IP-address
and port of the remote FIX Engine. If Open() has already
been called, and Close() has not been called,
an HRESULT of -3 is returned, if the Open()
fails for any other reason, an Error Event is fired.
[id(2)] HRESULT Close();
Use this method to close the connection to the remote FIX Engine.Close()
must always be called to close a session, even if Open()
failed!
[id(3)] HRESULT InitMsg(long
type);
Us this method to initialize a message for sending. The type parameter
is the type of the FIX message. Once this has been called, use th AddField
or UpdateField method.
[id(4)] HRESULT AddField(long
id, BSTR value);
[id(5)] HRESULT UpdateField(long
id, BSTR value);
[id(6)] HRESULT AddFieldInt(long
id, long value);
[id(7)] HRESULT UpdateFieldInt(long
id, long value);
These methods set fields on the message. AddField will add
multiple fields if the field already exists in the message, UpdateField
will Add the field if it doesn't exist, or modify it if it does.
[id(8)] HRESULT SendMsg();
Once all fields have been added , use this method to send the message.
[id(9)] HRESULT AsyncGetMsg();
This method can be called at any time after the connection has been
opened. Once called, the activeX control will deliver at most one message
to the application by means of an asynchronous ActiveX event. To obtain
subsequent messages, this method must be called again - it is usual to
call it as the last thing done in the method that handles the FixMsg
event.
[id(10)] HRESULT GetField(long id,
[out,retval]BSTR
*presult);
[id(11)] HRESULT GetFieldInt(long
id, [out,retval]long *presult);
[id(12)] HRESULT GetMsgType([out,retval]long
*presult);
Once the FixMsg event has been fired, these methods can be
used to obtain the fields of the message. The type of the message is actually
a parameter to the FixMsg event, so you probably never need to callGetMsgType.
The
values returned by these methods always relate to the incoming message,
they do NOT relate to the values passed in theAddField/UpdateField
methods above. The values remain valid only until the next call to AyncGetMsg,
after which, they will be replaced by the values from the next message,
as soon as it arrives.
[id(13)] HRESULT DisableAutoRecovery(BOOL
dissable);
If you use this method, you must call it before the call to Open.
It removes the need to make any calls to SetComplete below.
[id(14)] HRESULT SetComplete();
Imagine the scenario where you are processing an incoming list of message,
perhaps the list comprises the orders for one portfolio. You do not save
the portfolio until the last order has been received. Now, if the FIX connection
goes down just before the last message has arrived, what happens. Well,
the Fix protocol layer will only re-request messages it has not received,
and since most of the messages belonging to the list have been received
they will not be re-requested. Your application must save the messages
in the list some-how, and re-start from where it left off once the Fix
Session has been re-established. The SetComplete method allows
you to set check-points, whereby, in the event of a connection failure,
all messages received since the last call to SetComplete will be re-requested,
so in the above scenario, you would call SetComplete after each
portfolio has been successfully processed and saved.
[id(15)] HRESULT BeginBufferedOutput();
[id(16)] HRESULT AbortOutputBuffer();
[id(17)] HRESULT FlushOutputBuffer();
These methods provide you with an output buffer. In the above scenario,
you may want to send and acknowledgment after each item in the list has
been processed, but you might not want to actually send the acknowledgements
until all the items in the list have been received. If you call BeginBufferedOutput,
all subsequent calls to SendMsg merely record the message in an
internal buffer - the messages are not actually sent until FlushOutputBuffer
is called. If connection errors occur during the flush operation, any partly
flushed buffer is automatically fully flushed the next time the application
is re-started. If AbortOutputBuffer is called, the buffer
is discarded.
[id(18)] HRESULT ResetSession();
This method is only available in FIX Version 4.2, it initiates a session
reset.
[id(19)] HRESULT RunScript(BSTR
path, int reconnectFlag);
This method can be used to process a script file.
[id(20)] HRESULT GetLastError([out,retval]
long *error);
Call this method to obtain the last error value, 0 is returned if there
have been no errors. Note: if an error occurs, a FixError event
is fired, with the error code as a parameter. All error codes are negative,
and are listed here
[id(21)] HRESULT AddLoginField(long
id, BSTR value);
[id(22)] HRESULT AddLoginFieldInt(long
id, long value);
Add a tag tat will be sent as part of the Fix logon sequence (to support
non-standard login messages). This must be used before the Open() call.
[id(23)] HRESULT AddHdrField(long
id, BSTR value);
[id(24)] HRESULT AddHdrFieldInt(long
id, long value);
Add a tag tat will be sent as part of every fix sequence (it is actually
added to the end the tags but before the standard fix trailer). This
must be used before the Open() call.
[id(25)] HRESULT SetRecoverySeqNos(long
out, long in);
This can be used to recover from certain out-of-sequence conditions,
as explained here. (it should be used with care)
[id(26)] HRESULT ResetSessionAtLogon();
Arrange for the Logon message to also reset the sequence number of
both sides. This must be used before the Open() call.
[id(27)] HRESULT InitMsgEx(BSTR
type);
Use this method to initialize a Fix Message where the Type is longer
than a single character.
[id(28)] HRESULT PreferStringMsgTypeEvent(BOOL
useString);
Call this method to arrange for all notifications to be via OnFixMsgEx
rather than OnFixMsg.
[id(27)] HRESULT InitMsgEx(BSTR
type);
Call this method to get the message type where you are processing messages
that might have a type longer than a single character. If the type is a
single character, that’s fine, the string will be just one character long.
[id(30)] HRESULT OverrideTargetSubId(BSTR
newSubId, BOOL permanent);
Change the target SubID at any time during the session. If permanentisfalse,
the change is for the next outbound message only.
[id(31)] HRESULT BeginFieldEmum();
[id(32)] HRESULT EnumNextField([out]long
*id, [out retval]BSTR *value);
[id(33)] HRESULT EnumNextFieldInt([out]
long *id, [out retval]long *value);
These methods can be used to enumerate the fields in the current message.
The value parameter will be 0 when there are no more fields available.
E.G.
Call FixGW1.BeginFieldEmum
Dim Value As String
Dim Id As Long
Id = 1
Do While (Id <> 0)
Value = FixGW1.EnumNextField(Id)
Loop
Events
The FixGWctl control fires only 3 events, these are as follows...
[id(1)] void OnFixMsg(long msgType);
This event is fired when a Fix Message arrives - AsyncGetMsg
must previously have been called for this event to be triggered. Once the
method has fired, no more FixMsg or FixMsgEx events
will be triggered until
AsyncGetMsg has been called again. The
msgType
parameter contains the type of the message received.
[id(2)] void OnFixError(long code);
This event is fired if an error occurs. All error codes are negative,
and are listed here. Attempts to call methods other
than Close() and GetLastError() will
fail after an error event has been fired. The session must be closed
if an error occurs. Close() must always be called to close
a session, even if Open() failed!
[id(3)] void OnFixScriptComplete();
This event is fired when a Script has completed.
[id(4)] void OnFixMsgEx(BSTR msgType);
This event is fired when a Fix Message arrives - AsyncGetMsg
must previously have been called for this event to be triggered. This event
is different from OnFixMsg in that the msgType
parameter is a string, it is fired if the Fix Message type is more
than one bye in length. If you call PreferStringMsgTypeEvent(TRUE)
all
notifications will be via this event and OnFixMsgwill not
be fired, and thus you need only implement this event.
Once
the method has fired, no more FixMsg or FixMsgEx events
will be triggered until
AsyncGetMsg has been called again.
NB: the …Ex() methods were implemented to add support
for message types longer than a single character. Even if your message
type is a single character, you can still use the …Ex()
methods, and by calling PreferStringMsgTypeEvent
you can ensure that you only receive OnFixMsgEx notifications,
making your application simpler.
HRESULT Return Values
The control defines its own set of HRESULT returns from methods. If one
of these values is returned, it will result in an exception in the client
application, in terms of MFC applications, a pointer to a COleDispatchExceptionobject
is thrown by MFC, the m_scError member will contain the actual return
code. Possible return codes are as follows...
-1 connection not open
-2 attempt to send
a message without initializing it
-3 attempt to open
an already open connection
-4 error state - the fix
connection is in the 'error state', i.e. the OnFixError event has been
fired. The connection must now be closed.
-5 no parameter block (this
indicates an internal error and should never be seen).
The VB Sample
The sample implements the following scenario...
(sample app|FixGWctl)...[FixGatwWay,cfg:loopback]
-----//----- [FixGAteWay,cfg:testsrvr]...(testsrvr
app)
Client
local Fix
Engine
remote Fix Engine remote server app
Refere to the system components diagram
for more details on the relationship of the client application to the Fix
GateWay process.
 |
This VB samples implements the code fragments above. When you click
the Connect button you will be asked to start the fixgwsrv
test server program. Doing this gives the application a 'dummy' remote
fix Engine to connect to.
To use the sample, click Connect, start fixgwsrv (a consol app)
as directed and take the o option to open a session
within fixgwsrv, and then just hit return to accept
the default fixgwsrv session. From fixgwsrv,
you can use the h option to send a heart beat
to this app, it will appear in the text box. Take the vfixgwsrv
option for verbose mode, then, returning to this app, click the Send
Msg button, you will see a News message appear in the fixgwsrv
window. |
The C++ MFC Sample
 |
This application implements the same functionality as the VB app. It
is, however slightly more sophisticated, in that the Connect button will
automatically start fixgwsrv as well, but you
still need to enter o and accept the default in the
fixgwsrv
window.
The lower half of the screen contains some diagnostic buttons that will
display log files and configuration files - these are purely for information
and are not required for the app to establish a FIX session.
Just like the VB app above, this app opens a Fix Gateway session using
the standard "loopback" configuration, the test fixgwsrv
application
opens its own Fix Gateway session using the "testsrvr" configuration. These
2 configurations are supplied with the distribution and designed for testing
purposes. They allow a test client application (in this case the sample)
to connect to a test server application (in this case fixgwsrv)
via a FIX session.
(NB: to add the FixGWctl ActiveX control to an MFC app, just use the
Project/Add
To Project/Components and Controls Visual Studio menu option, and
then select Registered Active X Controls from the list.) |
The FixGWLite Automation Object
NOTE: the FixGWLite Automaton Objet is only included in the WIN32
version of Fix GateWay.
This object implements much the same interface as the FixGWCtl, however
this object cannot process asynchronous messages. This object can be used
in the simple scenario, where you need to send a series of messages and
then listen for responses in an ordered synchronous manner.
The object is named FixGWLite.API.1
[id(1)] HRESULT Open(BSTR configName);
[id(2)] HRESULT Close();
[id(3)] HRESULT InitMsg(long type);
[id(4)] HRESULT AddField(long id, BSTR value);
[id(5)] HRESULT UpdateField(long id, BSTR value);
[id(6)] HRESULT AddFieldInt(long id, long value);
[id(7)] HRESULT UpdateFieldInt(long id, long value);
[id(8)] HRESULT SendMsg();
[id(9)] HRESULT GetMsg([out,retval]long *msgType);
[id(10)]HRESULT GetField(long id, [out,retval]BSTR *presult);
[id(11)]HRESULT GetFieldInt(long id, [out,retval]long *presult);
[id(12)]HRESULT DisableAutoRecovery(BOOL dissable);
[id(13)]HRESULT SetComplete();
[id(14)]HRESULT ResetSession();
[id(15)]HRESULT RunScript(BSTR path, int reconnectFlag);
[id(16)]HRESULT GetLastError([out,retval]long *error);
[id(17)]HRESULT BeginBufferedOutput();
[id(18)]HRESULT AbortOutputBuffer();
[id(19)]HRESULT FlushOutputBuffer();
[id(20)]HRESULT AddLoginField(long id, BSTR value);
[id(21)]HRESULT AddLoginFieldInt(long id, long value);
[id(22)]HRESULT AddHdrField(long id, BSTR value);
[id(23)]HRESULT AddHdrFieldInt(long id, long value);
[id(24)]HRESULT SetRecoverySeqNos(long out, long in);
[id(25)]HRESULT ResetSessionAtLogon();
[id(26)]HRESULT InitMsgEx(BSTR type);
[id(27)]HRESULT GetMsgEx([out,retval]BSTR *msgType);
[id(28)]HRESULT OverrideTargetSubId(BSTR newSubId, BOOL permanent);
The main difference to the FixGWCtl ActiveX control is the GetMsg()
method, this will not return until a message arrives or an error occurs
(such as the remote system dropping the line)
FixGW Error Handling Strategies
The Fix Gateway is a separate process running the fix communication protocol
with a remote Fix Engine. The user application communicates with the Fix
Gateway via a socket. If the line drops between the Fix Gateway and the
remote Fix Engine, the user application must be notified, and any messages
that the User application has sent before the error notification must be
guaranteed to arrive at the remote Fix Engine. This section explains how
this is achieved. Note, the fixtest and fixgwsrvutilities
can be used to test the system's terror handling capability.
Connection Errors While Sending Messages
When the FixClient::SendFixMsg() is called, the message is sent
to the Fix Gateway via a socket, provided no error occurs in the
call to send the message on the socket, FixClient::SendFixMsg()
returns successfully. The problem is, that the line between the Fix Gateway
and the remote Fix Engine may have dropped, but the SendFixMsg method does
not detect this. (NB: SendFixMsg could query the state of the
FixEngine but this would slow things down too much).
In the Fix Gateway process, when a message is received via FixClient::SendFixMsg()
it is sent to the remote Fix Engine - if the line to the remote Fix Engine
is down, the message is added to the fix Journal file, and the outgoing
sequence number is incremented so that when the connection is re-established,
the message will be requested by the remote side. When this happens, an
Out-Of-Band message is sent back to the User Application setting and error
state flag. FixClient::SendFixMsg() checks the error state
flag, and if it finds it set, it returns an error. The consequence of this
strategy is, some messages may be sent when the fix connection is down,
but this should only be one or two messages before the User Application
is detects the error, and these messages are not lost, they are transmitted
as soon as the fix connection is re-established.
If the User Application is waiting for a Fix Message and the fix connection
drops, an internal fix-admin messages is sent to the User Application to
notify it of the error condition straight away.
Sending Messages Using Buffers
The fixgw library implements a buffering facility which enables
your application to send messages to a local buffer rather than sending
them to the gateway, then when happy with the current application state,
the application can flush the buffer to the gate way. If the line goes
down or the application is killed while flushing the buffer, the current
position in the buffer is remembered when the application restarts, and
by calling FixClient::SynchronizeOutputBuffer(), your application
can being sending from the point at which it stopped. Once the buffer has
been flushed you should call FixClient::SetComplete() (provided
you haven't previously called FixClient::DisableAutoRecovery(), in
which case you never need to call SetComplete(). )
The
FixClient::SetComplete()
method
Once you receive a Fix Message, the fix protocol is done with it, your
application should then store it into a database soon as possible
so that if your application is killed or crashes, the message is not lost.
Imagine the situation where your application is slow (for whatever reason)
to processes messages it received. The FixGateway (FixEngine) process
will continue to send you messages until your application (socket) buffers
are full. If at this point your application crashes the messages
in the application buffer will be lost, but as far as the Fix protocol
layer is concerned, the message will have been received successfully
and will not be re-requested. SetComplete() protects your from this
roblem.
When the FixGateway starts, it checks its last received sequence number
with the one specified in the last call to SetComplete(). If the SetComplete
sequence number is lower, it checks in the Receive Journal and if the missing
messages are there, it sends them to the client app(with the Possible
Dup flag set to Y), and all should be well. If the messages
are not in the Receive Journal, the Fix Engine sets its own expected sequence
number to the one specified by SetComplete() thus forcing a
re-send of any 'lost' messages. By the remote side. NOTE: the messages
should only be missing from the Receive Journal if the Receive Journal
has been switched off. (If you do switch the Receive Journal off, be
aware, that, if the missing message was time-sensitive, the remote
Fix engine may decide not to re-send the message, but rather, replace it
with a gap fill.
The best way to use SetComplete() is to save the message in the database
as soon as your receive it, and then call SetComplete(). Then,
when your application re-starts, it should check the database for received
but un-processed messages first, process these, and then create the
Fix session and continue as normal.
You may decide to only call SetComplete() when you have fully processed
the message, e.g, if you are collecting messages in a list.
If your application crashes with unprocessed messages in its list, the
SetComplete mechanism will ensure that these messages are not
lost. Doing this removes the need to save the message immediately into
the database.
You may call SetComplete() after each message has been processed,
or after a sequence of messages have been processed, once called,
it applies to all messages received since the last call. It is, however,
probably safest to save each message as you receive it
and call SetComplete() after each successful save.
If you don’t want to use SetComplete()at all, just call DisableAutoRecovery(true)
as soon as the session is created.
The Script File Format
The FixClient::RunScript() method accepts a script file that contains
a set of fix messages and sends them to the Fix gateway. The script file
should contain lines in the following format
BEGIN fixMsgType
This defines the start of a fix message - fixMsgType
can be either the actual value of a fix message or a symbolic name as defined
in <fixconst.h>, but without the leading FIX_MSG_, i.e. fixMsgType
could be E
or
Order_List
fixFieldId value
This adds a field to the current fix message - fixFieldId
can be the numeric or symbolic id, e.g. 38 or OrderQty, value
can be any string, it can contain embedded spaces if it is surrounded by
double or single quotes. If the value is a fix-time or fix-date, value
can be @N, this is interpreted as the current time plus N
seconds,
or %N which is interpreted the current date plus N
days. If a unique id is required for any value, a
& can be used
at the start of the string - the & is replaced by the Hour/
Minute/Second at the time the script file began to run, in the form of
HHMMSS,
the rest of the value is then appended to the
HHMMSS
-
in this way & represents a different value each time a script
runs, but the same value for the lifetime of the script. Note for &
to be expanded into
HHMMSS, it must appear at the start
of the string.
If, however, more than one script runs in a second, the &
value will not change. For this reason there are several substitution characters
that can be used, but note they must appear at the front of the string
& … substituted with HHMMSS
^ … substituted with HHMMSSmmm where mmm
is the current milliseconds value, giving a unique value on a dily basis.
| … substituted with the number of seconds since
10/10/2001 (in hex), thus | should give a
unique 8 character
value over time provided the script doesn’t run more than once per second.
~ … substituted with XXXXXXXmmm
where
XXXXXXX is the number of seconds since 10/10/2001
in hex and
mmm
is the current milliseconds value, thus ~ should give a unique
10 character value over time
regardless of how
quickly the scripts run.
SEND
Send the current fix message to the fix gateway.
RESET_AMPERSAND
Reset the value used when substituting &. This will be reset
to the current hour/minute/second - note: the substitution values are automatically
reset each time a script runs.
SET_AMPERSAND value
Set the value used when substituting &. The value
parameters can be any string. Ampersand substitution is designed to enable
the same script file to generate different ID's on each run, but you may,
temporally, wish to test what happens when you get 2 runs with the same
IDs, in this situation, you can use the SET_AMPERSAND at the top of the
script to set a fixed value that will remain the same on each run of the
script.
LOAD_AMPERSAND
This command loads a previously saved ampersand (&) value
from persistent storage. If there is not a previously saved value,
the ampersand value is set to 000000. Typically,
you will LOAD_AMPERSAND and then INCR_AMPERSAND
at the start of the script, and SAVE_AMPERSAND at the end,
thus guaranteeing a unique value for ampersand on each script run. Values
of ampersand loaded like this will be zero-padded to 6 character positions.
SAVE_AMPERSAND
You can save the current value of & to persistent
storage with this command.
INCR_AMPERSAND
Increment the value of the & substitution, if this is not
a numeric value, a value of 0 will be used
DECR_AMPERSAND
Decrement the value of the & substitution, if this
is not a numeric value, a value of 0 will be used
RESET_HAT
Reset the value of the ^ substitution
RESET_BAR
Reset the value of the | substitution
RESET_TILDE
Reset the value of the ~ substitution
BEGIN_BUFFER
All subsequent messages are sent to the buffer, rather than the fix
gateway.
FLUSH_BUFFER
Flush the output buffer, sending buffered messages to the gateway.
Subsequent messages are sent directly to the gateway unless BEGIN_BUFFER
is called again.
#
This is a comment
A simple script file, to send 2 orders in a list would look like this...
# example script file
BEGIN Order_List
ListID &4
ListSeqNo 1
ListNoOrds 2
ClOrdID &01
ClientID ZCN0002
Account xxxxxxxxx
FutSettDate %2
Symbol OCT.L
SecurityID 12345678
IDSource 2
Side 2
OrderQty 20000
Price 987
Currency GBP
TransactTime @0
TradeDate %0
SEND
BEGIN Order_List
ListID &4
ListSeqNo 2
ListNoOrds 2
ClOrdID &02
ClientID ZCN0002
Account xxxxxxx
FutSettDate %2
Symbol OCT.L
SecurityID 12345678
IDSource 2
Side 1
OrderQty 20000
Price 987
Currency GBP
TransactTime @0
TradeDate %0
SEND
The fixgwsrv Tool
This tool allows you to run fix client or server sessions with a minimum
of effort. The tool understands a simple script format whereby you can
send fix message to whoever is connected. The source
code for this tool is included as a sample. The tool presents a character-based
menu for simplicity and portability, and allows you to 'open' any config
file you require. The program can act as a server and/or a client - two
versions of the program can run on the same machine and communicate with
each other. For testing purposes a configuration file named testsrvr
is supplied, this configuration sets the fix gateway into server mode.
A second configuration, loopback, is supplied for testing purposes,
this config is used by the fixtest sample program,
which simply connects to the fixgwsrv on the local machine. You can then
send heartbeats and other fix messages to fixtest from the fixgwsrv menu,
and watch them appear. This tool is particularly useful when testing your
applications, you can create a script-file with a set of standard fix data
and run fixgwsrv as a dummy, replacing the actual Fix Engine you will want
your application to connect to.
Fixgwsrv displays the following character based menu...
o) Open Session
c) Close Session
h) Send Heartbeat
t) Send Test Request
s) Send Script File
n) Send Test News
r)
Reset Session q) Test Reset Seq
Num
v) verbose mode
x) exit
o) open session
This option prompts for the name of a config
file, the default value is testsrvr, this implements a server that
will listen for incoming connections. The fix session runs in a separate
thread leaving a user interface thread free to process menu commands. The
c)close
session option can be used to close the session.
h) send heart beat
Send a fix heartbeat to the remote fix engine.
t) send test request
Send a fix test request to the remote fix engine - we should get a
heart beat in response, which will be seen if verbose mode is
on.
s) run script file
Run a script file, 5LISTS.TXT is prompted for by default.
n) send test news
A test news message is sent
r) reset session
cause both sides to reset their sequence numbers back to 1. This will
only work if both sides of the fix session are running protocol version
4.2 AND the fix gateway config file must
have ManualSessionSwitch set to 0.
q) test reset sequence numbers
The sequence numbers are reset from 100.
v) set verbose mode
Verbose mode switches of the display of the menu, but displays all
incoming fix messages as they are received. Menu commands are still processed,
and entering v again will switch verbose mode off.
If you select the s) option, you
will see the following sub menu
2) 2 Orders Sample script
5) 5 Orders Sample script
OR the name of a script file
This allows you to easily run the sample scripts or any script of your
own. If you want to run your own script, just type its name.
The source code for this tool is included as a sample.
The fixtest Sample
This is a simple test program, the source for which
is included in the distribution. This program also display a simple menu,
as follows...
fixtest menu
0) normal mode
1) none buffered mode
2) local client crash-simulation testing
mode
3) local gateway crash-simulation testing
mode
4) test File fix-msg-source and fix-msg-destination
x) exit
To run the test, first start fixgwsrv, and take the
(o)open
menu option and open the testsrv session - this is the default session
to open, so you shouldn't have to type in its name, just accept the default.
Now, from a different command line window, start the
fixtest program
- it will give you a small menu, choose
option 0. In order to give
fixtest
some work to do, return to fixgwsrv and take the (s) send script
file menu - you will be able to chose from the pre-defined scripts
5LISTS.TXT
(option 5), 2LISTS.TXT(option 2) or enter your own script
file name. Running5LISTS.TXT script sends lists of orders to
fixtest,
which simply compiles these lists into a file, and sends acknowledgements
back to the sender. It is a relatively trivial test, but demonstrates many
features of the fixgw lib. It can be used to test recovery features in
the event of a system failure, as fixtest validates the file it
creates, and
fixgwsrv validates the replies it receives - the validation
is based on the FIX_FLD_ClientID which is set in ascending order
in 5LISTS.TXT.
The main point of interest in fixtest is the ProcessFixMsg(FixString
&sfix, FixClient &fixcli), where the messages are complied
into lists. and void ProcessNews(FixString &sfix, FixClient &fixcli,
StringType &orderList) where NEWS messages are handled
- the 2 test programs use NEWS messages to trigger certain actions.
Note: if you select option (0), you will be invited to "RELEASE
VALIDATION FILES", if you are running a normal test you should
enter Y and do this. You should not do this if you
are testing a recovery after a previously simulated crash.
Simple fixtest / fixgwsrv Test Session
This session will involve connecting the fixtest
program to the fixgwsrv program via
a fix session. They will exchange messages and verify that all messages
have arrived successfully. Fixtest
will use the loopback configuration
file, and fixgwsrv will use the testsrvr
configuration file. Both thes config files are supplied with the distribution,
in fact fixgwsrvr can load any configuration
file you choose, but fixtest can only
access the loopback config.
(fixtest)...(FixGatwWay,cfg:loopback)
-----//-----
(FixGatwEay,cfg:testsrvr)...(fixgwsrv)
Just follow the simple instructions bellow to run a test fix session...
-
Start fixgwsrv and fixtest.
These
programs are both windows console applications.
-
Select option (o) from fixgwsrv
and then just hit return at the next prompt.
-
Select option(0) from fixtest
and enter Y at the next prompt
-
In fixtest, selectoption
(s) and enter 5 at the next
prompt
-
You should see messages scrolling in the fixtest
window. After a few seconds you should see the message ORDER
FILES VALIDATED AND SAVED in the fixtest
window, and [TEST LOG VERIFIED OK]in
the fixgwsrv window
-
Enter x in the fixgwsrv
window to close the session.
-
Fixtest will detect that fixgwsrv
has closed, enter a q to close fixtest.
Using fixtest and fixgwsrv To Test
The System's Error Recovery Capabilities
Note: in following discussion fixtest
is used to refer to the fixtest client application and the associated
fixgw
process, and similarly
fixgwsrv
refers to the fixgwsrv client application and the associated
fixgw processes. Remember, self validation during these tests rely
on the fact that 5LISTS.TXT script files supplied with the distribution
has a pre-defined format that allows fixtest
and fixgwsrv to determine if all messages
have arrived successfully.
Testing Recovery from failure in the User Application
(fixtest)...(FixGatwWay,cfg:loopback)
----//-----
(FixGatwEay,cfg:testsrvr)...(fixgwsrv)
this side will
crash
this side will wait for a reconnect
Firstly start fixgwsrv and open
a session. Start fixtest in
crash-simulation
testing mode (menu option 2).
Use
fixgwsrv
to
run the 5LISTS.TXT script file (option S and option 5),
fixtest
will crash half way through, it will either exit directly or give you the
option to quit or reconnect, you should quit.
The
fixgwsrv program will wait for
a reconnection, and then continue sending from where it left off, hence
you should restart fixtest in normal
mode (option 0) Depending on the situation, it may take up to 30
seconds for the 2 fix engines to re-synchronize, so please be patient.
Eventually, messages will flow from where they left off, and fixgwsrv
and fixtest should print out a messages
indicating that validation has completed successfully.
Restart fixtest in normal mode,
it will re-received the orders already sent (but not processed) , and then
continue to receive the rest of the messages. fixtest
and fixgwsrv will both validate the
files they have created, and should report no errors. NB: it may take up
to 30 seconds for the 2 fix engines to re-synchronize themselves during
error recovery in this and the next test.
Testing Recovery from failure in the Fix Gateway
The assumption is that the gateway process dies because of user intervention,
i.e. someone kills it, it will not, of course, crash on its own.
(fixtest)...(FixGatwWay,cfg:loopback)
----//----- (FixGatwEay,cfg:testsrvr)...(fixgwsrv)
this
side dies
Firstly start fixgwsrv and open
a session. Start fixtest in
local gateway crash-simulation testing mode(menu option 3). Again,
use
fixgwsrv
to run the 5LISTS.TXT
script file (option S and option 5). The gateway
used by fixtest will die and fixtest
will detect this it will either exit directly or give you the option to
quit
or reconnect, you should quit,
The remote
gateway server(fixgwsrv) will detect that its
fix client(fixtest)
has died, it will close down and inform
the
user application(fixgwsrv), fixgwsrv will
then wait for a reconnection.
When
fixtest
is
restarted in normal mode (option 0), it will restart its gateway,
which will request a resend of any messages that either it or the user
application has lost. Once again, depending on the situation, it
may take up to 30 seconds for the 2 fix engines to re-synchronize, so please
be patient. Eventually, messages will flow from where they left off, and
fixgwsrv
and
fixtest should print out a messages
indicating that validation has completed successfully.
Disclaimer
These tests demonstrates the systems reliability in failure situations.
The likely causes of failure are
-
user application crash
-
computer failure due to power loss or hardware fault
-
user intervention to kill the gateway process
The above tests show good recovery in the face of a user application crash,
however, the behaviour of the user application is entirely the responsibility
of the user. Similarly, if the system fails due to hardware errors, the
gateway behaves well, but again, it is the users responsibility to minimize
the likelihood of hardware failure, using uninteruptable power supplies
and fault tolerant hardware.
Troubleshooting
The main source of information when troubleshooting are the log
files. If, however, the problem is related to log-file location specification,
you might have difficulty finding the logs. There is a set of informational
files created at startup to help diagnose any problem. These files have
the name $TEMP/.fixgw-Name.info,
and $TEMP/.fixcli-Name.info where $TEMP
is the system temp directory and Name is
the name of the information contained. Five such files
are created as follows...
.fixgw-log.info |
The location of the log file |
.fixgw-cfg.info |
The configuration file opened by the gateway |
.fixgw-logerror0.info |
This records errors while creating the log directory |
.fixgw-logerror1.info |
This records errors while creating the log directory after a
manual fix session switch |
.fixgw-root.info |
The location of the active root - this is an advanced feature and only
applicable to UNIX/Linux systems |
.fixgw-lic.info |
This file is only created if there are any licensing problems. |
.fixgw-srv-cfg.info |
This file records config parameters are read by the gateway process |
.fixcli-log.info |
This location of the client log file |
.fixcli-logerror0.info |
This records errors during the first stage of creating the log
directory |
.fixcli-logerror1.info |
This records errors during the seconds stage of creating the
log directory |
.fixcli-loginit1.info |
The client session-log directory |
.fixcli-loginit2.info |
The client session-log path |
.fixcli-exec.info |
The comand issued by the client to start the fix gateway |
.fixcli-cli-cfg.info |
This records config paramaters as read by the client lib |
NOTE: You cannot copy FixGateway to another computer and expect it to
run. Further, if you rename you computer, you will have to reinstall FixGateway,
you can, however, change the IP address and not have to reinstall.
Appendix I - Implementation Notes
The Local Client Reliable Protocol
Before reading this refer to the system diagram.
Now, when the client sends a message and the return code indicates success,
as far as the client is concerned the messages has gone and will be received.
Under normal circumstances the messages reaches the gateway and is then
sent, using the FIX protocol to the remote fix engine. This is all very
well, but if the machine crashes, or if someone kills the fixgw
process after the message has left the client lib but before it arrives
at the gateway, the message is lost, but the client thinks it has been
sent. This potential problem is overcome by the ‘Local Client Reliable
Protocol’ (LCRP) which is a very simple protocol used between the clientg
lib and the fix gateway. Basically every message has its own LCRP number.
When the client opens a gateway session, the gateway sends the the client
the last LRCP number it received, and the client then checks a local journal
file, and re-sends any messages that it previously sent whose LRCP is bellow
the number sent by the gateway. Remember, this isn’t the FIX protocol at
work here, this is just a mechanism to ensure that if the client lib sends
a message, that message is guaranteed to reach the fix gateway regardless.
The mechanism is implemented by a file called jnl.txt, in which
outgoing messages are saved. When the library opens a session, it renames
jnl.txt,
(if it exists), to jnl.wrk and then checks the last LCRP number
received from the gateway with records in jnl.wrk and resends missing
messages. The jnl.txt/jnl.wrk file is located in a 'session' sub-directory
of a directory with the same named as the config file, either located in
the system temp directory, in the directory specified by the environment
variable
FIXGWLOG, or in the directory specified by the
CliLogDir
config
parameter. It follows from this, that if you open a config and each time
have FIXGWLOG set to different values or sometimes not
set at all, the client lib will behave inconsistently with regard to the
LRCP, since it will not be checking the correct journal files against the
correct LRCP number - previously sent messages may well get sent again.
Note: one source of problems in this area is if the a config is opened
by different users that don’t have FIXGWLOG set consistently.
If for any reason you don’t want this protocol to operate, set UseLCRP
to 0 in the config file
Client Buffering Support
This is provided by the methods...
FixError FixCli::SynchronizeOutputBuffer();
FixError FixCli::BeginBufferedOutput();
FixError FixCli::AbortOutputBuffer();
FixError FixCli::FlushOutputBuffer();
The buffer is persisted in a file located in a directory with the same
named as the config file, either located in the system temp directory or
in the directory specified by the environment variable FIXGWLOG.
It follows from this, that if you open a config file and have FIXGWLOG
set to different values or not set at all, the client lib will behave inconsistently
with regard to buffering. Afer a config is opened, if you are using
buffering you should call SynchronizeOutputBuffer(). This checks
the buff.cmt file for records that are not flags as having been sent,
these are then sent. Buffered records are stored in a file buff.txt until
the buffer is flushed, the file is then renamed buff.cmt, and each record
is actually sent, as a record is sent it is marked as sent
within the file.
The Implementation of FixCli::SetComplete()
When FixCli::SetComplete() is called, the sequence number of the
current message is saved in a file setcomplete.log is located
in a directory with the same named as the config file, either located in
the place specifed by the CliLogDirconfig parameter or
system temp directory or in the directory specified by the environment
variable FIXGWLOG. It follows from this, that if you open
a config file and have FIXGWLOG set to different values
or not set at all, the client lib will behave inconsistently with regard
to processing SetComplete() data. When the client starts a new
session, it passes the last processed sequence number to the gateway, if
this is lower than the last sequence number the gateway received from the
remote fix engine, it firstly searches the Receive Journal for the missing
messages and, if found, passes them to the client app with the Possible
Dup flag set to Y, but if the messages are not found, the
gateway uses the sequence number defined by SetComplete so that the missing
messages are re-requested from the remote Fix Engine. Note a value of
–1
in the setcomplete.log file is an ‘initial value’
flag, and is ignored.
Appendix II - Recovery from total sequence number
failure
Firstly, you should never be in a situation of total sequence number failure
unless you have suffered some sort of hardware failure.
Firstly, how could this situation arise. We’ll, if your machine suffers
total failure and you move your application onto a backup machine, depending
on how things are configured, your backup system won’t know the last sequence
number sent, so it can only send 0, the receiving system will likely reject
this message as a duplicate and then you’re stuck - the only thing
you can do is phone up the 3rd party and get them to reset their sequence
numbers to 0.
We’ll, there is an alternative to phoning up if you are running Fix
4.2 or higher. If you are in this situation, you can use the FixClient::SetRecoverySeqNos()
to set the outgoing sequence number to a high value, maybe 899999. How
will this help? Well, the 3rd party won’t reject your message, but it will
ask for a resend of the sequence number it has missed. Your system won’t
of course, have these number, but the FixGateway will know that it is trying
to recover a connection from an error condition, so it will respond with
a GapFill to the remote site, and then, it will issue a session reset.
Thus everything will be working again. Of course, you will have to manually
investigate what might have been lost due to the failure of your machine.
If you don’t want to suffer this sort of problem in the first place, you
should run your application on fault-tolerant hardware, at the very least,
you should have a UPS and probably disk-mirroring as well.
|