by Martin Kou
In the last article of this series, we've discussed the design motivations behind Hummingbot, the clock and the market connectors. Today, we'll be discussing the architecture behind trading strategies - the very component that decides Hummingbot trades with your money. We will also discuss how you can diagnose problems and debug Hummingbot in live trading.
If market connectors are the hands and eyes of Hummingbot, then strategy is the brain of Hummingbot. Strategy objects process market signals, and decide when and what orders to create or remove on markets.
We will use the Avellaneda & Stoikov market making strategy as an example for our discussions.
Watching the Market Like A Movie
Every strategy class is a subclass of the
TimeIterator class - which means, in normal live trading, its
c_tick() function gets called once every second. Each time
c_tick() is called, the strategy object should be making observations on the latest market information, and decide what it should do.
You can imagine strategy objects are watching the markets like a movie, where each second is one frame of the movie - and it's constantly trying to decide what to do about any new developments from the movie.
Let's take a look at the
c_tick() function of the Avellaneda & Stoikov market making strategy in Hummingbot, below:
Here is an overview of what the strategy is doing every second - after it's been properly initialized.
Line 412 - perform market observations and update indicators
This function performs observations on both price actions on the market, and also the current inventory on the trader's account. Specifically, it updates the price volatility trailing indicator
_avg_vol, and periodically updates the
_eta parameters as specified from the Avellaneda & Stoikov paper.
Line 415 - checking timestamps for creating new orders
This line looks at the current wall clock time and determines whether it's time to create new orders on the market.
Line 424 - decide on the order prices, if creating order
This line calculates the prices for creating market making orders, from the indicators and parameters just calculated above.
Line 426 to 433 - create an intermediate order proposal for creating orders later
The intermediate order proposal specifies the price, amount and side of orders to be created. However, they do not consider whether there are already similar active orders on the market.
- Line 435 - cancel expired active orders, or if the new order proposal has different prices to the existing orders
Line 438 to 443 - execute order proposals
These lines executes the order proposals generated from the logic above - sending orders to the exchange and tracking them.
Reading Prices and Order Book
If you do a manual trace of the
get_mid_price() functions in the Avellaneda & Stoikov strategy code, you'll find it leads to
OrderBook.c_get_price() in the module
OrderBook class tracks the real-time order book, including depth on both sides, trades and prices, on live exchanges. Each market pair in an exchange market will have one order book. Since trailing indicators often depend on price and order book depths as their fundamental inputs, order book information is often among first inputs to be read by a strategy in every
Sophisticated strategies often need some trailing indicators from the market, in addition to the current prices or order book depth information, to make trading decisions. This includes most of the technical analysis indicators such as EMA, MACD and Bollinger Bands. In Avellaneda & Stoikov strategy example, the object
self._avg_vol, which is an instance of
AverageVolatilityIndicator, is a trailing indicator for recent price volatility.
Let's take a look at how it collects new samples from the strategy code. For every call to
c_collect_market_variables() would send the newest price to
When you look into the relevant code for calculating the values of the trailing indicator, you'll find that
AverageVolatilityIndicator stores a fixed number of samples of prices and outputs a smoothed standard deviation statistic of the prices in the window.
If you want to write your own custom indicators, you can do so by inheriting from
BaseTrailingIndicator just like the above, and writing your own sampling and calculation logic.
High Performance Backtesting / Hummingbot Pro Preview
If you have looked behind the hood of the
Clock class in Hummingbot, you'll find that it has two modes: real time and back testing.
We are going to release an enhanced version of Hummingbot, Hummingbot Pro, that allows for high performance back testing with high resolution historical order book data. It gives you the ability to plug in any strategy written for Hummingbot, run historical order book data traces with it, and give you the output on the trading performance for that strategy. It is capable of simulating days of high frequency trading within seconds. It also allows you to test a wide range of parameters with the strategy to search for the optimal parameters for trading.
A big reason for using Cython within Hummingbot is high performance backtesting. While there are other Python open source quant frameworks that allow live trading and backtesting - most of them only work with low resolution data (e.g. daily OHLCV candles) and are not designed with high frequency trading simulations in mind. Having performance-critical code written in Cython allows Hummingbot strategies to be simulated with high-resolution order book data.
Here are some preview images of the kind of output you can get from Hummingbot Pro backtesting simulations.
Hummingbot Pro will be a paid service for professional traders and hedge funds. It is coming in Q4 2021.
Community and Developer Friendliness
Hummingbot is designed from the ground up with developers in mind. Crypto markets are constantly changing. Whether it is the services and APIs offered by exchanges, the participants and the way the markets move - it's constantly in flux. Developer are uniquely suited to take advantage of this kind of environment, because they are able to modify and tune their strategy and connector code as the market evolves.
When you are writing a new connector, or a new strategy - it is critical to be able to observe the detailed behavior of your code, and diagnose any problems as it is happening. The logging facility is one tool developers can use. The other tool in a Hummingbot developer's arsenal, is the debug console.
The Hummingbot debug console is disabled by default. It needs to be enabled by editing
conf/conf_global.py and setting
Once that has been set, you will be able to telnet to
localhost:8211 to access an interactive Python console that has access to the same memory space as the live Hummingbot instance. You can, for example, examine the live properties from the currently running strategy object and look at the active orders it has made and is tracking.
You can read more about the debug console from Hummingbot documentation.
Our Discord server is a good place you can find other Hummingbot developers, who, like you, may be creating their own strategies, indicators and market connectors. We have several developer oriented channels, where you can get community support on how to create your own modifications to Hummingbot.
Finally, if you would like to report issues in Hummingbot, or contribute code - our Github page can be found at https://github.com/CoinAlpha/hummingbot.
Using the Avellaneda & Stoikov market making strategy as an example, we have discussed how Hummingbot strategies work, how strategies observe the markets, and how trailing indicators can be created in Hummingbot.
We then discussed the high performance aspect of Hummingbot and gave a preview for the upcoming Hummingbot Pro service.
Finally, we discussed some tricks you can use to diagnose the behavior of a live Hummingbot instance via the debug console, and how you can get help from the Hummingbot community.
This concludes part 2, and the overall Hummingbot Architecture series. If you have any further questions, feel free to drop by our Discord server at https://discord.hummingbot.io.