Tutorial: Creating algo strategies using the Python Honey Framework

Jacob Plaster
Bitfinex
Published in
5 min readMar 28, 2019

The Honey Framework Python module is still in experimental stage and it is not recommended to be ran on live markets yet.

Algorithmic trading has gained a lot of popularity in the world of cryptocurrencies and, together with the all-new Python edition of the Honey Framework, it is now as easy as ever to create your own algo strategy.

So what is the Honey Framework?

The Honey Framework is a Python/NodeJs library which is the scaffolding to your algo trading strategy. The framework provides many features such as the ability to add pre-made/custom indicators, complex position management, real-time data feeds, multiple back-testing modes and a lot more.

In the following tutorial we use the Python Honey Framework to create a simple trading strategy and perform both an offline and a live back-test. (Brief disclaimer, this trading strategy is purely for educational purposes and is not advice on how to create a live profitable strategy.)

Installing the dependencies

In order to get the framework up and running we need to make sure we have bitfinex-api-py, bfx-hf-indicators-py and bfx-hf-strategy-py all installed into our PYTHONPATH directory.

Once we have cloned all three of these repos we can install the dependencies by running the commands:

cd bitfinex-api-py
python3 -m pip install -r requirements.txt --user
cd ../bfx-hf-strategy-py
python3 -m pip install -r requirements.txt --user

Creating the strategy instance

First we need to import all of the libraries that we are going to use. The HF uses Python3 asyncio as its runtime event loop so we need to import that. We are going to use the EMA (Moving average) crossover point to detect when to enter/exit our positions so we also need to import that from the bfxindicators module that we cloned earlier.

import asyncio
from hfstrategy import Strategy
from bfxhfindicators import EMA

# Initialize strategy
strategy = Strategy(
symbol='tBTCUSD',
indicators={
'emaL': EMA([100]),
'emaS': EMA([20])
},
exchange_type=Strategy.ExchangeType.EXCHANGE,
logLevel='DEBUG'
)

So this creates a new strategy instance that is going to execute on the tBTCUSD trading pair using 'Exchange' (not 'Margin'). Since our trading strategy is going to act upon the crossover of the Moving average, this creates 2 instances of the EMA indicator with a length of 100 and 20. The HF will automatically pass all price updates into the provided indicators and update them in real-time.

Entering the market

The HF exposes a set of events that we can use to manage the state of our strategy, each event is called depending on the current open position. For example, the on_enter event is called on every price update when there are no other positions open and the on_update_short is called whenever there is a price update and a short position open.

So we are going to use the on_enter event to add the logic on how/when we should enter the market.

@strategy.on_enter
async def enter(update):
emaS = strategy.get_indicators()['emaS']
emaL = strategy.get_indicators()['emaL']
if emaS.crossed(emaL.v()):
if s > l:
await strategy.open_long_position_market(mtsCreate=update.mts, amount=1)
else:
await strategy.open_short_position_market(mtsCreate=update.mts, amount=1)

The above code states that on every price update we want to get the latest values from our EMA indicators. If the indicators have crossed to signal a bullish movement then open a long market position and if they signal a bearish move then open a short position. Now that we have decided how to enter the market we must decide when to exit the market to take profits.

Exiting the market

We need to implement 2 new logic paths: on_update_long and on_update_short. The logic for both of these events will be very similar - if the indicators cross to signal the opposite direction then pull out.

@strategy.on_update_short
async def update_short(update, position)
emaS = strategy.get_indicators()['emaS']
emaL = strategy.get_indicators()['emaL']
if emaS.v() > emaL.v():
await strategy.close_position_market(mtsCreate=update.mts)

@strategy.on_update_long
async def update_long(update, position):
emaS = strategy.get_indicators()['emaS']
emaL = strategy.get_indicators()['emaL']
if emaS.v() < emaL.v():
await strategy.close_position_market(mtsCreate=update.mts)

Once close_position_market is called then a market order will be submitted and the strategy will return to calling on_enteron every price update.

Back-testing

The HF makes it really easy to back-test our strategy and offers 3 different modes: offline, with bfx-hf-data-server for historical data and on live market data with simulated order filling.

In order to back-test against offline data we need to have a file locally that contains a bunch of candles for us to load into the strategy. We can either grab this manually or use the example candles file that is located in the /examples directory in the bfx-hf-strategy-py repo.

from hfstrategy import Executor
exe = Executor(strategy, timeframe='1hr')

exe.offline(file='btc_candle_data.json')

This creates a new instance of the Executor class and loads our new strategy into its constructor. When we run the exe.offline function the candles are pushed into the strategy in the same way as it would be if the strategy was running on live data.

Once the back-test has completed, an in-depth report of our stratgey will be logged to the console displaying profit/loss, volume, each trade, fees paid and a lot more. Also, if show_chart is not disabled in the Executor constructor then matplotlib will render a visual report showing the strategy enter/exit points on the price data.

If we want to test our strategy in the live environment but aren’t ready for it make any real trades then we can also use the exec.backtestLive function. This uses the live bfxapi WebSocket but stubs out the OrderManager and uses a simulated version.

exe.backtest_live()

Sending a sigkill signal (pressing CTRL-C) will exit the strategy and display the same chart/report as above.

Executing on live markets

In the future when we are confident that our strategy is ready to make live trades on the markets then we can simply tell the executor to connect to the live market by calling Executor.live with our Bitfinex API KEY/SECRET.

exe.live("API_KEY", "API_SECRET")

Again, sending a sigkill will display the report/chart.

Thanks for reading!

If you would like to contribute to the project then please just open a pull request to github.com/bitfinexcom/bfx-hf-strategy-py.

For the NodeJS version then head to github.com/bitfinexcom/bfx-hf-strategy.

If you would like to see more examples then please head to github.com/bitfinexcom/bfx-hf-strategy-py/master/examples.

Stay up to date with Bitfinex announcements on Twitter, Telegram, LinkedIn and Youtube.

We’ve recently open-sourced a number of the development libraries most essential to us. If you are interested in learning more about these, please visit our Github.

Join us on our mission to create the most innovative & industry-leading cryptocurrency exchange.

--

--