Home Creators Posts Import Register Favorites Logout
haven't archived this post yet. have a subscription? use the importer!

Downloads

  • adxmomentum-files.7z

Missing 1 file, as this post has not yet been imported.

Content

Intro

Hi there and welcome to this blog series where I will be searching for the best trading strategy for cryptocurrency trading.

This is the blog post that is complementary to the video where I will test the ADX Momentum strategy.

In each blogpost I am going to test a strategy that is explained in a youtube video, trading site or is publicly available for use. Each strategy will be tested on its performance by trading multiple digital asset pairs over their largest possible backtest period.The result of this test is then compared with the results of earlier tested strategies. If you like these video’s and want to see more of them, then please click on the like button and subscribe to my channel. If you want to get notified on my newest content then click on the bell icon. It will also help the youtube algorithm.

If you like these articles and you want to see how I execute these tests, then see the video that I included in this post.

Please remember that this post is for entertainment and educational purposes only. I am not a professional, so always do your own research before using any information in real trading.

In this post I will test a strategy that is freely available for use in the Freqtrade github site under the strategies repository. It is the ACX Momentum strategy written by Gert Wohlgemuth.

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

This strategy makes use of the ADX with DI+ and DI- indicators and the momentum indicator. And because they’re all momentum based, I call this a momentum strategy.

The ADX is a trend strength indicator and it is accompanied by the DI+ and DI- indicator. The ADX itself just gives an indication of the trend strength but not the direction of the trend. If it is above 25, then it is considered a strong trend.

The DI+ and DI- have been added to indicate the direction of the trend. When the DI+ is above the DI- then the trend is bullish and vice versa.

https://www.investopedia.com/articles/trading/07/adx-trend-indicator.asp

The momentum indicator is a centered oscillator and obviously also shows you the momentum of an asset. When the indicator is above 0, then it indicates an uptrend and when it is below 0, than it indicates a downtrend.

https://help.signals.network/knowledge-base/indicators/oscilators/momentum-indicator-mom

Stoploss and takeprofit

This strategy has a default takeprofit point when the ROI hits 1% profit

The strategy has a stop loss sell signal when the price hits a 25% loss. So I guess that’s what the breathing room of this strategy is.

Running through the code

The complete code you can find with this link: https://github.com/freqtrade/freqtrade-strategies/blob/master/user_data/strategies/berlinguyinca/ADXMomentum.py

# Minimal ROI designed for the strategy.

# adjust based on market conditions. We would recommend to keep it low for quick turn arounds

# This attribute will be overridden if the config file contains "minimal_roi"

minimal_roi = {

# DCD: The ROI is set to be 1%. SO after 1% profit it exits the trade.

"0": 0.01

}

# Optimal stoploss designed for the strategy

# DCD: The bot will exit the trade if it has a loss of 25% after entering a trade.

stoploss = -0.25

# Optimal timeframe for the strategy

# DCD: Original timeframe is 1 hour.

timeframe = '1h'

# Number of candles the strategy requires before producing valid signals

# DCD: The strategy starts working after 20 candles (so after 20 hours of waiting with the

# original timeframe.

startup_candle_count: int = 20

The ROI is set to be 1%. SO after 1% profit it exits the trade.

The bot will exit the trade if it has a loss of 25% after entering a trade.

Original timeframe is 1 hour.

The strategy starts working after 20 candles (so after 20 hours of waiting with the original timeframe.

ADX will be used to indicate trend strength & DI+ and DI- for trend direction

(Parabolic) SAR is used but it is not used in the buy and sell configurations of this strategy so I think the author has forgotten to remove this from the strategy file.

(Momentum) indicator is used

def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

dataframe.loc[

(

# DCD: When ADX is above 25 and MOM is above 0 and DI+ is bullish and DI+ > DI- then BUY

# SAR is not used here so I see.

(dataframe['adx'] > 25) &

(dataframe['mom'] > 0) &

(dataframe['plus_di'] > 25) &

(dataframe['plus_di'] > dataframe['minus_di'])

),

'buy'] = 1

return dataframe

def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

dataframe.loc[

(

# DCD: Sell signal is given when mom is below 0 and DI+ < DI-

# Other indicators have still buy parameter settings

(dataframe['adx'] > 25) &

(dataframe['mom'] < 0) &

(dataframe['minus_di'] > 25) &

(dataframe['plus_di'] < dataframe['minus_di'])

),

'sell'] = 1

return dataframe

If we read the buy and sell conditions we see that:

BUY SIGNAL

The buy signal occurs when

  • ADX is above 25 AND
  • MOM is above 0 AND
  • DI+ is above 25 AND
  • DI+ is bigger than DI-

Then a BUY signal is produced

As you can see, the SAR is not used.

SELL SIGNAL

The sell signal is given when

  • ADX is still above 25 AND
  • MOM gets below 0 AND
  • DI- gets above 25 AND
  • DI+ is under DI-

Initial backtest results

According to the backtest over all the available timeperiods I have the following results:

  • Best timeframe: 1 day
  • Total profit of strategy: -644 %
  • Drawdown of strategy: 907 %
  • Winrate 95 %
  • Risk of ruin of strategy:  n/a %

After the backtests I have the following somewhat surprising results.

Out of the box these results are horrible and considering this I think it’s better that we use this strategy more as an example to learn something than to actually use it for trading at the moment..

What strikes me first is that this strategy was intended for a 1 hour timeframe, but with the backtest period and the trading pairs I used it produced the worst results of all…

The winrate is quite good and I wish more of the earlier tested strategies could have such a win rate. But how come that this produces such bad results then?

To make sense of this I have taken the example of the 1 day backtest result to see what’s happening.

If you read the results of this backtest, then you see that the amount of winning trades is indeed very good and they produce a profit of 2125 dollars. However the low amount of losing trades due to a stop loss signal produces a loss of 2737 dollars.  And watching this I immediately must think of the strategy file where the takeprofit setting is only 1% but the stop loss is triggered at a loss of 25%. So for this strategy to be profitable, I think it should at least have 26 winners for each stoploss to get only 1% profit, if my calculations are correct.  So 26 times 170 is 4420 winning trades for every losing trade and this is not the case here. I fell short of 742 winning trades. And then I am not counting the losses of force sell and normal sell signals here…

So knowing this, can I tweak the parameters to get better results from this strategy.

For this I have created a specific strategy hyperopt file where I will be searching for the ideal adx and momentum period, but also the level where a buy or sell trigger must occur. See this code at the bottom of this post.

Also I will be searching for optimized parameters for stop loss and profit points. That is where the main problem exists.

So Let’s see what I can do with hyperparameter optimization…

Hyperparameter optimisation

freqtrade hyperopt -c  user_data/backtest-config.json  --epochs 1000 --spaces buy stoploss roi --hyperopt-loss SharpeHyperOptLossDaily --timeframe 1d -s ADXMomentumHopt

Conclusions

After the hyperparameter optimisation session and doing a backtest over the 1 day timeframe I got some staggering improvements.

This strategy turned from an initial loser to a good winner.

And if you watch the hyperopt results, then you can see where this improvement comes from.

Instead of taking small 1 percent profits, the ROI settings have changed to taking profits after 72 percent, 43 or 15 and at the end just leaving a trade if there is no profit at all.

Also the stop loss setting has increased to 34,5 %. Probably to let the strategy breathe more.

And ofcourse the indicator settings also have been changed to be more responsive to market circumstances.

Strategy League

The ADX Momentum enters the overall league in the 5th position.

The most noteworthy is the winrate, which is the highest of all strategies so far.

But I must admit that this is only the result of the hyperopt session which tweaked a lot of parameters. I think these results are mainly because of the tweaked the ROI settings.

And finally, the draws that this strategy has, are actual trades that did not get a result in the backtest. Nonetheless, in real trading these consume trading fees and wil take a bite of your account.

The strategy code

The complete code you can find with this link: https://github.com/freqtrade/freqtrade-strategies/blob/master/user_data/strategies/berlinguyinca/ADXMomentum.py

The hyperopt code

# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame
# Add your lib to import here
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import pandas_ta as pta
import numpy as np  # noqa
import pandas as pd  # noqa
# These libs are for hyperopt
from functools import reduce
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,IStrategy, IntParameter)
# --------------------------------
# freqtrade hyperopt -c  user_data/backtest-config.json  --epochs 1000 --spaces buy stoploss roi --hyperopt-loss SharpeHyperOptLossDaily --timeframe 1d -s ADXMomentumHopt
class ADXMomentumHopt(IStrategy):
"""
author@: Gert Wohlgemuth
Hyperopt code by: DutchCryptoDad (2022)
converted from:
https://github.com/sthewissen/Mynt/blob/master/src/Mynt.Core/Strategies/AdxMomentum.cs
"""
minimal_roi = {"0": 0.01}
stoploss = -0.25
timeframe = '1h'
startup_candle_count: int = 20
# Hyperopt spaces
adx_buy_hline = IntParameter(15, 35, default=25, space="buy")
adx_period = IntParameter(7, 21, default=14, space="buy")
plus_di_period = IntParameter(20,30 , default=25, space="buy")
minus_di_period = IntParameter(20, 30, default=25, space="buy")
mom_period = IntParameter(7, 21, default=14, space="buy")
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
for val in self.adx_period.range:
dataframe[f'adx_{val}'] = ta.ADX(dataframe, timeperiod=val)
for val in self.mom_period.range:
dataframe[f'mom_{val}'] = ta.MOM(dataframe, timeperiod=val)
for val in self.plus_di_period.range:
dataframe[f'plus_di_{val}'] = ta.PLUS_DI(dataframe, timeperiod=val)
for val in self.minus_di_period.range:
dataframe[f'minus_di_{val}'] = ta.MINUS_DI(dataframe, timeperiod=val)
return dataframe
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
conditions.append(
(
(dataframe[f'adx_{self.adx_period.value}'] > self.adx_buy_hline.value) &
(dataframe[f'mom_{self.mom_period.value}'] > 0) &
(dataframe[f'plus_di_{self.plus_di_period.value}'] > self.adx_buy_hline.value) &
(dataframe[f'plus_di_{self.plus_di_period.value}'] > dataframe[f'minus_di_{self.minus_di_period.value}'])
),
)
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
conditions.append(
(
(dataframe[f'adx_{self.adx_period.value}'] > self.adx_buy_hline.value) &
(dataframe[f'mom_{self.mom_period.value}'] < 0) &
(dataframe[f'minus_di_{self.minus_di_period.value}'] > self.adx_buy_hline.value) &
(dataframe[f'plus_di_{self.plus_di_period.value}'] < dataframe[f'minus_di_{self.minus_di_period.value}'])
),
)
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'sell'] = 1
return dataframe

The hyperopt json results

{
"strategy_name": "ADXMomentumHopt",
"params": {
"trailing": {
"trailing_stop": false,
"trailing_stop_positive": null,
"trailing_stop_positive_offset": 0.0,
"trailing_only_offset_is_reached": false
},
"buy": {
"adx_buy_hline": 23,
"adx_period": 11,
"minus_di_period": 21,
"mom_period": 21,
"plus_di_period": 27
},
"sell": {},
"protection": {},
"roi": {
"0": 0.726,
"11177": 0.43800000000000006,
"15959": 0.158,
"29387": 0
},
"stoploss": {
"stoploss": -0.345
}
},
"ft_stratparam_v": 1,
"export_time": "2022-02-06 10:29:42.327417+00:00"

Comments

No comments found for this post.