Backtest your Trading Strategies with Bitfinex Terminal and Honey Framework

Backtest your Trading Strategies with Bitfinex Terminal and Honey Framework

The Honey Framework provides easy ways to create and trade back trading strategies. Today we’re looking at how you can re-test your trading strategies with Bitfinex Terminal and Node.js data. Bitfinex Terminal was released with the needs of algo traders in mind – a fast and reliable way to reconcile and share historical data. We will take a trading strategy, EMA Crossover, and back it up on historical candle data.

We will run our backtest on 5 minute candles of BTCUSD trading data. That data, as in blockchain, is stored in a Merkle Tree and can be cryptographically validated. The data is shared over a peer-to-peer (P2P) network and can be streamed live. That means the backtest can start running while we’re fetching the data and resuming the downloads after a connection reset has been done for us. All of these features make Terminal a compelling choice for trading data sharing and trading signals.

Bitfinex Terminal supports Dazaar Cards, which are an easy way to access the data streams. Each data stream has a unique, cryptographically authenticated ID, similar to a Bitcoin address. The Dazaar card contains a content description and stream ID. Imagine it as a torrent file, but for real-time encrypted data streams.

Let’s give it a try

Initially, we set the required dependencies:

npm install dazaar hyperbee bitfinex-terminal-key-encoding bfx-hf-util bfx-hf-backtest 
  bfx-hf-strategy bitfinex-terminal-terms-of-use

We also have to create a file; Let’s say backtest.js. We can now start writing our code.

To see the results of our backtest on the console we must enable debug output:

process.env.DEBUG = process.env.DEBUG || 'bfx:*'

The EMA-Cross-strategy we are running today is one of the model strategies included in the Honey Framework. We load that strategy with our other dependencies. One dependency on it is the Terms of Use for Bitfinex Terminal – we will have to upload them to Dazaar once they have been read and acknowledged:

const dazaar = require('dazaar')
const swarm = require('dazaar/swarm')
const Hyperbee = require('hyperbee')
const keyEncoding = require('bitfinex-terminal-key-encoding')

const HFBT = require('bfx-hf-backtest')
const { SYMBOLS, TIME_FRAMES } = require('bfx-hf-util')
const EMAStrategy = require('bfx-hf-strategy/examples/ema_cross')

const terms = require('bitfinex-terminal-terms-of-use')

We also define the role of a small helper to give us the time of exactly 24 hours in the past:

const get24HoursAgo = (date) => {
  const res = date.getTime() - (1 * 86400 * 1000)

  return new Date(res)
}

For posttesting, we must define the market we are testing and transfer it to the EMA Strategy:

const market = {
  symbol: SYMBOLS.BTC_USD,
  tf: TIME_FRAMES.FIVE_MINUTES
}

const strat = EMAStrategy(market)

Then we can start Dazaar:

const dmarket = dazaar('dbs/terminal-backtest')

With the above command, Dazaar will create a local database at dbs/terminal-backtest. All our data will be stored in this folder, so if you want to start over, you can delete it.

As a next step we are downloading the Dazaar Card for BTCUSD from https://raw.githubusercontent.com/bitfinexcom/bitfinex-terminal/master/cards/bitfinex.terminal.btcusd.candles.json and downloading it:

wget https://raw.githubusercontent.com/bitfinexcom/bitfinex-terminal/master/cards/bitfinex.terminal.btcusd.candles.json
const card = require('./bitfinex.terminal.btcusd.candles.json')

Dazaar also supports paid gatekeepers, eg for the sale of successful strategy trading signals, but Bitfinex Terminal data is free. Our card is loaded into Dazaar. We enable thin mode, with barely set mode, it will only download data requested by us. We also upload the terms of service we needed above, after we read them and acknowledged them:

const buyer = dmarket.buy(card, { sparse: true, terms })

If we wanted to download a full copy of all the candles in the background, we would install sparse i false.

Once the data is ready, Dazaar will emit a feed event. Once that is dropped, we can set up Hyperbee on top of Dazaar feed. Hyperbee provides us with a B-Tree structure to easily query the data. For Terminal, we use a special key encoding module, which makes it easier to query the data. We have already loaded the key encoding module with const keyEncoding = require('bitfinex-terminal-key-encoding'). After setting up a Hyperbee database, we can call runTest and working with the database:

buyer.on('feed', function () {
  console.log('got feed')

  const db = new Hyperbee(buyer.feed, {
    keyEncoding,
    valueEncoding: 'json'
  })

  runTest(db)
})

The last part of our backtest is the actual testing. For that, we have to define the function runTest:

async function runTest (db) {

}

Inside the function runTest we must define the timeframe we want to use for our test. For the beginning of the schedule, we use the helper function we created at the beginning of our tutorial. The value for to point to current time:

 const from = get24HoursAgo(new Date())
 const to = new Date()

Along with our strategy and the market, we transfer the schedule definitions to HFBT.execStream. It returns a function called exec and a function called onEnd:

  const { exec, onEnd } = await HFBT.execStream(strat, market, {
    from,
    to
  })

The function exec is applied to all elements of the stream, but first we have to start the stream. We will run our test on 5m candles. To get all the data within that 24 hour timeframe we use our variables from a to in the respective fields lte (less than equal) a gte (more than equal):

  const stream = db.createReadStream({
    gte: { candle: TIME_FRAMES.FIVE_MINUTES, timestamp: from },
    lte: { candle: TIME_FRAMES.FIVE_MINUTES, timestamp: to }
  })

With the help of Iterator Async we call exec each entry and store the result in it btState:

  let btState
  for await (const data of stream) {
    const { key, value } = data
    btState = await exec(key, value)
  }

Then, once the repeater gives us no more results, we call the function onEnd, which will print our results to the console:

  await onEnd(btState)

This is the whole function runTest:

async function runTest (db) {
  const from = get24HoursAgo(new Date())
  const to = new Date()

  const { exec, onEnd } = await HFBT.execStream(strat, market, {
    from,
    to
  })

  const stream = db.createReadStream({
    gte: { candle: TIME_FRAMES.FIVE_MINUTES, timestamp: from },
    lte: { candle: TIME_FRAMES.FIVE_MINUTES, timestamp: to }
  })

  let btState
  for await (const data of stream) {
    const { key, value } = data
    btState = await exec(key, value)
  }

  await onEnd(btState)
}

After the function is finished runTest, we’re almost done. To get started, we have to go online:

swarm(buyer)

Here is the entire code in one file for our backtest.

Now, when we run our file, we print the result for the console:

As you can see, we would have made a loss with this strategy during the schedule 9/6/2020, 3:18:58 PM to 9/7/2020, 3:18:58 PM, at the time of writing this article.

Collection

In this article, we have looked at how backdating Honey Framework trading strategy works with Bitfinex Terminal. We were able to create post-test in less than 70 lines of code with an easy and reliable way to sync large amounts of data. Everything ran on top of Dazaar, which can also be used to sell trading signals. One of our next articles will cover exactly that topic, so stay tuned!

Source