fix protocol with python

How to Implement a FIX Trading Engine in Python

A while ago, I started exploring the world of trading in hopes of building my own trading bots and learning more about this field. While researching, I came across the FIX protocol.

In this post, I cover some of the basics of the FIX protocol. More specifically, I’m talking about FIX 4.4. Additionally, I am including a bare-bones implementation of a client engine in Python 3.7. For this, I used the open-source framework QuickFIX with Python bindings.

I chose Python because it allows you to go from idea to product very quickly. However, the original QuickFIX was written in C++ underneath, but there are several implementations in other languages (Java, .NET, Go).

Books To Read

Before we go into what FIX is and how it works, I wanted to share some good books about algorithmic trading here. These links are referrals from Amazon, so I may get a commission if you buy them, but the cost remains the same to you. Also keep in mind that I may add books here that I’m also planning to read. If you have other suggestions, feel free to let me know in the comments.

  1. Machine Learning for Algorithmic Trading: Predictive models to extract signals from market and alternative data for systematic trading strategies with Python, 2nd Edition 2nd Edition
  2. Trading Evolved: Anyone can Build Killer Trading Strategies in Python
  3. Quantitative Trading: How to Build Your Own Algorithmic Trading Business
  4. Inside the Black Box: The Simple Truth About Quantitative Trading

As you can see, some of the books explicitly deal with algo trading with Python. Others are more general about how algorithmic trading works. I could not find a good book specifically about the FIX protocol that seemed decent, so let me know if there is one I should check out.

What is FIX?

FIX stands for Financial Information eXchange and it’s a protocol for communicating transactions and financial operations with exchanges, brokers, etc. It is a messaging protocol with a simple structure of tag/value pairs.

Part of its appeal is that you can transmit messages pretty quickly because it is a lightweight protocol. This helps if you are doing high-frequency trading, for example. Check the official FIX protocol site’s explanation here to read more about it.

There are several hundred different tags, each one with many possible values and they allow granular control of every aspect of action or trade. One can use FIX messages to set up a buy or sell order, or to subscribe to a market data stream.

To communicate using the FIX protocol, there must be a server and client-side. The server is sometimes known as the acceptor and the client is called the initiator.

For example, if we are an individual client connecting to a broker through FIX, the broker would be the acceptor or server and we would be the initiator. The opposite side of the connection is always the counterparty.

Understanding the FIX Protocol

The FIX protocol consists of a collection of tag/value pairs separated by a vertical bar “|” (actually ‘\x01’ in Unicode). Each tag communicates our intentions to the counterparty (e.g. our broker).

A FIX message always starts with the version of the protocol (tag 8), followed by the length of the body (tag 9) and the message type (tag 35); these and some other tags are part of the header of the message. Finally, we close with the checksum (tag 10). In between header and closing tags, we add any other fields we might need.

Look at the next section for some examples.

Finally, to get information about tags and their use, check this great resource. You can look up the usage of any tag and the corresponding values it can take. That site has been incredibly useful for quick references.

Structure of a FIX Message

For reference to the FIX protocol, you can visit this site. It contains information about each tag and message type, such as data type, use, required tags, etc. I would start by looking into tag 35 to see the different message types.

Particularly, the message types I most often had to deal with were:

  • 35 = A (logon)
  • 35 = 0 (heartbeat)
  • 35 = 3 (reject)
  • 35 = 5 (logout)
  • 35 = 8 (Execution report)
  • 35 = D (New Order Single)
  • 35 = V (Market Data Request)
  • 35 = W (Market Data snapshot refresh)

Others that I have occasionally encountered with Tier1FX and/or FXPig (both forex brokers) are:

  • 35 = j (Allocation Instruction)
  • 35 = P (Allocation Inst Acknowledgement)
  • 35 = Y (Market Data Request Reject)

The Header

A FIX message (at least for version 4.4 of the protocol) always begins with tags 8, 9, and 35, in that order. These tags comprise the ‘header’ of the message. Tag 8 is the beginning string and indicates the FIX version to use. Next, tag 9 indicates the body length of the message (an integer representing the number of characters after it).

Finally, tag 35 indicates the type of message that we are sending. For example, 35=A is a ‘logon‘ message and requires login credentials tags (553 and 554). 35=5 is a ‘logout‘ message. A single buy/sell order has 35=D.

Body And End of Message

Tags 8,9, and 35 are part of the header of the message (along with many other tags as defined in the XML dictionary file). After the header, the body of the message starts, and it requires more tags. Take a look at some of the examples later below.

Finally, the message ends with the checksum (tag 10). The checksum follows specific rules to compute. I think it is the sum of all bytes in the message, up to but excluding the checksum field itself. Then we take that value and apply modulo 256 to it and represent it as a 3-digit number, with leading zeros. Fortunately, QuickFIX computes it automatically, so it’s not something we need to deal with.

Each tag/value pair is separated from each other by ‘\x01’ in Unicode. This symbol appears as a vertical bar ‘|’ but one needs to be careful not to actually type ‘|’ for the message. It needs to be ‘\x01’.

Example Message

A logon FIX message might look like this:

8=FIX.4.4|9=129|35=A|34=1|49=***|52=20190321-04:10:19.000|56=***|98=0|108=30|141=Y|553=***|554=***|10=147|

If you want to know what each of the tags above means, check the reference link I provided before. However, just to save you some time, tags 553 and 554 correspond to your username and password, respectively.

Each counterparty can have its own non-standard FIX implementation. For this reason, we need to know the FIX configuration used by the counterparty in advance. You can usually do this by requesting a document (an XML dictionary) from the broker or counterparty. This XML file indicates the types of messages supported by the platform.

The repository includes the Fortex FIX protocol implementation for reference. Use that document (or get an up-to-date one) to develop a client for Tier1FX or Fortex since they override the FIX standard to suit their own needs.

FIX Sessions

To connect to a counterparty, we do it by using sessions. If the counterparty is a broker, they will most likely provide two sessions, one for trading and one for receiving quotes. Each session has its own endpoint with a different port number that our client connects to.

All aspects of a session are described on a configuration file which is used by the FIX application. The configuration file will contain flags for whether to check custom tags or ignore them, if a data dictionary should be used to validate messages, etc.

Repeating Groups

The standard FIX protocol does not specify an order for tags in the message, except for the three in the header (8,9,35) and the trailing one (10). Another exception is repeating groups.

Repeating groups are blocks of tags within a message which can appear several times in that message. For example, when requesting market data quotes, we can ask for top-of-book data (so only the highest bid and the lowest ask prices) and that will come enclosed in a repeating group.

To illustrate, the following message is a refresh (35=W) sent by the counterparty as a response to a subscription data request (35 = V) for the EUR/USD forex trading pair:

8=FIX.4.4|9=185|35=W|34=328|49=***|52=20190322-15:31:36.850|56=***|55=EUR/USD|262=2-1553282994.168723|268=2|269=0|270=1.127870|271=1000000|269=1|270=1.127900|271=1000000|10=029|

If we observe tag 268 in the message above, we see it has a value of 2. Tag 268 tells us the number of market data entries that come ahead, so basically it tells us that there are two repeating groups for market data.

We can recognize repeating groups in this message because they contain tags 269, 270, and 271. We see that the group repeats again. Tag 269 tells us if the quote is a bid or an ask. Tag 270 tells the price and tag 271 tells the volume. The QuickFix library does a good job of automatically detecting these groups and separating them.

Different counterparties can include more tags for this particular group. Moreover, the .xml FIX dictionary file will define how to form repeating groups.

Python FIX Client Implementation

Now that I’ve covered the basics, let me share with you a simple QuickFIX engine I wrote in Python. You can check the full code in my repository. There are many files, and most of them have to do with running the app but are not directly related to FIX.

To check the actual FIX implementation, go to fixapp/clients/, and there you will have two files:

The main implementation that you may want to go over is in fix_clients.py.

Conda Environment

I recommend using a virtual environment to install the dependencies for this project. I did it this way:

conda create -n auto_trader python=3.7

Once the environment is created, we need to activate it:

conda activate auto_trader

and inside the environment we can now install the dependencies with pip:

pip install -r requirements.txt

Troubleshooting

Sometimes, installing QuickFIX with pip is not as straightforward as desired. Since it is a C++ library, it needs some compilation. In this section, I want to cover some possible errors and how to solve them. If you install the requirements with :

pip install -r requirements.txt

you might get some compilation errors, such as:

  • gcc: error trying to exec ‘cc1plus’: execvp: No such file or directory

This error probably means that there is something wrong with your C++ compiler. It could show an error like the following:

... 
   compilation terminated.
    ...not found
    Testing for std::unique_ptr...
    gcc -pthread -B /home/andresberejnoi/miniconda3/envs/auto_trader/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -std=c++0x -DPYTHON_MAJOR_VERSION=3 -IC++ -I/home/andresberejnoi/miniconda3/envs/auto_trader/include/python3.7m -c test_std_unique_ptr.cpp -o test_std_unique_ptr.o
    gcc: fatal error: cannot execute ‘cc1plus’: execvp: No such file or directory
    compilation terminated.
    ...not found
...

In this case, all I had to do was install the following:

sudo dnf install gcc-c++

and everything worked after.

FIX Config File

In order to initiate a FIX connection with a counterparty, you need to have a FIX configuration file. I left one as an example, here. Keep in mind that there are fields I left incomplete (marked with ****).

You need to provide your credentials for the particular broker you are joining. For example, I used Tier1FX as the broker in my app, so that means I created an account with them and all the details in that file were provided by them.

If you use a different broker, adjust the parameters to match yours.

QuickFIX Base Python Class

To create your own FIX engine, you inherit from the quickfix.Application class. A simple FIX client could look like this:

import quickfix as fix


class FixClient(fix.Application):
    settingsDic = {}
    TRADE_SESS  = None
    QUOTE_SESS  = None


    def onCreate(self, sessionID):
        '''Improve this function later. Right now it expects exactly two sessions which contain specific strings'''
        num_sess = self.session_settings.size()
        if sessionID.toString().lower().find('quote') != -1:        #if the sessionID contains the word 'quote' we will assume it is a quote session
            self.QUOTE_SESS    = sessionID
        elif sessionID.toString().lower().find('trade') !=-1:       #if sessionID contains 'trade' we assume it is a trade session.
            self.TRADE_SESS = sessionID

        self.settingsDic[sessionID.toString()] = self.session_settings.get(sessionID)
        return


    def onLogon(self, sessionID):
        self.sessionID = sessionID
        return


    def onLogout(self, sessionID):
        return


    def toAdmin(self, message,sessionID):
        msg_type    = message.getHeader().getField(fix.MsgType().getField())

        if msg_type == fix.MsgType_Logon:
            username = self.settingsDic[sessionID.toString()].getString('SenderCompID')
            password = self.settingsDic[sessionID.toString()].getString('Password')
            message.setField(fix.Username(username))
            message.setField(fix.Password(password))
        return


    def fromAdmin(self, message, sessionID):
        fix_str = unicode_fix(message.toString())
        self.decoder.print_report(message)
        return


    def toApp(self, message, sessionID):
        fix_str = unicode_fix(message.toString())
        return


    def fromApp(self, message, sessionID):
        '''Capture Messages coming from the counterparty'''
        fix_str = unicode_fix(message.toString())
        self.decoder.print_report(message)
        return


def unicode_fix(string):
    """Take string and replace characters FIX '|' characters to ones that appear correctly in the terminal and return it"""
    bar_in_unicode = '\x01'  # '|' from FIX messages in unicode
    new_str = string.replace(bar_in_unicode, '|')
    return new_str

The methods above are there for you to override with some desired behavior:

  • onCreate
  • onLogon
  • onLogout
  • toAdmin
  • fromAdmin
  • toApp
  • fromApp

I don’t want to stretch this post too long, so here is the official documentation for QuickFIX. Scroll down the page to find the Python section and an explanation of what each of those methods does.

Also, check my implementation to see a more specific example.

Final Thoughts

I was not expecting this post to be this long, but there was a lot to cover. You can use FIX to trade or to even set up a trading bot that runs 24/7. My implementation is just an example but I would not recommend using it with real money.

You can take a look at your broker’s API and see if they support FIX. Sometimes this option is only available for institutional traders.

If you found this information interesting, please consider subscribing to the newsletter email list. I will probably only send 1 or 2 emails per month with updates on new posts and other useful information I find.

Let me know if there is any question in the comments, and here are more Python posts that you might be interested in.

Have anything in mind?