Version 1.9.44
Onix Solutions Java FIX Engine is a simple fully Java compliant tool that will FIX-enable applications written in Java.
The Engine provides the following services:
JAR | Notes | License |
---|---|---|
onixs-util | The collection of utility classes. | OnixS License |
onixs-fix-engine | The FIX engine. | OnixS License |
onixs-fast-coder | The FAST coder. | OnixS License |
onixs-fixml-converter | The FIXML converter. | OnixS License |
slf4j-api | The SLF4J logging facility facade. | Massachusetts Institute of Technology (MIT) X11 License |
logback-classic, logback-core | The logging facility implementation. This can be replaced with any supported implementation. | LGPL v2.1 |
commons-io | The commons-io is a library of utilities to assist with developing IO functionality. | Apache License Version 2.0 |
joda-time | Date and time processing library. | Apache License Version 2.0 |
commons-pool | The commons-pool library provides an object-pooling API and implementation. | Apache License Version 2.0 |
commons-lang | The commons-lang library provides extra functionality for classes in java.lang. | Apache License Version 2.0 |
xercesImpl | The Xerces-J is the reference implementation of high performance, fully compliant XML parser. | Apache License Version 2.0 |
xml-apis | The XML APIs collection. | W3C License |
JAR | FIX Engine | FAST Coder | FIXML Converter | Session Scheduler |
---|---|---|---|---|
onixs-util | + | + | + | + |
onixs-fix-engine | + | + | + | + |
onixs-fast-coder | + | |||
onixs-fixml-converter | + | |||
onixs-fix-scheduler | + | |||
slf4j-api | + | + | + | + |
logback-classic, logback-core | + | + | + | + |
commons-io | + | + | + | + |
joda-time | + | + | + | + |
commons-pool | + | + | + | + |
commons-lang | + | |||
xercesImpl | + | |||
xml-apis | + | |||
commons-codec | + | + | + | + |
quartz | + | |||
c3p0 | + |
All Engine classes have a well formed API and could be easy extended for customer requirements.
The typical way of using the Engine is as follows:
Exception handling is used as a fundamental error-reporting mechanism. In
the event of any errors arising then the biz.onixs.fix.engine.EngineException
is
thrown.
To initialize the FIX Engine class library the biz.onixs.fix.engine.Engine.init(..)
method
is used. This method must be called before all other methods.
The engine configuration is set during initializtion phase. See Configuration → Engine Initialization for details.
To shutdown the Engine the biz.onixs.fix.engine.Engine.shutdown()
method
is used. No other Engine methods must be called after this method.
Example:
import biz.onixs.fix.engine.Engine; public class SimpleEngine { public static void main(String[] args) { try { final Engine engine = Engine.init(); // Session-related logic... engine.shutdown(); } catch (Exception ex) System.out.println("Exception: " + ex); } } }
See also: Configuration → Engine Initialization.
As the FIX protocol evolves its new versions will be released to you to extend the current functionality.
Supported FIX versions are: FIX 4.0, 4.1, 4.2, 4.3, 4.4 and 5.0. The biz.onixs.fix.engine.Version
enum
is used to identify them.
Example:
Version v = Version.FIX44;
Each FIX version can be produced by appropriate number too, e.g.:
final Version fixVersion = Version.getByNumber("4.2");
The general format of a FIX message is a stream of <tag>=<value> fields with a field delimiter (<SOH>) between fields in the stream (so-called "tag-value FIX format").
Below is an example of a FIX message:
It is represented with the biz.onixs.fix.engine.Message
class.
To create a message the biz.onixs.fix.engine.Message(java.lang.String
type, biz.onixs.fix.engine.Version version)
constructor is used.
To parse a message in the raw format the biz.onixs.fix.engine.Message(java.lang.String
rawMessage)
constructor is used.
To validate a message the biz.onixs.fix.engine.Message.validate()
method
is used. This method checks for the presence of required
fields.
Example (samples-pro-guide/src/main/java/biz/onixs/fix/sample/proguide/FixMessageCreation.java):
// Init the engine final Engine engine = Engine.init(); // Create "New Order - Single", MsgType = "D" final Message order = new Message("D", Version.FIX40); // Another way to create FIX message final String rawMsg = "8=FIX.4.0\u00019=86\u000135=D\u000149=0\u000156=0\u000134=1\u000152=99990909-17:17:17\u0001" + "11=90001008\u000121=1\u000155=IBM\u000154=1\u000138=10\u000140=1\u000159=0\u000110=191\u0001"; final Message order2 = new Message(rawMsg); // Validate created message order2.validate(); // Shutdown the engine engine.shutdown();
To set a field value the appropriate biz.onixs.fix.engine.Message.set()
method
is used.
To get a field value the biz.onixs.fix.engine.Message.get()
method
is used.
Example:
String ClOrdID = order.get(11); execReport.set(17, "Unique identifier of execution message");
To remove a field the biz.onixs.fix.engine.Message.remove(int tag)
method
is used.
The Tag class contains the constants for the tag values. This class is defined for each FIX version namespace. The use of these constants makes the source code more readable.
Example:
import biz.onixs.fix.engine.Message; import static biz.onixs.fix.engine.FIX40.Tag; ... // New Order – Single Message order = new Message("D", Version.FIX40); order.set(Tag.ClOrdID, "90001008"); order.set(Tag.Side, "1"); order.set(Tag.TimeInForce, "0");
See also: FIX Repeating Group.
A FIX session is defined as a bi-directional stream of ordered messages between
two parties within a continuous sequence number series. It is represented by
the biz.onixs.fix.engine.Session
class.
To create a session object the appropriate constructor is used.
To terminate the session the Session.dispose()
method
is used.
A session has one of two possible roles: Acceptor or Initiator.
Acceptor is the receiving party of the FIX session. It listens for the incoming connection on the pre-defined port. The acceptor has responsibility to perform first level authentication and formally declare the connection request "accepted" through transmission of an acknowledgment Logon message.
Initiator establishes the telecommunications link and initiates the session via transmission of the initial Logon message.
To obtain current role the Session.getRole()
method is used.
To establish the session as Acceptor the Session.logonAsAcceptor()
method
is used.
To establish the session as Initiator the Session.logonAsInitiator()
method
is used.
To disconnect the session the Session.logout(...)
method
is used.
Example:
import biz.onixs.fix.engine.Message; import biz.onixs.fix.engine.Session; import biz.onixs.fix.engine.Version; ... Session acceptor = new Session("SenderCompID", "TargetCompID", Version.FIX40); acceptor.logonAsAcceptor(); Session initiator = new Session("TargetCompID", "SenderCompID", Version.FIX40); // Sends the Logon message and waits for the acknowledgment Logon initiator.logonAsInitiator("localhost", Engine.getInstance().getListenPort()); // Message exchange and processing ... // Sends the Logout message and waits for the confirming Logout initiator.logout(); acceptor.logout(); // Free resources initiator.dispose(); acceptor.dispose();
See also: Session state, Custom Logon Message, Sequence Numbers.
To send a message to counterparty the Session.send(...)
method
is used. As soon as a session is created it is possible to start sending messages
via the session. If the session is not established, the messages will be sent
when the connection is established with the counterparty.
To receive incoming application-level messages it is necessary to implement
the Session.InboundApplicationMessageListener
interface
for incoming message event handler and register it by Session.setInboundApplicationMessageListener(Session.InboundApplicationMessageListener
listener)
.
Example.
package biz.onixs.fix.sample.proguide; import biz.onixs.fix.engine.Session; import biz.onixs.fix.engine.Engine; import biz.onixs.fix.parser.Version; import biz.onixs.fix.parser.Message; import biz.onixs.fix.tag.FIX42; import biz.onixs.fix.tag.Tag; import biz.onixs.util.Utils; import java.text.SimpleDateFormat; import java.util.Date; public class MessageExchange implements Session.InboundApplicationMessageListener { private static final String SENDER_COMP_ID = "SenderCompId"; private static final String TARGET_COMP_ID = "TargetCompId"; private static final Version VERSION = Version.FIX42; private static final String ACCEPTOR_HOST = "localhost"; private static final int ACCEPTOR_PORT = 11011; private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyyMMdd-HH:mm:ss"); public void onInboundApplicationMessage(Object sender, Session.InboundApplicationMessageArgs args) { System.out.println("Incoming application-level message: " + args.getMsg()); // processing of the application-level incoming message... } private void run() { final Engine engine = Engine.init(ACCEPTOR_PORT); final Session acceptor = new Session(SENDER_COMP_ID, TARGET_COMP_ID, VERSION); acceptor.logonAsAcceptor(); final Session initiator = new Session(TARGET_COMP_ID, SENDER_COMP_ID, VERSION); initiator.logonAsInitiator(ACCEPTOR_HOST, ACCEPTOR_PORT); initiator.setInboundApplicationMessageListener(this); final Message order = new Message("D", VERSION); order.set(Tag.ClOrdID, "Unique identifier for Order as assigned by the buy-side"); order.set(Tag.HandlInst, "1"); order.set(Tag.Symbol, "Ticker symbol"); order.set(Tag.Side, "1"); order.set(Tag.TransactTime, FORMATTER.format(new Date())); order.set(FIX42.Tag.OrdType, "1"); order.validate(); acceptor.send(order); Utils.waitForEnterToTerminateApp(); acceptor.logout(); acceptor.dispose(); initiator.dispose(); engine.shutdown(); } public static void main(String[] args) { final MessageExchange messageExchange = new MessageExchange(); messageExchange.run(); } }
This example can be found in samples/samples-pro-guide, class biz.onixs.fix.sample.proguide.MessageExchange.java
All FIX messages are identified by a unique sequence number (the MsgSeqNum field).
Sequence numbers are initialized at the start of each FIX session starting at 1 (one) and increment throughout the session. Each session establishes an independent incoming and outgoing sequence series.
A single FIX session can exist across multiple sequential (not concurrent) physical connections. Parties can connect and disconnect multiple times while maintaining a single FIX session. In other words, FIX Session is comprised of one or more FIX Connections.
Resetting the inbound and outbound sequence numbers back to 1, for whatever reason, constitutes the beginning of a new FIX session.
Monitoring sequence numbers enables parties to gracefully synchronize applications when reconnecting during a FIX session.
To disconnect the FIX Connection while maintaining a single FIX Session the Session.logout(...)
method
is used. It is possible to continue the session later using the Session.logonAsAcceptor()
or Session.logonAsInitiator(...)
methods
again.
To terminate a FIX Session the Session.dispose()
method
is used.
The Engine automatically synchronizes sequence numbers when reconnecting during a FIX session on the base of the information previously stored in log files.
See also: Sequence Numbers Management.
The engine is configured during initialization phase. The initialization method is overloaded. Thus there are multiple ways to pass the configuration.
The engine settings are taken from the properties.
PropertyBasedSettings settings = new PropertyBasedSettings("engine.properties"); Engine engine = Engine.init(settings);
The properties file is loaded as described in the Unified Resource Loading.
The configuration file example:
Dialect=LavaDialect.xml Log.InboundMessages=true Validate.NumberOfRepeatingGroupInstances=false Validate.RequiredFields=true Validate.UnknownFields=true
The engine settings are available as the class properties.
EngineSettings settings = new EngineSettings(); Engine engine = Engine.init(settings);
The settings can be changed before initialization method call.
The pre-defined license file name is "OnixS.lic". The license file is loaded during engine initialization as described in the Unified Resource Loading.
The default license file name can be changed using "LicenseFile"
configuration parameter or EngineSettings.LicenseFile
property.
The following table explains different options to keep the license file.
License File Location | License File Name | LicenseFile Property | Note |
---|---|---|---|
application classpath | OnixS.lic | no change required | |
current directory | *.lic | no change required | Any file with the ".lic" extension in the current directory is checked automatically. |
user home directory | *.lic | no change required | Any file with the ".lic" extension in the user home is checked automatically. |
application classpath | AltName.ext | AltName.ext | |
absolute path "/foo/bar" | AltName.ext | /foo/bar/AltName.ext | |
relative path "foo/bar" | AltName.ext | foo/bar/AltName.ext |
The SLF4J (Simple Logging Facade for Java) is used for logging. The SLF4J detects and uses the concrete logging implementation configured by user. By default, the Logback logging implementation is recommended.
The Logback (or any other logging implementation) must be configured at the beginning of the application lifecycle.
The examples of Logback configurations can be found in the application examples.
The details of Logback configuration can be found here.
See also: Classpath Libraries Selection.
By default, the FIX engine uses file system directory specified by the "Log.Directory" parameter for storing incoming and outgoing messages, session’s state data.
For each FIX session the following files are created:
Where:
When the biz.onixs.fix.engine.Session.logonAsAcceptor()
object
is created anew after the Engine's restart, FIX Session's state (the sequence
numbers of last received and sent messages, previously sent messages, etc)
is restored from these files.
To start a FIX session as a new one (so called "clean start"), it is necessary to remove the log files from the previous runs.
Connecting parties must bi-laterally agree as to when sessions are to be started/stopped and log files are backed up based upon individual system and time zone requirements.
When the FIX session is finished, the log files are not needed anymore. They can be backed up and later a new FIX session with the same SenderCompID and TargetCompID will start sequence numbers from 1. The usual practice is to backup the log files at the end of each business day (so called "End Of Day procedure") to start the sequence numbers from 1 at the beginning of the next day.
See also: Log.Directory, Log.InboundMessages, Sequence Numbers.
Property | Description | Type | Default Value |
---|---|---|---|
LicenseFile | Specifies the name of the license file resource. | string | OnixS.lic |
Dialect | Specifies the location of one or several files with the FIX Dialects definition. The file names are separated with the '|' character. The dialect files are loaded during engine initialization as described in the Unified Resource Loading. | string | <empty> |
ListenPort | FIX Engine listens on this port(s) for incoming connections. If it is set to '0' then only
session-initiators can be created. If it is set to '-1' then the telecommunication level is disabled and
only message parsing/assembling can be used. Use comma delimited list if more than one listen port is required. It is important to avoid using ports used by other services. |
string | 0 |
Log.Directory | Inbound/outbound messages and session's state data are stored in this directory. | string | FixEngineStorage |
Log.InboundMessages | Option to log inbound messages. | boolean | true |
Validate.NumberOfRepeatingGroupInstances | Option to validate that the declared number of repeating group instances is equal to the actual one. For in inbound and outbound messages | boolean | false |
Validate.RequiredFields | Option to validate the presence of required fields in inbound and outbound messages. | boolean | false |
Validate.UnknownFields | Option to validate the presence of unknown fields in inbound and outbound messages. | boolean | false |
ConnectionRetries.Interval | The initial time interval between the attempts to restore the telecommunication link, in milliseconds. | integer | 90000 |
ConnectionRetries.Number | Number of attempts to restore the telecommunication link. The range is [0..MAX_INT]. | integer | 3 |
Connection.TcpNoDelay | Option to improve latency at the expense of message throughput. | boolean | true |
Connection.TcpSendBufferSize | The size of the TCP buffer allocated to FIX connection for sending data, in bytes. | integer | 65535 |
Connection.TcpReceiveBufferSize | The size of the TCP buffer allocated to FIX connection for receiving data, in bytes. | integer | 65535 |
Connection.OutputQueueSize | The size of the internal output queue allocated to FIX connection, in messages. | integer | 1000 |
ReasonableTransmissionTime | The reasonable transmission time as % from HeartBtInt value. When either end of the connection has not received any data for (HeartBtInt * (100 + ReasonableTransmissionTime)/100) seconds, it will transmit a Test Request message. If there is still no Heartbeat message received after (HeartBtInt * (100 + ReasonableTransmissionTime)/100) seconds then the connection should be considered lost and corrective action be initiated. |
integer | 20 |
SessionListenerDelayWarningThreshold | The limit of time to spend in the session listener code. If the listener takes longer then the warning is generated. In milliseconds. | integer | 1000 |
The settings names (keys) are case-sensitive.
By default, any external resource is looked for at the following places and in the following order (if other is not set explicitely):
If it can't be found at the 1st location then the 2nd one is checked, etc.
For example, you can put a dialect file to the classpath or current directory and it will be located by engine.
It is permissible for fields to be repeated in the same message within a repeating
group (e.g. "215=2<SOH>216=2<SOH>217=Dest_1<SOH>216=4<SOH>217=Dest_2<SOH>"
represents
a repeating group with two repeating instances "delimited" by tag 216 (first
field in the repeating group.).
The NoRoutingIDs<215> field specifies the number of repeating group instances (2 in this case). This is so called "NumberOfInstances field". The NumberOfInstances field must immediately precede the repeating group contents (repeating group instances).
The FIX repeating group is represented with the biz.onixs.fix.engine.Group
class.
To get the biz.onixs.fix.engine.Group
object that represents
the existing repeating group of the message the biz.onixs.fix.engine.Message.getGroup(int
numberOfInstancesTag)
method is used.
To create a new repeating group or modify the number of instances in the existing
one the biz.onixs.fix.engine.Message.setGroup(int numberOfInstancesTag,
int numberOfInstances)
method is used.
To remove a repeating group method biz.onixs.fix.engine.Message.remove(int
numberOfInstancesTag)
is used.
The Group
class works with fields and embedded repeating groups
in the same manner as the biz.onixs.fix.engine.Message
class,
but each method has an additional parameter that defines the index of the repeating
group instance (starting from 0).
Example.
class RepeatingGroupSettingValue { private static final Logger LOG = LoggerFactory.getLogger(RepeatingGroupGettingValue.class); public static void main(String[] args) { final Engine engine = Engine.init(); final Message message = new Message("V", Version.FIX42); message.set(Tag.MDReqID, "ABSD"); message.set(Tag.SubscriptionRequestType, 1); message.set(Tag.MarketDepth, 1); message.set(Tag.MDUpdateType, 1); message.set(Tag.AggregatedBook, "N"); // create a repeating group NoMDEntryTypes with two instances final Group groupMDEntryTypes = message.setGroup(Tag.NoMDEntryTypes, 2); groupMDEntryTypes.set(Tag.MDEntryType, 0, "EntryType_1"); // first instance groupMDEntryTypes.set(Tag.MDEntryType, 1, "EntryType_2"); // second instance // create a repeating group NoRelatedSym with two instances final Group groupRelatedSym = message.setGroup(Tag.NoRelatedSym, 2); groupRelatedSym.set(Tag.Symbol, 0, "EURUSD_0"); // first instance groupRelatedSym.set(Tag.Symbol, 1, "EURUSD_1"); // second instance message.validate(); LOG.info("Message: {}", message); engine.shutdown(); } }
Example.
class RepeatingGroupGettingValue { private static final Logger LOG = LoggerFactory.getLogger(RepeatingGroupGettingValue.class); public static void main(String[] args) { final Engine engine = Engine.init(); final Message message = new Message("8=FIX.4.2\u00019=142\u000135=V\u000149=0\u000156=0\u000134=0\u0001" + "52=99990909-17:17:17\u0001262=ABSD\u0001263=1\u0001264=1\u0001265=1\u0001266=N\u0001267=2\u0001" + "269=EntryType_1\u0001269=EntryType_2\u0001146=2\u000155=EURUSD_0\u000155=EURUSD_1\u000110=163\u0001"); LOG.info("MDEntryTypes group"); final Group mdEntryTypes = message.getGroup(Tag.NoMDEntryTypes); if (mdEntryTypes != null) { for (int i = 0; i < mdEntryTypes.getNumberOfInstances(); i++) { LOG.info("Instance # {}", i); LOG.info("MDEntryType = '{}'", mdEntryTypes.get(Tag.MDEntryType, i)); } } LOG.info("RelatedSym group"); final Group relatedSym = message.getGroup(Tag.NoRelatedSym); if (relatedSym != null) { for (int i = 0; i < relatedSym.getNumberOfInstances(); i++) { LOG.info("Instance # {}", i); LOG.info("Symbol = '{}'", relatedSym.get(Tag.Symbol, i)); } } engine.shutdown(); } }
Sometimes there is a requirement to accept an incoming FIX session "on the fly", without the prior creation
of the Session
object. The Engine exposes an ability to create session object in response to the
incoming FIX connection. To take advantage of this feature it is necessary to subscribe to the DynamicAcceptor event
in the Engine object.
The event arguments object provides information to make decision whether to accept connection:
To accept the incoming connection:
Session
object needs to be created using session identification information (session id) provided
in the event arguments or logon message
Session
object is passed to the createdSession
property of the Engine.DynamicAcceptorArgs
.
Otherwise the incoming connection will be rejected. Optionally a reject reason can be passed to the event arguments object.
Example:
public class DynamicAcceptorSample { private void run() { final EngineSettings engineSettings = new EngineSettings(); engineSettings.addListenPort(4500); final Engine engine = Engine.init(engineSettings); engine.setDynamicAcceptorListener(new AcceptingLogic()); Utils.waitForEnterToTerminateApp(); engine.shutdown(); } public static void main(String[] args) { (new DynamicAcceptorSample()).run(); } class AcceptingLogic implements Engine.DynamicAcceptorListener { public void onDynamicAcceptor(Object sender, Engine.DynamicAcceptorArgs args) { final SessionId sessionId = args.getSessionId(); System.out.println("Dynamic acceptor: " + sessionId + ", logon message " + args.getIncomingLogonMessage()); final Session session = new Session(sessionId); System.out.println("Session created: " + session); args.setCreatedSession(session); } } class RejectingLogic implements Engine.DynamicAcceptorListener { public void onDynamicAcceptor(Object sender, Engine.DynamicAcceptorArgs args) { final SessionId sessionId = args.getSessionId(); System.out.println("Dynamic acceptor: " + sessionId + ", logon message " + args.getIncomingLogonMessage()); args.setRejectReason("Face control failed"); } } }
To obtain the current session state the biz.onixs.fix.engine.Session.getState()
method
is used.
The states of the FIX session are listed below.
State | Role: Acceptor | Role: Initiator |
---|---|---|
DISCONNECTED |
The session is disconnected.
|
|
AWAIT_LOGON |
The session is waiting for the initial Logon message.
|
N/A
|
AWAIT_CONFIRMING_LOGON |
N/A
|
The initial Logon message was sent and the session is waiting for the acknowledgment Logon message. |
ESTABLISHED |
The session is fully established (after the successful Logon exchange).
|
|
RECONNECTING | Session-initiator is trying to restore the telecommunication link. |
N/A
|
AWAIT_CONFIRMING_LOGOUT |
The initial Logout message was sent and the session
is waiting for the acknowledgment Logout message.
|
|
WAIT_FOR_RETRANSMISSION |
The session is waiting for the message retransmission from the counterparty.
|
To be notified about the changes in the session state implement biz.onixs.fix.engine.Session.StateChangeListener
and
the register it as event handler by the biz.onixs.fix.engine.Session.setStateChangeListener(biz.onixs.fix.engine.Session.StateChangeListener
listener)
.
Example:
import biz.onixs.fix.engine.Session; import biz.onixs.fix.engine.Version; class SessionListener implements Session.StateChangeListener { private static final String SENDER_COMP_ID = "AcceptorAndInitiator_SND"; private static final String TARGET_COMP_ID = "AcceptorAndInitiator_TRG"; private static final Version VERSION = Version.FIX42; public void onStateChange(Object sender, Session.StateChangeArgs e) { System.out.println("New session state: " + e.getNewState()); // processing of the application-level incoming message... } public static void main(String[] args) { try { final Engine engine = Engine.init(); Session sn = new Session(SENDER_COMP_ID, TARGET_COMP_ID, VERSION); SessionListener listener = new SessionListener(); sn.setStateChangeListener(listener); // ... } catch (Exception ex) System.out.println("Exception: " + ex); } } }
The events of the biz.onixs.fix.engine.Session
class are listed below.
Warning. It is critical to avoid
in the session event listener calling thread. Please do it from another thread.
Event Listener | Event Description |
---|---|
biz.onixs.fix.engine.Session.StateChangeListener |
Session state is changed. |
biz.onixs.fix.engine.Session.ErrorListener |
Error condition is detected. |
biz.onixs.fix.engine.Session.InboundApplicationMessageListener |
Application-level message is received from the counterparty. |
biz.onixs.fix.engine.Session.InboundSessionMessageListener |
Session-level message is received from the counterparty. |
biz.onixs.fix.engine.Session.OutboundApplicationMessageListener |
Application-level message will be sent to the counterparty. |
biz.onixs.fix.engine.Session.OutboundSessionMessageListener |
Session-level message will be sent to the counterparty. |
biz.onixs.fix.engine.Session.WarningListener |
Warning condition is detected. |
biz.onixs.fix.engine.Session.MessageResendingListener |
Application-level message resending request. |
Example:
import biz.onixs.fix.engine.Session; import biz.onixs.fix.engine.Engine; import biz.onixs.fix.parser.Version; public class SessionListener implements Session.StateChangeListener, Session.InboundSessionMessageListener, Session.InboundApplicationMessageListener, Session.OutboundSessionMessageListener, Session.MessageResendingListener, Session.OutboundApplicationMessageListener, Session.WarningListener, Session.ErrorListener { private static final String SENDER_COMP_ID = "AcceptorAndInitiator_SND"; private static final String TARGET_COMP_ID = "AcceptorAndInitiator_TRG"; private static final Version VERSION = Version.FIX42; public void onStateChange(Object sender, Session.StateChangeArgs e) { System.out.println("New session state: " + e.getNewState()); } public void onInboundApplicationMessage(Object sender, Session.InboundApplicationMessageArgs e) { System.out.println("Incoming application-level message: " + e.getMsg()); } public void onInboundSessionMessage(Object sender, Session.InboundSessionMessageArgs e) { System.out.println("Incoming session-level message: " + e.getMsg()); } public void onOutboundApplicationMessage(Object sender, Session.OutboundApplicationMessageArgs e) { System.out.println("Outgoing application-level message: " + e.getMsg()); } public void onOutboundSessionMessage(Object sender, Session.OutboundSessionMessageArgs e) { System.out.println("Outbound session-level message: " + e.getMsg()); } public void onWarning(Object sender, Session.WarningArgs args) { System.out.println("Session warning: " + args.getReason()); } public void onError(Object sender, Session.ErrorArgs args) { System.out.println("Session error: " + args.getReason()); } public boolean onMessageResending(Object sender, Session.MessageResendingArgs args) { return false; } public static void main(String[] args) { try { final Engine engine = Engine.init(); final Session sn = new Session(SENDER_COMP_ID, TARGET_COMP_ID, VERSION); final SessionListener listener = new SessionListener(); sn.setStateChangeListener(listener); sn.setInboundSessionMessageListener(listener); sn.setInboundApplicationMessageListener(listener); sn.setOutboundSessionMessageListener(listener); sn.setOutboundApplicationMessageListener(listener); sn.setWarningListener(listener); sn.setErrorListener(listener); sn.setMessageResendingListener(listener); // ... engine.shutdown(); } catch (Exception e) { System.out.println("Exception: " + e); } } }
Sometime there is need to set additional fields (e.g. Username<553>,
Password<554>
) in the initiation Logon message. In this
case the biz.onixs.fix.engine.Session.logonAsInitiator(java.lang.String
host, int port, int heartBtInt, biz.onixs.fix.engine.Message customLogonMsg)
method
must be used.
Example:
Session acceptor = new Session("CustomLogonMessage_SND", "CustomLogonMessage_TRG", Version.FIX43); acceptor.LogonAsAcceptor();
Session initiator = new Session("CustomLogonMessage_TRG", "CustomLogonMessage_SND", Version.FIX43); Message customLogonMsg = new Message("A", Version.FIX43); customLogonMsg.set(Tag.Username, "USER_NAME"); customLogonMsg.set(Tag.Password, "PASSWORD");
initiator.LogonAsInitiator("localhost", Engine.getInstance().getListenPort(), 30, customLogonMsg); // message exchange and processing ...
acceptor.logout(); initiator.logout(); acceptor.dispose(); initiator.dispose();
Check samples/samples-buyside, the CredentialBuySide example.
The expected sequence number of the next incoming message can be:
Session.getInSeqNum(...)
;Session.setInSeqNum(...)
, but only when session is not connected.The sequence number of the next outgoing message can be:
Session.getOutSeqNum(...)
;Session.setOutSeqNum(...)
, but only when session is not connected.Some implementations of FIX protocol (FIX Dialects) expect that the FIX Session coincides with FIX Connection,
in other words that the sequence numbers are reset back to 1 after the Logout messages exchange. To interact with
such FIX implementations, the keepSequenceNumbersAfterLogout
parameter of Session
's
constructor should be set to false.
ResetSeqNumFlag (141) field in the Logon (A) message indicates that the both sides of the FIX session should reset sequence numbers.
To send a Logon message with this field during FIX connection
establishment, use the setResetSeqNumFlag
parameter of the Session.logonAsInitiator(String
host, int port, boolean setResetSeqNumFlag)
method.
Next time when the session object is created incoming and outgoing sequence numbers:
setResetSeqNumFlag = true
in the Session.logonAsInitiator(...)
;
setResetSeqNumFlag = false
in the Session.logonAsInitiator(...)
.
See also: Sequence Numbers.
It is common for firms to implement slightly different interpretations of the FIX protocol (FIX dialects).
Within the FIX protocol such deviations from the FIX specification must be described in the FIX Engine, to ensure that the parsing or creation of the corresponding FIX messages are correct.
The Dialect
configuration setting specifies
one or several dialect definition files in XML format. The dialect definition file must conform
to the dialect XML schema.
FIX dialect can be set on:
Please read details below.
The engine level dialect is defined by <FIX>
element with no id
attribute specified. The dialect defined for the engine level is set implicitly for all engine sessions.
<?xml version="1.0" encoding="utf-8"?> <Dialect xmlns="http://ref.onixs.biz/fix/dialects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ref.onixs.biz/fix/dialects http://ref.onixs.biz/fix/dialects/dialects-2_9.xsd"> <FIX version="4.2"> <Message type="N"> <Group numberOfInstancesTag="73"> <Field tag="37"/> </Group> </Message> </FIX> </Dialect>
In this example the dialect is set for all FIX 4.2 sessions. I.e. it changes the base FIX version definitions in the engine.
You need no additional steps to start using this dialect from your code.
Session session = new Session("SenderCompID","TargetCompID", Version.FIX42); ... Message message = new Message("D", Version.FIX42); session.send(message);
The session level dialect is defined by <FIX>
element with id
attribute specified. The dialect defined for the session level needs to be set explicitly for the selected
sessions.
<?xml version="1.0" encoding="utf-8"?> <Dialect xmlns="http://ref.onixs.biz/fix/dialects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ref.onixs.biz/fix/dialects http://ref.onixs.biz/fix/dialects/dialects-2_9.xsd"> <FIX version="4.2" id="MyFIX"> <Message type="N"> <Group numberOfInstancesTag="73"> <Field tag="37"/> </Group> </Message> </FIX> </Dialect>
In this example the dialect is defined over base FIX 4.2 version definition and has specified id
.
I.e. it doesn't change the base FIX version definitions in the engine.
Using FIX dialect in the code is easy.
Version version = Version.getById("MyFIX"); Session session = new Session("SenderCompID","TargetCompID", version); ... Message message = new Message("D", version); session.send(message);
To add a new user-defined field to a FIX Message add the corresponding <Field>
entity to the FIX
Dialect description file.
<?xml version="1.0" encoding="utf-8"?>
<Dialect xmlns="http://ref.onixs.biz/fix/dialects"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ref.onixs.biz/fix/dialects http://ref.onixs.biz/fix/dialects/dialects-2_9.xsd"> <FIX version="4.0"> <Message type="D"> <!-- Does not belong to Standard FIX 4.0 --> <Field tag="526" name="SecondaryClOrdID"/> </Message> </FIX> </Dialect>
To add a new user-defined repeating group to a FIX Message add the corresponding <Group>
entity to
the FIX Dialect description file.
<?xml version="1.0" encoding="utf-8"?> <Dialect xmlns="http://ref.onixs.biz/fix/dialects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ref.onixs.biz/fix/dialects http://ref.onixs.biz/fix/dialects/dialects-2_9.xsd"> <FIX version="4.0"> <Message type="D"> <!-- Number of repeating groups for pre-trade allocation, does not belong to Standard FIX 4.0 --> <Group numberOfInstancesTag="78" name="NoAllocs"> <!-- Does not belong to Standard FIX 4.0 --> <Field tag="79" name="AllocAccount"/> <!-- Does not belong to Standard FIX 4.0 --> <Field tag="661" name="AllocAcctIDSource"/> <!-- Does not belong to Standard FIX 4.0 --> <Field tag="736" name="AllocSettlCurrency"/> <!-- Other group fields --> </Group> </Message> </FIX> </Dialect>
To make a field, which is required according to the FIX Protocol specification, optional, and visa-versa, add the
corresponding <Field>
entity to the FIX Dialect description and set the isRequired
attribute accordingly.
<?xml version="1.0" encoding="utf-8"?> <Dialect xmlns="http://ref.onixs.biz/fix/dialects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ref.onixs.biz/fix/dialects http://ref.onixs.biz/fix/dialects/dialects-2_9.xsd"> <FIX version="4.0"> <Message type="D"> <!-- Required in Standard FIX 4.0, but optional in this FIX dialect --> <Field tag="21" isRequired="false" name="HandlInst"/> <!-- Optional in Standard FIX 4.0, but required in this FIX dialect --> <Field tag="109" isRequired="true" name="ClientID"/> </Message> </FIX> </Dialect>
To define a user-defined message add the corresponding <Message>
entity to the FIX Dialect
description.
<?xml version="1.0" encoding="utf-8"?> <Dialect xmlns="http://ref.onixs.biz/fix/dialects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ref.onixs.biz/fix/dialects http://ref.onixs.biz/fix/dialects/dialects-2_9.xsd"> <FIX version="4.0"> <Message type="UserDefinedMessage_1"> <Field tag="100"/> <Field tag="101"/> </Message> </FIX> </Dialect>
Such a user-defined message can be used exactly the same way as the standard FIX Message:
Message userDefinedMsg = new Message(Version.FIX40, "UserDefinedMessage_1");
userDefinedMsg.set(100, "Field 100");
userDefinedMsg.set(101, "Field 101");
userDefinedMsg.validate();
System.out.println("User defined message: " + userDefinedMsg);
To remove a field add the corresponding <Field>
entity to the FIX dialect definition and use
attribute mode="remove"
.
<?xml version="1.0" encoding="utf-8"?> <Dialect xmlns="http://ref.onixs.biz/fix/dialects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ref.onixs.biz/fix/dialects http://ref.onixs.biz/fix/dialects/dialects-2_9.xsd"> <FIX version="4.2">
<Message type="b">
<Group numberOfInstancesTag="296">
<Field tag="302" mode="remove"/>
</Group>
</Message>
</FIX>
</Dialect>
Check samples/samples-buyside and samples/samples-sellside for paired live applications with dialect support.
See also: Configuration → Settings.
The SSL (Secure Sockets Layer) support helps to make FIX communication secure.
The SSL support is implemented using Sun's java security / JSSE (Java Secure Socket Extension) implementation. Learn more here about java security and JSSE.
The SSL configuration procedure is slightly different for FIX acceptor and initiator sessions.
The acceptor SSL configuration is performed on the engine level via the following method.
Engine.setSSLContext(sslContext); ... Engine.init(...);
Parameters:
sslContext
— SSL context.Make sure that you configure SSL before doing engine init.
The initiator SSL configuration is performed on the session level via the following method.
Session session = new Session("SenderCompID", "TargetCompID", fixVersion); ... session.setSSLContext(sslContext); ... session.logonAsInitiator(...);
Parameters:
sslContext
— SSL conext.Make sure that you configure SSL before doing session logon.
One of the methods to create SSL context follows.
SSLContext sslContext = SslContextFactory.getInstance(keyStoreName, trustStoreName, keyPassword);
Parameters:
keyStoreName
— key store resource name;trustStoreName
— trust store resource name; can be set to null
if you don't want
to validate counterparty's certificate;
keyPassword
— key pair password used during key pair generation.The key and trust stores are loaded as described in the Unified Resource Loading.
To perform key and certificate management the keytool
can be used. This tool is a part of JDK package
and the complete documentation can be found in the JDK documentation, section "JDK Tools and Utilities".
Below you can find the simplified pre-steps to do in order to organize secure communication between two parties.
Generate the key pair for the "SellSide":
keytool -genkey -keyalg RSA -dname "cn=SellSide, ou=FixEngine, o=OnixS, c=US" -alias sellside -keypass keypass1 -keystore C:\sellside.keystore.bin -validity 360
Generate the key pair for the "BuySide":
keytool -genkey -keyalg RSA -dname "cn=BuySide, ou=FixEngine, o=OnixS, c=US" -alias buyside -keypass keypass2 -keystore C:\buyside.keystore.bin -validity 360
Export the certificate of "SellSide":
keytool -export -alias sellside -file sellside.cer -keystore C:\sellside.keystore.bin
Export the certificate of "BuySide":
keytool -export -alias buyside -file buyside.cer -keystore C:\buyside.keystore.bin
Import the certificate of "BuySide" into the "SellSide" trust store:
keytool -import -alias buyside -file buyside.cer -keystore C:\sellside.truststore.bin
Import the certificate of "SellSide" into the "BuySide" trust store:
keytool -import -alias sellside -file sellside.cer -keystore C:\buyside.truststore.bin
As a result we have two pairs of key store and trust store for each party to establish secure communication:
This is what was used for samples.
Check samples/samples-buyside and samples/samples-sellside for paired live applications with SSL support.
There is an option to skip saving of some inbound FIX messages to session storage. For instance, for a performance reason.
This can be configured on a per-session basis:
session.setInboundMessageLogFilter(messageFilter);
The messageFilter
is the implementation of biz.onixs.fix.filter.MessageFilter
interface.
For instance, the TypeMessageFilter
implementation is used to filter specified message types:
MessageFilter messageFilter = new TypeMessageFilter("W X"); session.setInboundMessageLogFilter(messageFilter);
The space-delimited list of message types to filter is specified.
When the Resend Request (2) message is received from the counterparty (e.g. due to a sequence number mismatch), MessageResending event is raised if an application has previously subscribed to this event. Otherwise, the SequenceReset-GapFill (4) message will be sent instead of the requested message.
If the sent application-level message needs to be resent to the counterparty then the return value of the
MessageResending event listener should be set to true
while handling the event. Otherwise the
SequenceReset-GapFill (4) message will be sent instead (e.g. in case of an aged order).
The message that is about to be resent is available via Msg
property of Session.MessageResendingArgs
parameter.
Example.
public class MessageResendingListenerSample implements Session.MessageResendingListener { private void run() { // ... final Session session = new Session("SenderCompID", "TargetCompID", Version.FIX50); session.setMessageResendingListener(this); // ... } public boolean onMessageResending(Object sender, Session.MessageResendingArgs args) { System.out.println("Message is about to be resent to the counterpart: " + args.getMsg()); // Return false if it's necessary to skip this message (GapFill will be sent). return false; } // ... }
Memory based session storage could be used to maintain a high-performance FIX session when persisting of session state and messages in the file system is not required.
To create such a session storageType
parameter of Session
constructor should be set to
SessionStorageType.MemoryBasedStorage.
boolean keepSequenceNumbersAfterLogout = false; Session session = new Session("SenderCompID", "TargetCompID", Version.FIX42, keepSequenceNumbersAfterLogout, SessionStorageType.MemoryBasedStorage);
For the security reasons the values of the following fields can be scrambled in the file-based session storage:
In order to achieve this on the engine level the following option can be used:
final FileBasedStorageRepository storageRepository = Engine.getStorageRepositoryManager().getFileBasedStorageRepository(); storageRepository.setScramblePassword(true);
In real life FIX connection occur at regular intervals. A lot of trading systems have public schedules which declare time frames for trading. The Session class exposes members for FIX connections basic handling. It also provides users with automatic reconnection facility in case of connection failure. However, there is no way in the Session functionality to maintain connections on systematic basis.
To satisfy the needs of real life trading schedules, the OnixS Java FIX Engine offers the Sessions Scheduler. This service will automatically connect certain FIX sessions to the counterpart at the beginning of the trading day as well as disconnect at the end of a day. It is also possible to keep a FIX Session connected for an entire trading week and disconnect it at the end of last trading day.
The SessionScheduler is a workhorse class of the Sessions Scheduler component. This class exposes a simple but comprehensive API to handle connections by schedules of any complexity.
The register method schedules a session for automatic logon and logout according to the specified session schedule.
If a Session is being registered at the time noted by schedule, the the scheduler will connect the Session immediately.The unregister method removes specified session from the scheduling service.
The unregister method doesn't disconnect active session from trading system. Therefore, if session was already connected by scheduler, it remains connected after unregistering.
The Scheduler exposes Error event to get notified about errors in session management.
There are different session schedule implementation classes.
The first two parameters (firstDay and lastDay) define activity week. During these days the session will always be connected at logon time and disconnected at logout time if specified session duration equals to a single day. If the session must continue for several days, then the scheduler will connect session at logon time on the day specified by the firstDay parameter and disconnected at logout time on the day specified by lastDay parameter.
The logonTime and logoutTime parameters respectively define time of logon and logout for the session for each activity day if session duration is a single day. If the session must continue for the entire week, then logonTime parameter value specifies the time for logon to be performed on the first day of activity week and logoutTime parameter value defines the time of logout performed on the last day of the activity week.
Finally the resetPolicy parameter specifies whether the session sequence numbers must be reset and on which basis (never, daily or weekly).
Check samples/samples-scheduling, the ProgramScheduleSample example.
To simplify development, the Session Scheduler provides an option to define session schedules and connection settings in the configuration file for later referencing in the source code.
The presets defined in the configuration file can be loaded using SchedulerSettingsLoader class. Then both schedules and connection settings can be referenced in the source code using the string identifiers.
The configuration file has XML-based format. The appropriate XML schema is available here.
Check samples/samples-scheduling, the FileScheduleSample example.
When Accepting an incoming FIX connection additional authentication checks may be required. The typical checks are Logon username/password and source IP address. Depending on the result of the logon verification check the decision can be made whether to Accept the FIX connection or reject and close the connection. This is typically performed at the logon stage.
In order to achieve this you should subscribe to the inbound session messages and add the required check logic. If the check fails and the FIX connection needs to be rejected (closed) then an exception can be thrown from the inbound session message listener.
In the following example the password authentication check is demonstrated.
public class LogonPasswordVerification implements Session.InboundSessionMessageListener { private void run() { // ... session.setInboundSessionMessageListener(this); // ... } public void onInboundSessionMessage(Object sender, Session.InboundSessionMessageArgs args) { final Message message = args.getMsg(); if (message.getType().equals(FIX43.MsgType.Logon)) { // ... if (! "ExpectedPassword".equals(message.get(FIX43.Tag.Password))) { throw new RuntimeException("Password is incorrect"); } // ... } } }
The text from the exception goes to the FIX logout message text field (Text<58>).
Check samples/samples-buyside/Credential and samples/samples-sellside/Credential examples pair.
This tool enables converting FIX message to FIXML and vice versa. The following sample code demonstrates how simple it is.
public class FixmlConverterSample { private static final Logger LOG = LoggerFactory.getLogger(FixmlConverterSample.class); public static void main(String[] args) throws Exception { final Engine engine = Engine.init(-1); final FixmlConverter converter = new FixmlConverter("src/main/conf/sample/schema", Version.FIX44); // final String inFixFile = "sample/Order.txt"; LOG.info("Loading FIX message from file: {}", inFixFile); final Message inFixMessage = Utils.readFixMessage(inFixFile); LOG.info("Input FIX message: {}", inFixMessage); String fixmlMessage = converter.fix2fixml(inFixMessage); LOG.info("Output converted FIXML message: {}", fixmlMessage); // final String inFixmlFile = "sample/ExecRpt.xml"; LOG.info("Loading FIXML message from file: {}", inFixmlFile); final String inFixmlMessage = Utils.readFixmlMessage(inFixmlFile); LOG.info("Input FIXML message: {}", inFixmlMessage); final Message outFixMessage = converter.fixml2fix(fixmlMessage); LOG.info("Output converted FIX message: {}", outFixMessage); // engine.shutdown(); } }
Check samples/samples-fixml-converter examples.
FAST is a binary encoding method for message oriented data streams. FAST is an acronym for FIX Adapted for STreaming. The original purpose of FAST was optimization of FIX messages.
In order to decode FAST data to FIX message the following 4 steps are required:
The FIX engine must be initialized as described in the following section. The appropriate FIX dialect must be configured during this step if necessary as described here.
To load FAST templates from an XML source the following code sequence can be used.
XmlTemplateLoader templateLoader = new DomXmlTemplateLoader(); InputStream is = new FileInputStream("templates.xml"); TemplateLibrary templateLibrary = templateLoader.load(is);
Create Decoder instance first.
Decoder decoder = new Decoder(templateLibrary, FastVersion.FAST_1_1, Version.FIX50);
Decode FAST stream chunks then.
Message fixMessage = decoder.decode(data);
The FIX engine must be shutdown as described in the following section.
Check samples/samples-fast for FAST related examples.
A lot of examples can be found in the samples directory. The examples are categorized by sub-directories.
In most cases the example sub-directory contains Readme.html file with the important information - configuration, startup, etc.
The examples are ready to be opened in the Eclipse Java IDE, the workspace is pre-created for your convinience. However, the additional configuraion steps are required:
Session-acceptor is the receiving party of the FIX session. It listens for the incoming connection on the pre-defined port. The acceptor has responsibility to perform first level authentication and formally declare the connection request "accepted" through transmission of an acknowledgment Logon message.
Fields which are required based on the presence or value of other fields.
The non-printing, ASCII "SOH" (\u0001, hex: 0x01, referred to in this document as <SOH>), is used for field termination.
Integer message sequence number, the tag number is 34.
The Financial Information Exchange (FIX) Protocol is a message standard developed to facilitate the electronic exchange of information related to securities transactions. It is intended for use between trading partners wishing to automate communications.
It is comprised of three parts: logon, message exchange, and logout.
This is a custom, slightly different interpretation of FIX protocol.
It is comprised of one or more FIX Connections, meaning that a FIX Session spans multiple logins.
Session-Initiator establishes the telecommunications link and initiates the session via transmission of the initial Logon message.
Each message within the FIX protocol is comprised of required, optional and conditionally required fields (fields which are required based on the presence or value of other fields). Systems should be designed to operate when only the required and conditionally required fields are present.
Fields in a FIX message that are not expected in accordance with the FIX protocol or its dialect.
Assigned value used to identify firm sending message, the tag number is 49.
Assigned value used to identify receiving firm, the tag number is 56.
These fields are intended to be implemented between consenting trading partners and should be used with caution to avoid conflicts, which will arise as multiple parties begin implementation of the protocol. The tag numbers 5000 to 9999 have been reserved for them. These tags can be registered/reserved via the FIX website.
Messages those format is privately defined between the sender and receiver. A "U" as the first character in the MsgType field (i.e. U1, U2, etc) indicates such messages.
The binary encoding method for message oriented data streams. FAST is an acronym for FIX Adapted for STreaming.