How to write a profitable strategy for algotrading Bitcoin

In this tutorial, I'm going to write a strategy for trading bitcoin with Jesse.  The point of this tutorial is to get you started on using Jesse so you can write your own strategies. I'll also teach you a few tricks that I've picked up over the years that help me write profitable strategies. This is probably going to be a series of articles.

The point of this tutorial is not to give you an awesome strategy; it is to get you started on writing one. In part one, I'll cover everything from:

  • How to use technical analysis
  • How to enter trades
  • How to exit trades
  • How to manage risk

First, make sure you have Jesse installed. If you don't, go ahead and do it. Next, make sure you imported candles. Let's do that now since it takes a few minutes to finish, which is fine because we'll be writing the strategy:

jesse import-candles Bitfinex BTCUSD 2016-01-01

Create the strategy

Let's name this strategy SampleTrendFollowing. cd into your Jesse project and run:

jesse make-strategy SampleTrendFollowing

Now open SampleTrendFollowing located at strategies/SampleTrendFollowing/__init__.py . This is the code generated by Jesse:

from jesse.strategies import Strategy
import jesse.indicators as ta
from jesse import utils


class SampleTrendFollowing(Strategy):
    def should_long(self) -> bool:
        return False

    def should_short(self) -> bool:
        return False

    def should_cancel(self) -> bool:
        return False

    def go_long(self):
        pass

    def go_short(self):
        pass

Using technical analysis 

We'll be using EMA indicator for detecting the direction of the market trend. Let's use two EMA lines with periods of 50 for slower EMA, and 21 for faster EMA. 

@property
def long_ema(self):
    return ta.ema(self.candles, 50)
    
@property
def short_ema(self):
    return ta.ema(self.candles, 21)

Notice that I defined them as class properties the @property keyword. This allows me to use them as self.long instead of self.long() which is a bit easier in my opinion.

Let's also define the ATR indicator which is my favorite tool for setting the stop-loss price:

@property
def atr(self):
    return ta.atr(self.candles)

Entry rules

Our entry rules are simple: go long when the fast EMA line breaks above the slow EMA, and vice versa for short trades. 

def should_long(self) -> bool:
    return self.short_ema > self.long_ema
    
def should_short(self) -> bool:
    return self.short_ema < self.long_ema

Managing Risk

One critical part of every strategy is position sizing. A simple compounding position sizing will get you a long way. For example, let's risk 3% of our total capital per trade.

We also need to specify our entry price. To keep it simple, let's open our positions using a market order.

def go_long(self):
    entry = self.price
    stop = entry - 3*self.atr
    qty = utils.risk_to_qty(self.capital, 3, entry, stop)
    profit_target = entry + 5*self.atr

    self.buy = qty, entry
    self.stop_loss = qty, stop
    self.take_profit = qty, profit_target

def go_short(self):
    entry = self.price
    stop = entry + 3 * self.atr
    qty = utils.risk_to_qty(self.capital, 3, entry, stop)
    profit_target = entry - 5 * self.atr

    self.sell = qty, entry
    self.stop_loss = qty, stop
    self.take_profit = qty, profit_target

Notice that I used ATR indicator for both my stop-loss and take-profit targets.

While it is a generally good practice to exit trend following strategies dynamically, I however set exit points at the moment of opening positions. I did it for the sake of keeping this tutorial simple.

Routes

Now we need to tell Jesse to trade SampleTrendFollowing strategy when we execute the backtest command. We also need to choose a timeframe, and a symbol to trade. I chose 6h timeframe and BTCUSD as my symbol. Your routes.py file should look like this:

# trading routes
routes = [
    ('Bitfinex', 'BTCUSD', '6h', 'SampleTrendFollowing'),
]

Running backtest

Let's see how it performs:

jesse backtest 2019-01-01 2020-01-01

And here are the results:

 CANDLES              |
----------------------+--------------------------
 period               |  365 days (12.17 months)
 starting-ending date | 2019-01-01 => 2020-01-01


 exchange   | symbol   | timeframe   | strategy             | DNA
------------+----------+-------------+----------------------+-------
 Bitfinex   | BTCUSD   | 6h          | SampleTrendFollowing |


Executing simulation...  [####################################]  100%
Executed backtest simulation in:  22.61 seconds


 METRICS                         |
---------------------------------+-------------------------------------
 Total Closed Trades             |                                  24
 Total Net Profit                |                    3991.39 (39.91%)
 Starting => Finishing Balance   |                   10000 => 14063.47
 Total Open Trades               |                                   1
 Open PL                         |                               97.15
 Total Paid Fees                 |                              498.22
 Max Drawdown                    |                              -15.9%
 Sharpe Ratio                    |                                1.18
 Annual Return                   |                              26.46%
 Expectancy                      |                      166.31 (1.66%)
 Avg Win | Avg Loss              |                     558.76 | 383.12
 Ratio Avg Win / Avg Loss        |                                1.46
 Percent Profitable              |                                 58%
 Longs | Shorts                  |                           67% | 33%
 Avg Holding Time                | 2.0 weeks, 12.0 hours, 53.0 minutes
 Winning Trades Avg Holding Time |       1.0 week, 4.0 days, 6.0 hours
 Losing Trades Avg Holding Time  |      2.0 weeks, 5.0 days, 2.0 hours

Look at that, it's actually profitable! Well, at least in 2019 it was. Is that enough backtest for a trend-following strategy trading on 6h timeframe? It's your call, but I would go further back if I have the data. In this case, I went back to 2017 until this year's May 3th:

  CANDLES              |
----------------------+--------------------------
 period               |   1218 days (3.34 years)
 starting-ending date | 2017-01-01 => 2020-05-03


 exchange   | symbol   | timeframe   | strategy             | DNA
------------+----------+-------------+----------------------+-------
 Bitfinex   | BTCUSD   | 6h          | SampleTrendFollowing |


Executing simulation...  [####################################]  100%
Executed backtest simulation in:  92.07 seconds


 METRICS                         |
---------------------------------+------------------------------------
 Total Closed Trades             |                                120
 Total Net Profit                |                 11596.71 (115.97%)
 Starting => Finishing Balance   |                  10000 => 21878.82
 Total Open Trades               |                                  1
 Open PL                         |                             323.36
 Total Paid Fees                 |                            2315.78
 Max Drawdown                    |                            -34.07%
 Sharpe Ratio                    |                               0.78
 Annual Return                   |                             17.57%
 Expectancy                      |                      96.64 (0.97%)
 Avg Win | Avg Loss              |                    647.43 | 418.62
 Ratio Avg Win / Avg Loss        |                               1.55
 Percent Profitable              |                                48%
 Longs | Shorts                  |                          66% | 34%
 Avg Holding Time                |     1.0 week, 2.0 days, 23.0 hours
 Winning Trades Avg Holding Time | 1.0 week, 21.0 hours, 58.0 minutes
 Losing Trades Avg Holding Time  |     1.0 week, 4.0 days, 22.0 hours

Conclusion 

The point of this tutorial was to get you started on writing strategies with Jesse. I will write more articles like this, exploring more features of Jesse such as using multiple timeframes, filters, events, and more.

If you are on Medium, please clap for the Medium version of this article.

© 2020 jesse-ai.com. All rights reserved.