Back test your trading strategies with Bitfinex Terminal & Honey Framework

The Honey Framework provides easy ways to create and back test trading strategies. Today we’ll take a look at how to back test your trading strategies Bitfinex Terminal data and Node.js. Bitfinex Terminal was released with the needs of algo traders in mind – a fast, reliable way to sync and share historical data. We take a trading strategy, EMA Crossover, and test it against historical candle data.

We will be running our back test on 5 minute candles based on BTCUSD trading data. Just like in a blockchain, this data is stored in a Merkle Tree and can be cryptographically verified. The data is shared over a peer-to-peer (P2P) network and can be streamed live. That means the back test can be run while we get the data and resume the downloads after the connection is reset for us. All these features make Terminal an attractive choice for sharing trading data and trading signals.

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

Let’s try

To get started, we install 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 need to create a file; let’s say backtest.js. We can now start writing our code.

To see the results of our console back test, we need to enable debug output:

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

The EMA Cross strategy we are pursuing today is one of the sample strategies in the Honey Framework. We load that strategy with our other dependencies. One dependency on it is the Bitfinex Terminal Terms of Use – we’ll have to load them into Dazaar after reading and acknowledging them:

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 a small helper function 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 back testing, we need to define the market we are testing and pass it on to the EMA strategy:

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


const strat = EMAStrategy(market)

Then we can initialize Dazaar:

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

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

As the next step, we download the Dazaar Card for BTCUSD from https://raw.githubusercontent.com/bitfinexcom/bitfinex-terminal/master/cards/bitfinex.terminal.btcusd.candles.json and load 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 feeds, for example for selling trading signals of a successful strategy, but the Bitfinex Terminal data is free. Our map is loaded in Dazaar. We turn on sparse mode, with sparse mode set, it only downloads the data requested from us. We also load the terms of service we needed above, after reading and confirming 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 set sparse to false.

Once the data is ready, Dazaar will create a feed event. After that airs, we can set up Hyperbee on top of the Dazaar feed. Hyperbee provides us with a B-Tree structure for easy querying of the data. For Terminal, we use a special key encryption module, which makes querying the data easier. We have already loaded the key encryption module with const keyEncoding = require('bitfinex-terminal-key-encoding'). Once the Hyperbee database is set up, we can make a call runTest and work 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 back test is the actual testing. For that we have to define the function runTest:

async function runTest (db) 

Within the position runTest we need to define the time frame we want to use for our test. For the start of the timeframe, we’ll use the helper function we created at the beginning of our tutorial. The value for to points to the current time:

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

We set the deadlines together with our strategy and the market HFBT.execStream. It returns a function named exec and a function called onEnd:

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

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

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

Using an Async Iterator we call the exec with each item and store the result in btState:

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

Then when the iterator no longer gives us results, we call the function onEnd, which will print our results to the console:

  await onEnd(btState)

Here’s the whole feature 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 finishing the position runTest, we’re almost done. To start everything, we have to go online:

swarm(buyer)

Here’s the whole code in one file for our back test.

Now if we run our file, we will get the result printed on the console:

Results

As you can see, with this strategy we would have made a loss in the period from 9/6/2020, 3:18:58 PM to 9/7/2020, 3:18:58 PM, when this article was written.

Conclusion

In this article, we took a look at how back-testing a Honey Framework trading strategy works with Bitfinex Terminal. We were able to back-test 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 link