Original post by Axon Trade Co-Founder and CEO Serg Gulko
Huobi Global is a Singapore based cryptocurrency exchange founded in 2013. With daily volumes of around $2 billion per day, Huobi Global is an extremely liquid exchange for multiple crypto trading pairs including BTC, ETH, LTC, XRP, and more. As of January 2020, it has around 400 verified markets and about 25 unverified markets. It’s worth mentioning that Huobi Global, unlike many other exchanges, has never been hacked and has millions of customers all over the globe.
In this article, we would like to demonstrate how to build a relatively simple but fully functional trading application on top of Axon Trade FIX API that would connect to Huobi. You can freely re-use this as source code in your own projects.
The application will show how to establish two outgoing FIX connections to the Axon Trade FIX Gateway. One of the sessions will be used to receive real-time, normalized market data for a single pair — ETH/USDT. The second session will provide access to trading capabilities alongside certain account’s financial information such as open orders and positions.
So, as the first step, we will connect to the market data session and will subscribe to updates for ETH/USDT.
Simultaneously, the application will establish a connection to a trading session and will perform full cycle recovery operations — get a list of open orders and get available balances.
After we finish the handshake phase, the application will start sending limit orders to buy ETH. The order price will be always 20% outside the market just to make sure that the order will rest in the book and not execute. We will allow the order to rest on the book for several seconds and then cancel it. Then the cycle will start over. Eat. Sleep. Repeat.
Huobi has an advanced and rich API. But, like any big and complex thing, it has its own disadvantages. For example, you can’t send orders using WebSocket and have to use REST API. So what’s the issue? Many digital assets exchanges do the same.
The problem is the confirmation that comes over WebSocket doesn’t contain the client’s assigned ID and only has a Huobi-generated Order ID. However, you can know this external Order ID only after you’ll receive and parse the response on your REST API request. Sometimes, WebSocket’s message is delivered faster than you receive your Order ID which leaves you guessing what this report is all about.
The Axon Trade Orders router successfully solves this problem so our clients can enjoy streamlined ExecutionReport orders in a proper way.
Taking into account that this application will not be that simple, we will use several supportive classes, developed by our team. We do not recommend using these classes in high-load applications because of a lack of optimization but it’s good enough for the purpose of this course.
Feel free to contact us if you need assistance with porting to another language/FIX library, we are happy to help!
The application we will build is a state machine that moves from one stage to another depending on previous conditions. Here is the diagram with possible(simplified to increase readability) states:
As you can see, the application will start sending orders only if it is in the APP_ENTER_MARKET stage which is a combination of MD_READY and ORD_READY. If something unexpected happened, e.g. one of the sessions lost a connection, all trading activities will be stopped.
Let’s get started with the easiest part — establishing a connection to a market data session and subscribing to updates for ETH/USDT.
The Axon Trade MD Feeder expects that you will start your handshake by sending a special message called Logon that contains fields Username and Password.
You might ask a reasonable question — is it secure to send unencrypted credentials? Well, Axon Trade clients are connected to our server using one of these options:
IPSec VPN channel
Co-locating within Equinix NY4 data center
Therefore, all traffic between you and us is completely isolated from other people. And, as we already know your password, there is no sense to hide it from us:)
In the case of successful authorization, our server will respond with a Logon message which moves us to the stage of sending a message to subscribe to the market data stream.
In FIX we use MarketDataRequest to indicate updates for what instrument(s) and exchanges we would like to receive.
In order to act as a true normalization layer, we decided to maintain our own symbology. So, instead of trading ethusdt(on Huobi), usdteth(on Bittrex), or 4(on BlockTrade) the only one name you have to use is ETH/USDT.
Here is a sample of MarketDataRequest structure and part of Java code to create a corresponding message:
Once received by server, MarketDataRequest message will trigger a chain of events:
Server will send real-time incremental updates for the required instrument when the corresponding symbol will be changed on the Huobi end. Instead of sending a full book (which sometimes could be really big) Axon Trade takes an approach to optimize data flow by sending only the parts that actually have been changed — e.g. trades, new price levels added or removed, size updates.
Our book management rules are based on widely used principles from traditional finance — a combination of NEW(0), UPDATE(1), and DELETE(2) events. This is a very straightforward approach that helps to easily build reliable and predictable software.
To illustrate this concept, we built and share on our repository a very simple book. We do not recommend using this code in a production environment because it suffers from significant performance penalties. But it’s still good enough for the purpose of this article.
So we will “feed” all market data events into our simple book. This way we can be sure that we have an accurate picture of what is going on in a market (with one particular instrument).
Perfect, we have the data! But what will we do with it? Let’s dive into building a trading connector!
This part is much more complicated compared with market data because it requires position and order synchronization. In order to keep this article in a reasonable size, we will omit actual synchronization and recovery techniques and will only show how to send messages. Anyway, each trading application has its’ own backend and own rules…
The first step is completely the same as the market data session — Logon. Once we pass authorization, it’s time to request a list of our assets under management. FIX has a special message for this — RequestForPositions. Depending on the usage context, the server will return a current position snapshot and/or will continue to notify you in real-time if position size changes in case of trade, withdrawal or deposit.
The workflow is following — you sent a RequestForPositions message and the server will get back to you with one RequestForPositionAck message that indicates what to expect next. In case you do have some positions, the information will arrive in the form of PositionReport messages.
The very last PositionReport message will have a special marker that we will use to switch to another stage — order synchronization.
Sometimes your trading session might have live working orders sitting somewhere in books. The Axon Trade FIX API has a special message to call for these working horses — OrderMassStatusRequest. In the FIX world, most of the information about orders is transmitted using ExecutionReport message. This message contains the most number of fields in comparison with other messages from FIX language. But it helps to precisely identify orders and their respective statuses.
As long as Axon Trade acts as a DMA provider, we want to make sure that all our operations are 100% transparent. So alongside the client’s assigned ID (transmitted in field ClOrdID(Tag 11)) and Axon Trade assigned ID (OrderID(Tag 37), always integer number), you will see an exchange-assigned ID (in SecondaryOrderID(Tag 198) field).
Our order synchronization strategy works like this — send OderMassStatusRequest and wait for server response. There are two possible scenarios(in case of a valid initial message):
Server will respond with a list of ExecutionReports which can be filtered by MdReqId(request ID, assigned by client) and ExecType=STATUS(I)
In the case when you don’t have open orders, the server will respond with specially formatted ExecutionReport. We did it this way because unlike counterparty to Request for Positions — RequestForPositionsAck, FIX does not have a default confirmation message for order status which might mean you’ll wait for your reports forever.
At this point, our positions and orders are synchronized so let’s have some fun shooting orders!
Our application will take the top of the book price and then will send a Limit order with price X pips aways from the best bid or offer (depending on a side we choose). Our goal is to create an order which will rest in the book for a certain amount of time before the algo cancels it.
To place an order, we will use a message called NewOrderSingle. Its’ fields are pretty straightforward and self-explanatory — you need to specify Side(Tag 54 ) (buy or sell), Type(Tag 40) (Limit in our case), provide a limit price and order size, set Symbol(Tag 55) and a routing destination(Tag 11) (HUOBI in this case). The full fields description for NewOrderSingle message can be found here.
Once an order is sent out, the server will respond with a certain number of ExecutionReports indicating life cycle changes. The Axon Trade platform uses the following statuses to indicate what is going on with your order:
PENDING_NEW: order passed initial verification and is accepted by our platform for further execution
NEW(0): order has been delivered to the target exchange and accepted for execution
PARTIALLY FILLED(1)/FILLED(2): part of the order size or whole order has been matched with a counterparty and executed
CANCELED(4): order has been canceled manually or through API request
REJECTED(8): order has been rejected because of an internal error or wrong parameters
Of course, we all want to see a perfect PENDING_NEW/NEW -> FILLED cycle and our engineers are doing their best in order to provide the fastest tech. We also rely on our LP partners such as Huobi Global.
Perfect, your order is in a book so let’s hurry to cancel it before it gets executed!
To cancel an order, you need to send an OrderCancelRequest message. Here is the tricky part — you need to provide 3 IDs:
ClOrdID: ID for your cancel request
OrigClOrdID: this is an internal ID of your trading order sent on a previous step
OrderID: ID on Axon Trade side
Translated in human language, this record should be read as follows: “use Order ClOrdID to cancel my previously placed order OrigClOrdID/OrderID”
Once we cancel your order, the server will notify you with corresponding ExecutionReport, confirming that trading/working order has been canceled.
In accordance with the workflow of a demo application, the algo will sleep for 2 seconds and then will send another Limit order. Again. And again.
As you can see, it’s relatively easy to build a simple trading application for Huobi using the Axon Trade FIX API. When properly designed, it will work clockwise, without stops for sleep or rest. Of course, you need to integrate some logic to enter and exit markets but there are many signal providers, case studies, technical analysis libraries, and even AI platforms that could be utilized.
No matter what you will use as the “brain” for your actual application, rest assured that the Axon Trade FIX API for trading on cryptocurrency exchanges such as Huobi will remain a trustworthy and reliable partner!