Skip to content

iLink3 Support

Dmitry Vyazelenko edited this page Apr 17, 2023 · 9 revisions

What is iLink3 and who should use it?

iLink3 is a protocol for binary order entry used by CME. It combines a FIXP session protocol with SBE message encoding. The CME Documentation provides details of the protocol itself and should be considered a reference guide, the rest of this wiki page just explains how Artio can be used with iLink3.

How does iLink3 relate to Artio?

iLink3 is an order entry protocol that Artio supports. Just in the same way that Artio supports different versions of the FIX protocol. That means that Artio will manage the iLink3 session protocol and your application has to handle the business logic side of things for example sending orders and handling execution reports. Using Artio to build an iLink3 application enables you to focus your time on coding business logic and not worry about the low level protocol details.

Status

The currently Artio implementation has been tested with iLink3 UAT and AutoCert+ system for BrokerTec.

Artio does not currently implement the fault tolerance system for iLink3. Though sponsorship of this feature is welcomed.

Implementing iLink with Artio

Setup your project

Currently in order to build iLink3 you need to build a custom iLink3 because the Artio open source project doesn't distribute the required ilinkbinary.xml SBE schema. This file can be downloaded from the CME SFTP server and should be placed at the location artio-ilink3-codecs/src/main/resources/uk/co/real_logic/artio/ilink/ilinkbinary.xml within the Artio project structure. After downloading the XML file you can run the following command to build Artio with iLink3 support enabled:

./gradlew -Dfix.core.iLink3Enabled=true

Your development project that will be connecting to iLink3 then needs to add Artio as a dependency to its project structure by depending upon the Artio jars. Here is an example dependencies section that includes Artio with iLink3 support.

dependencies {
    implementation "uk.co.real-logic:artio-codecs:${artioVersion}"
    implementation "uk.co.real-logic:artio-ilink3-codecs:${artioVersion}"
    implementation "uk.co.real-logic:artio-ilink3-impl:${artioVersion}"
    implementation "uk.co.real-logic:artio-core:${artioVersion}"
}

Configuring and connecting to CME

Please refer to Artio's normal documentation and samples for examples of how to setup a FixEngine and FixLibrary within your project. These examples assume that you have a connected Library and go from there. You can also refer to the ILink3SystemTest as an example of how to connect to a system.

// (1) Setup a FixLibrary and Engine that we will refer to later.
final FixLibrary library = ...

// (2) You need to initialise the configuration object with the configuration for the gateway that you
// plan to connect to. Your `handler` object in the example must be an implementation of
// `ILink3ConnectionHandler` - we will discuss this interface in more detail below.
final ILink3ConnectionConfiguration connectionConfiguration = ILink3ConnectionConfiguration.builder()
            .host(HOST)
            .port(PORT)
            .sessionId(SESSION_ID)
            .firmId(FIRM_ID)
            .userKey(USER_KEY)
            .accessKeyId(ACCESS_KEY_ID)
            .handler(handler)
            .build();

// (3) Now you have the configuration object you can initiate a connection to an iLink3 Gateway.
// This returns an async reply object 
final Reply<ILink3Connection> reply = library.initiate(connectionConfiguration);

// (4) Await the completion of your reply object. That operation can be interleaved with other 
// code on your thread's duty cycle, but you must poll the library object regularly.
while (reply.isExecuting())
{
    idleStrategy.idle(library.poll(1));
}

// (5)  Check the final state of the Reply object - ie whether it has completed successfully or not.
if (reply.hasCompleted())
{
    final ILink3Connection connection = reply.resultIfPresent();
    System.out.println("Connected: " + connection);

    // (6) Now you can you use your connection object.
}
// There maybe an error when connecting
else if (reply.hasErrored())
{
    reply.error().printStackTrace();
}
// The connection may time out due to a network error
else if (reply.hasTimedOut())
{
    System.err.println("Timed out: " + reply);
}

Sending Application / Business level messages.

The overall pattern of sending a message

// (1) You need an SBE encoder flyweight - these are generated by your custom Artio iLink3 build.
final NewOrderSingle514Encoder newOrderSingle = new NewOrderSingle514Encoder();
// (2) Use the tryClaim API in order to wrap the encoder around an in memory buffer
final long position = connection.tryClaim(encoder);
if (position < 0)
{
  // Your attempt to claim a buffer has been back-pressured. This is unlikely to happen, but you
  // want to re-attempt this operation later until it succeeds.
  return;
}

// (3) Set the values for your order message
newOrderSingle.price().mantissa(price);
newOrderSingle.stopPx().mantissa(PRICENULL9Encoder.mantissaNullValue());
newOrderSingle.execInst().oB(false).nH(notHeld).aON(false);

newOrderSingle
    .orderQty(1)
    .securityID(securityID)
    .side(SideReq.Buy)
    .senderID(senderID)
    .clOrdID(clOrdId)
    .partyDetailsListReqID(1)
    .orderRequestID(orderRequestID)
    .location(USNY)
    .minQty(minQty)
    .displayQty(NewOrderSingle514Encoder.displayQtyNullValue())
    .expireDate(NewOrderSingle514Encoder.expireDateNullValue())
    .ordType(OrderTypeReq.Limit)
    .timeInForce(timeInForce)
    .manualOrderIndicator(ManualOrdIndReq.Automated)
    .executionMode(ExecMode.Aggressive)
    .liquidityFlag(BooleanNULL.NULL_VAL)
    .managedOrder(BooleanNULL.NULL_VAL)
    .shortSaleType(ShortSaleType.NULL_VAL);

// (4) Commit the message - this tells Artio that it is ready to send. If an error or exception has
// happened during processing then you should call connection.abort() instead of commit.
connection.commit();

A few application messages include variable length fields or repeating groups, for example the PartyDetailsDefinitionRequest message that needs to be sent to the Order Entry Service Gateway. If you're sending messages with variable length components then you must use the overload of tryClaim() that takes an int variableLength second argument. This is the total size of all the variable length and group fields in the message including their headers. Aka the total length of the message minus it's block length.

You should try to reuse the SBE encoders where possible in order to minimise allocation, rather than allocate them everytime a tryClaim call is made. The example above only allocates the flyweight in order to make the documentation easier to read.

Handling Messages

Your application should implement the ILink3ConnectionHandler in order to receive callbacks for different types of message. Any application / Business level messages will be passed to your application through the onBusinessMessage callback. Artio itself will handle the Session level messages. Here is an example implementation that just prints out business reject messages and shows you how to determine message type and wrap the decoders.

private final BusinessReject521Decoder businessReject = new BusinessReject521Decoder();

public void onBusinessMessage(
    final int templateId,
    final DirectBuffer buffer,
    final int offset,
    final int blockLength,
    final int version,
    final boolean possRetrans)
{
    switch (templateId)
    {
        case BusinessReject521Decoder.TEMPLATE_ID:
        {
            businessReject.wrap(buffer, offset, blockLength, version);
            System.out.println("Received: " + businessReject.toString());
            break;
        }
    }
 }

When the exchange detects a gap in the sequence it will send you a NotApplied message. This is a session level message but really requires an application level decision - whether you should simply gapfill by sending a Sequence message in response or whether you should replay the missing messages. This can be handled in the onNotApplied callback by calling either the gapfill() or retransmit() methods of it's NotAppliedResponse parameter.

Artio implementation differences compared to FIX

Artio's iLink3 implementation is much more simplified than its FIX implementation. Firstly there is no Acceptor/Sell/Server implementation - iLink3 is Initiator/Buy/client side only. The normal FIX implementation has a complex ownership model whereby the Engine can take over management of sessions, this hasn't been replicated with iLink3. If a FixLibrary is closed or times out then any iLink3 sessions they own are disconnected. Finally the slow consumer support within Artio has been removed. It's approach is mostly of interest to Acceptor based use cases and there aren't any within iLink3.

Logging and Debugging

The iLink3 implementation offers two LogTags for printing out session level (FIXP_SESSION) and business level (FIXP_BUSINESS) messages. These can be configured through commandline Java properties, for example:

-Dfix.core.debug=FIXP_SESSION,FIXP_BUSINESS

In order to inspect the produced archive of messages the FixArchivePrinter has been updated to support printing out of iLink3 messages.