Repost: My first strategy tested: MACD & SMA trading strategy. (Patreon)
Downloads
Missing 1 file, as this post has not yet been imported.
Content
Intro
This is the blog post that is complementary to the video where I will be investigating the MACD indicator and test out if it is a profitable indicator in combination with the 21 simple moving average.
The macd acts as my primary indicator and the sma is my confirmation indicator.
There are literally hundreds of websites and video’s which describe how macd is calculated and how it works, so I leave it up to you to do your own research on the internal workings of this indicator.
My main goal is to see if this indicator is profitable when using it in a mechanical trading system.
I’m using the default macd settings because a lot of people use it with these settings. This way there is a higher probability that more people act on the same signal.
The setup
See this blog post to know more about the approach I use when making these backtests: https://www.dutchalgotrading.com/general/my-backtesting-methodology/
Strategy
The strategy is as follows
Enter the trade if macd crosses over the signal line and the price is above or crosses above the 21 simple moving average.
Exit the trade when macd crosses below the signal line or price closes below the 21 SMA
Stoploss and takeprofit are determined by the hyper optimisation but are in any case percentage based.
Stoploss and takeprofit
Besides the entry and exit signals of the strategy based on the indicators, there are also stoploss and takeprofit points that can be used.
These stoploss and takeprofit levels are later on determined by the hyperopt functionality of the bot and are percentage based exit levels.
Pseudocode
In pseudocode the strategy looks like this
These lines should be programmed into easy to understand python code that is compatible with the trading bot for the automatic backtest.
Because I will use Freqtrade as my tool for backtesting data my code will be based on this software.
However because it is Python code as I said, it should be easy to read and understand. That is, if you know some python programming ofcourse.
Now lets begin with backtests of this strategy on multiple timeframes and see the results.
Initial backtest results
After backtesting and writing down all the backtest information in a sheet I have the following results
You can see that the daily timeframe has the best results overall
Also you can see that the lesser the timeframe, the more bad trades there are
Strangely the difference between time frames lower than 4 hours have roughly the same results. Possibly because at that point there are no more funds available to put on a trade.
Now let's start hyperparameter optimisation and investigate if there are better results to make when using a better stoploss and takeprofit setting
Let me also investigate which simple moving average provides better results than the 21 sma I used in this strategy.
Hyperparameter optimisation
After I have applied the parameter optimisation session, the bot returned the following advice:Change the 21 sma to a 26 simple moving average
- Apply a stop loss of minus 28% to let the trade breathe
- And apply multiple time based points to guarantee profits.
- You can find these on my blog. I’ll put a link in the description.
These small changes improved the strategies performance by 44%
Conclusions
So The conclusions of my analysis of this strategy are:
- Best timeframe for this macd sma crossover strategy strategy is the 1 day timeframe
- If you started with 1000 dollar, you now could have slightly over 39000 dollar
- Thats a Profit of more than 3800 percent %
- However you risk a drawdown from the highest profit point of around 467 percent
- The point where a drawdown starts is dependent on market circumstances and is very unpredictable So you should be very aware of this when starting to trade with a bot.
- I calculated that the risk of ruin is 170% because you have more losing trades than winning trades.
- However this is a swing trade strategy in which you should let your winners run and cut your losses.
- It is somewhat expected to have more losing trades than winners because this strategy would probably bleed your account during ranging markets. Thats probably also what causes the high drawdown.
- Luna performed the best with 57% wins and more than 500% profit
- Polkadot was the worst performing asset in this portfolio with a win rate of 18% and a loss of 272 percent.
- Overall there were more winning pairs than losing pairs with this strategy and that's hopeful.
So now I'm going to enter the results in the overall strategy comparison sheet.
Strategy League
Because this is my first video in this series I cannot compare it with another strategy.
However, up until now it seems that this strategy is profitable in the long run. But you have to be aware of the risks and possible drawdown involved.
Not every crypto pair was successful in 2021 and you can see this clearly in this test. So If you make a crypto pairs list that is based on the current top 50 coins, then there could be a possibility that it will give you similar results.
So if you want to use this strategy, please do your own research before using it under live trading circumstances!
The strategy code
# --- Do not remove these libs ---
import numpy as np # noqa
import pandas as pd # noqa
from pandas import DataFrame
from functools import reduce
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,IStrategy, IntParameter)
# --- Add your lib to import here ---
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
# import pandas-ta as pta
# --- Generic strategy settings ---
class smamacdcrossover(IStrategy):
INTERFACE_VERSION = 2
# Determine timeframe and # of candles before strategysignals becomes valid
timeframe = '1d'
# Determine roi take profit and stop loss points
minimal_roi = {"0": 2}
stoploss = -1
trailing_stop = False
use_sell_signal = True
sell_profit_only = False
sell_profit_offset = 0.0
ignore_roi_if_buy_signal = False
# --- Plotting ---
# Use this section if you want to plot the indicators on a chart after backtesting
plot_config = {
'main_plot': {
# Create sma21 line and fill the area between sma21 and sma 50
'sma': {'color': 'blue'},
},
'subplots': {
# Create subplot MACD
"MACD": {
'macd': {'color': 'blue', 'fill_to': 'macdsignal'},
'macdsignal': {'color': 'orange'},
'macdhist': {'color': 'green', 'type': 'bar', 'plotly': {'opacity': 0.4}}
},
},
}
# --- Define spaces for the indicators ---
# UNCOMMENT THIS FOR HYPEROPTING
# sma = IntParameter(5, 221, default=21, space="buy")
# --- Used indicators of strategy code ----
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Populate this section with the indicators you want to use in your strategy
# Simple moving average - COMMENT THIS FOR HYPEROPTING
dataframe['sma'] = ta.SMA(dataframe, timeperiod=21)
# UNCOMMENT THIS FOR HYPEROPTING
# for val in self.sma.range:
# dataframe[f'sma_{val}'] = ta.SMA(dataframe, timeperiod=val)
# MACD
# https://mrjbq7.github.io/ta-lib/func_groups/momentum_indicators.html
macd = ta.MACD(
dataframe,
fastperiod=12,
fastmatype=0,
slowperiod=26,
slowmatype=0,
signalperiod=9,
signalmatype=0,
)
dataframe["macd"] = macd["macd"]
dataframe["macdsignal"] = macd["macdsignal"]
dataframe["macdhist"] = macd["macdhist"]
return dataframe
# --- Buy settings ---
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# UNCOMMENT THIS FOR HYPEROPTING
# conditions = []
# conditions.append(((dataframe['macd'] > dataframe['macdsignal']) &
# (qtpylib.crossed_above(dataframe['close'],dataframe[f'sma_{self.sma.value}'])))
# | ((dataframe['close'] > dataframe[f'sma_{self.sma.value}']) &
# (qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal'])))
# )
# if conditions:
# dataframe.loc[
# reduce(lambda x, y: x & y, conditions),
# 'buy'] = 1
# Enter the conditions for buying - COMMENT THIS SECTION OUT FOR HYPEROPTING
dataframe.loc[
(
# Buy when close price crosses above sma21 when macd has already crossed macdsignal
((dataframe['macd'] > dataframe['macdsignal']) &
(qtpylib.crossed_above(dataframe['close'], dataframe['sma'])))
# Or buy when macd crosses above macdsignal when close price has already crossed sma21
| ((dataframe['close'] > dataframe['sma']) &
(qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal'])))
),
'buy'] = 1
return dataframe
# --- Sell settings ---
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# UNCOMMENT THIS FOR HYPEROPTING
# conditions = []
# (conditions.append((qtpylib.crossed_below(dataframe['close'],dataframe[f'sma_{self.sma.value}']))
# | ((qtpylib.crossed_below(dataframe['macd'], dataframe['macdsignal']))))
# )
# if conditions:
# dataframe.loc[
# reduce(lambda x, y: x & y, conditions),
# 'buy'] = 1
# Enter the conditions for selling (besides ROI TP if available)
dataframe.loc[
(
# Sell when macd crosses down macdsignal
(qtpylib.crossed_below(dataframe['macd'], dataframe['macdsignal']))
# Or sell when close price crosses below sma21
| (qtpylib.crossed_below(dataframe['close'], dataframe['sma']))
),
'sell'] = 1
return dataframe