MACD and EMA Trading Strategy Back Tested with Bitcoin 5 Minute Chart

Trader Pro’s video claimed that MACD+EMA trading strategy performed very well. I wanted to test his assertion and see how it played out with Bitcoin. In this article I will first describe the strategy, then backtest it with last 12 month’s five minute Bitcoin historical price data, afterwards optimize the strategy, and finally test the optimized parameters with data out of the range of the optimization. I would like to remind that this is not trading or investment advice, backtest results are often unreliable.

The Strategy

EMA Indicator

Exponential moving average (EMA) is a kind of moving average (MA). It allocates a greater weight and importance on the most recent data points. EMA reacts faster than a simple moving average to recent price changes. Traders use EMA, similar to other MAs, to detect buy and sell signals depending on crossovers and divergences from the historical mean. The most common used periods with EMA are 10, 50, and 200 periods.

MACD Indicator

When the momentum of the price increases, a shorter (e.g. 12) and a longer (e.g. 26) period EMA diverge from each other. MACD aims to profit from this divergence. The difference between the EMAs is plotted on a chart. A positive value indicates an upward momentum whereas a negative value represents a downwards momentum.

MACD function in TALib return three values: MACD, MACD signal, and histogram. We only use the first two in this strategy.

Entry and Exit Signals

For long positions:

Buy when:

  • price > EMA(200) and
  • MACD and MACD signal are both < 0
  • MACD crosses over MACD signal

Sell when:

  • fixed percentage of stop loss (1%) and profit target (2%).

For short positions:

Buy when:

  • price < EMA(200) and
  • MACD and MACD signal are both > 0
  • MACD signal crosses over MACD

Sell when:

  • fixed percentage of stop loss (1%) and profit target (2%).

Results and Interpretation

I tested this strategy within the time frame of 1 May 2020 and 5 May 2021. Here are the results:

Start                     2020-05-01 00:00:00
End                       2021-05-05 23:55:00
Duration                    369 days 23:55:00
Exposure Time [%]                   54.156457
Equity Final [$]                 320916.64728
Equity Peak [$]                  1151537.3636
Return [%]                         -67.908335
Buy & Hold Return [%]              554.260205
Return (Ann.) [%]                  -67.008369
Volatility (Ann.) [%]               12.597566
Sharpe Ratio                              0.0
Sortino Ratio                             0.0
Calmar Ratio                              0.0
Max. Drawdown [%]                  -73.713143
Avg. Drawdown [%]                   -2.771741
Max. Drawdown Duration      355 days 20:55:00
Avg. Drawdown Duration        9 days 09:52:00
# Trades                                  744
Win Rate [%]                        31.989247
Best Trade [%]                       4.103754
Worst Trade [%]                     -1.321455
Avg. Trade [%]                      -0.159261
Max. Trade Duration           5 days 09:20:00
Avg. Trade Duration           0 days 06:23:00
Profit Factor                        0.815869
Expectancy [%]                       -0.14776
SQN                                 -2.376315

This does not look good. Where buy and hold was highly profitable this strategy lost money. I think we got whipsawed a lot. In its current form this strategy is not only useless but looks harmful. Let’s optimize and see how it can be made better.

Optimization and Optimized Results

I optimized stop loss and profit targets for both short and long positions. Here are the top ten performing optimizations:

profit_short  profit  stop_loss  stop_loss_short
97            109     95         104                4.592635e+06
                      92         108                4.500133e+06
                      93         106                4.498104e+06
                                 107                4.307740e+06
98            109     90         102                4.233051e+06
97            109     93         105                4.062826e+06
                      90         106                4.057114e+06
98            109     93         107                3.962142e+06
97            109     94         106                3.942139e+06
                      93         108                3.877335e+06

Below is the heatmap of the parameters. We are interested in the yellow areas.

Heatmap of the optimization parameters for the MACD and EMA trading strategy

Here are the results with the optimized parameters profit_short = 97, profit = 109, stop_loss = 95, and stop_loss_short = 104.

Start                     2020-05-01 00:00:00                                       
End                       2021-05-05 23:55:00
Duration                    369 days 23:55:00
Exposure Time [%]                   91.019611
Equity Final [$]                4592635.10706
Equity Peak [$]                 4886035.48912
Return [%]                         359.263511
Buy & Hold Return [%]              554.260205
Return (Ann.) [%]                   354.60579
Volatility (Ann.) [%]              303.798608
Sharpe Ratio                          1.16724
Sortino Ratio                        9.517271
Calmar Ratio                        14.031138
Max. Drawdown [%]                  -25.272774
Avg. Drawdown [%]                   -1.387017
Max. Drawdown Duration       61 days 08:00:00
Avg. Drawdown Duration        1 days 01:41:00
# Trades                                  137
Win Rate [%]                        55.474453
Best Trade [%]                      11.651555
Worst Trade [%]                     -5.307878
Avg. Trade [%]                       1.119704
Max. Trade Duration          29 days 09:00:00
Avg. Trade Duration           2 days 10:55:00
Profit Factor                        1.619271
Expectancy [%]                       1.284819
SQN                                  1.808693

Looks much better. We did not loose money and made about two thirds of buy and hold. 55% of the trades were winning trades. But notice that we wait a lot. Average trade duration was 2 days. And at least once we had to wait about a month. Here is a chart from the optimized results. The green triangles show successful trades and red ones show the failed ones.

Chart showing example trades for the optimized EMA and MACD strategy for Bitcoin

Testing Optimized Parameters with Another Time Frame

Although I am not comfortable with setting the stop loss for short at a higher percentage at profit target, I will test the optimized strategy for one year ago using again a 12 month period. Between 1 May 2018 and 30 April 2020. Here are the results:

Start                     2018-05-01 00:00:00
End                       2020-04-30 23:55:00
Duration                    730 days 23:55:00
Exposure Time [%]                   90.787322
Equity Final [$]                  757737.8354
Equity Peak [$]                 1085257.11776
Return [%]                         -24.226216
Buy & Hold Return [%]               -4.626487
Return (Ann.) [%]                  -12.540586
Volatility (Ann.) [%]               53.761354
Sharpe Ratio                              0.0
Sortino Ratio                             0.0
Calmar Ratio                              0.0
Max. Drawdown [%]                    -55.7138
Avg. Drawdown [%]                   -8.489944
Max. Drawdown Duration      426 days 17:45:00
Avg. Drawdown Duration       42 days 22:38:00
# Trades                                  246
Win Rate [%]                        47.560976
Best Trade [%]                      10.927534
Worst Trade [%]                     -5.313921
Avg. Trade [%]                      -0.113487
Max. Trade Duration          66 days 00:05:00
Avg. Trade Duration           2 days 16:41:00
Profit Factor                        1.009916
Expectancy [%]                       0.024146
SQN                                 -0.415716

Not very good again. Because we over-fitted. Although our winning rate is close to 50%, we would have lost money during this period had we used this strategy.

Concluding Thoughts

MACD and EMA are commonly used in trading strategies but as we have seen it is not always reliable. We got many entry signals but the highly volatile and unpredictable Bitcoin market led us to fail about half of our attempts. I suggest doing careful backtesting on your own to criticize any trading system that is claimed to be profitable, especially if you are dealing with different markets that the strategies were offered for. Again, not trading advice.

Complete Python Code

import sqlite3 as sql
import pandas as pd
import numpy as np
import talib as talib
from datetime import datetime
from datetime import timedelta
from backtesting import Strategy, Backtest
from backtesting.lib import crossover
from matplotlib import pyplot as plt
import seaborn as sns

dbfile = 'bitcoin.db'
table = 'bitcoin_5minute_raw'
conn = sql.connect(dbfile)

start_date = '2021-05-01'
how_many_months = "-24"
SQL = "SELECT DISTINCT Date,Open,Close,Low,High,Volume_USD FROM " + \
table + " WHERE Date < date('"+ start_date +"') and Date > date('"+ \
start_date+"','start of month','"+how_many_months+ \
" month') ORDER BY Date"
print(SQL)

data = pd.read_sql(SQL, conn)
data['Date'] = pd.to_datetime(data['Date'])
data.set_index("Date", inplace = True)
data.columns = ['Open','Close','Low','High','Volume'];

class MACDCross(Strategy):
    stop_loss = 95
    profit = 109
    stop_loss_short = 104
    profit_short = 97
    long_short = 0
    buy_price = 0
    sell_price = 0
        
    def init(self):
        self.macd, self.macdsignal, self.macdhist = self.I(talib.MACD, self.data.Close, fastperiod=12, slowperiod=26, signalperiod=9)
        self.ema200 = self.I(talib.EMA, self.data.Close, timeperiod=200)
            
    def next(self):
        price = self.data.Close[-1]
        if (not self.position and 
        	price > self.ema200[-1] and
            crossover(self.macd,self.macdsignal) and 
            self.macd[-1] < 0 and self.macdsignal[-1] < 0):
            self.buy(sl=price*self.stop_loss/100)
            self.buy_price = price
            self.long_short = 1
        elif (price > self.buy_price*self.profit/100 and self.long_short == 1):
            self.position.close()
            self.long_short = 0
        elif (not self.position and 
        	price < self.ema200[-1] and
            crossover(self.macdsignal,self.macd) and 
            self.macd[-1] > 0 and self.macdsignal[-1] > 0):
            self.sell(sl=price*self.stop_loss_short/100)
            self.sell_price = price
            self.long_short = 2
        elif (price < self.sell_price*self.profit_short/100 and self.long_short == 2):
            self.position.close()
            self.long_short = 0
            self.sell_price = 0

bt = Backtest(data, MACDCross, cash=1000000, commission=.002, exclusive_orders=True)
output = bt.run()
print(output)
bt.plot()
'''
stats, heatmap = bt.optimize(profit_short = range(90,99,1), profit = range(101,110,1), stop_loss=range(90,99,1), 
    stop_loss_short=range(101,110,1),  maximize='Equity Final [$]',return_heatmap=True)
print(stats)
print(stats._strategy)
hm = heatmap.groupby(['profit', 'profit_short','stop_loss','stop_loss_short']).mean().unstack()
print(heatmap.sort_values(ascending=False).iloc[:10])
plt.figure(figsize=(12, 10))
sns.heatmap(hm[::-1], cmap='viridis')
plt.savefig('foo.png')
bt.plot()
'''

References

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.