Home Artists Posts Import Register

Content

Introduction

Hello everybody and welcome to another post where I will try to find the best algorithmic trading strategy.

Not so long ago I tested the BinHV45 code from the Freqtrade strategies github repository and in the end it proved to be not such a good strategy. Even after optimizing.

But then the CombinedBinhandCluc strategy was tested. A combination of that Binhv45 and Clucmay strategy. That one performed quite well.

In the repository of the Freqtrade bot there is even another also that is called BinHV27. Let’s investigate that code and see if this code is its BinHV45 little brother and how that version of BinHV performs.

Maybe we are yet in for another surprise…

And as always, all kudo’s go to the authors and collectors that provide us with this code.

The strategy

To start of from the beginning. The usual modules and libraries are set up so that they can be used further down in the code:

from freqtrade.strategy import IStrategy
from typing import Dict, List
from functools import reduce
from pandas import DataFrame
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from typing import Dict, List
from functools import reduce
from pandas import DataFrame, DatetimeIndex, merge
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy  # noqa

Then the typical parameters for roi, stop loss and the preferred timeframe for this strategy are set in the next section. and also a short sponsoring notice for user BinH from Slack.

The roi will take profit at 100 percent gains, but the stop loss is set to a huge 50% loss.

The timeframe is set to 5 minute here.

Let’s hope that stop loss get’s not hit that many times here…

"""
strategy sponsored by user BinH from slack
"""
INTERFACE_VERSION: int = 3
minimal_roi = {
"0": 1
}
stoploss = -0.50
timeframe = '5m'

Then a huge block of code appears. Just for the indicators method.

I see an overwhelming amount of indicators here like the rsi, adx, moving averages, trend signallers, delta’s and whatsmore.

A nice set of indicators to add to your own library for whenever you think you want to use one. But using them all at the same time is a little bit overkill I think. But ofcourse I can be wrong and this is the greatest algorithm that ever existed.

def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['rsi'] = numpy.nan_to_num(ta.RSI(dataframe, timeperiod=5))
rsiframe = DataFrame(dataframe['rsi']).rename(columns={'rsi': 'close'})
dataframe['emarsi'] = numpy.nan_to_num(ta.EMA(rsiframe, timeperiod=5))
dataframe['adx'] = numpy.nan_to_num(ta.ADX(dataframe))
dataframe['minusdi'] = numpy.nan_to_num(ta.MINUS_DI(dataframe))
minusdiframe = DataFrame(dataframe['minusdi']).rename(columns={'minusdi': 'close'})
dataframe['minusdiema'] = numpy.nan_to_num(ta.EMA(minusdiframe, timeperiod=25))
dataframe['plusdi'] = numpy.nan_to_num(ta.PLUS_DI(dataframe))
plusdiframe = DataFrame(dataframe['plusdi']).rename(columns={'plusdi': 'close'})
dataframe['plusdiema'] = numpy.nan_to_num(ta.EMA(plusdiframe, timeperiod=5))
dataframe['lowsma'] = numpy.nan_to_num(ta.EMA(dataframe, timeperiod=60))
dataframe['highsma'] = numpy.nan_to_num(ta.EMA(dataframe, timeperiod=120))
dataframe['fastsma'] = numpy.nan_to_num(ta.SMA(dataframe, timeperiod=120))
dataframe['slowsma'] = numpy.nan_to_num(ta.SMA(dataframe, timeperiod=240))
dataframe['bigup'] = dataframe['fastsma'].gt(dataframe['slowsma']) & ((dataframe['fastsma'] - dataframe['slowsma']) > dataframe['close'] / 300)
dataframe['bigdown'] = ~dataframe['bigup']
dataframe['trend'] = dataframe['fastsma'] - dataframe['slowsma']
dataframe['preparechangetrend'] = dataframe['trend'].gt(dataframe['trend'].shift())
dataframe['preparechangetrendconfirm'] = dataframe['preparechangetrend'] & dataframe['trend'].shift().gt(dataframe['trend'].shift(2))
dataframe['continueup'] = dataframe['slowsma'].gt(dataframe['slowsma'].shift()) & dataframe['slowsma'].shift().gt(dataframe['slowsma'].shift(2))
dataframe['delta'] = dataframe['fastsma'] - dataframe['fastsma'].shift()
dataframe['slowingdown'] = dataframe['delta'].lt(dataframe['delta'].shift())
return dataframe

Then the buy signals method appears and it is also quite a large piece of code.

Apparently the author of the code has determined that there are multiple circumstances a signal could be generated and that for each individual circumstance a buy signal can be created.

To begin with the beginning. In any case the starting block of conditions has to be fulfilled. It roughly states that if the slowsma is greater then 0 and if the close price is lower than the highsma and lowsma. And also if the ADX minus di greater than its ema and also if the rsi is greater or equal then the previous rsi value.

Then, if any of these blocks below are valid, because of the OR statement, the bot will do its job and buys into a position.

def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
dataframe['slowsma'].gt(0) &
dataframe['close'].lt(dataframe['highsma']) &
dataframe['close'].lt(dataframe['lowsma']) &
dataframe['minusdi'].gt(dataframe['minusdiema']) &
dataframe['rsi'].ge(dataframe['rsi'].shift()) &
(
(
~dataframe['preparechangetrend'] &
~dataframe['continueup'] &
dataframe['adx'].gt(25) &
dataframe['bigdown'] &
dataframe['emarsi'].le(20)
) |
(
~dataframe['preparechangetrend'] &
dataframe['continueup'] &
dataframe['adx'].gt(30) &
dataframe['bigdown'] &
dataframe['emarsi'].le(20)
) |
(
~dataframe['continueup'] &
dataframe['adx'].gt(35) &
dataframe['bigup'] &
dataframe['emarsi'].le(20)
) |
(
dataframe['continueup'] &
dataframe['adx'].gt(30) &
dataframe['bigup'] &
dataframe['emarsi'].le(25)
)
),
'enter_long'] = 1
return dataframe

There is a similar way that creates sell signals here. With the exception of a generic baseline signal that has to be met first.

The author just has defined multiple conditions that can appear that can cause a trade to exit.

If one of these blocks of conditions are met, then the position gets closed.

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

dataframe.loc[
(
(
~dataframe['preparechangetrendconfirm'] &
~dataframe['continueup'] &
(dataframe['close'].gt(dataframe['lowsma']) | dataframe['close'].gt(dataframe['highsma'])) &
dataframe['highsma'].gt(0) &
dataframe['bigdown']
) |
(
~dataframe['preparechangetrendconfirm'] &
~dataframe['continueup'] &
dataframe['close'].gt(dataframe['highsma']) &
dataframe['highsma'].gt(0) &
(dataframe['emarsi'].ge(75) | dataframe['close'].gt(dataframe['slowsma'])) &
dataframe['bigdown']
) |
(
~dataframe['preparechangetrendconfirm'] &
dataframe['close'].gt(dataframe['highsma']) &
dataframe['highsma'].gt(0) &
dataframe['adx'].gt(30) &
dataframe['emarsi'].ge(80) &
dataframe['bigup']
) |
(
dataframe['preparechangetrendconfirm'] &
~dataframe['continueup'] &
dataframe['slowingdown'] &
dataframe['emarsi'].ge(75) &
dataframe['slowsma'].gt(0)
) |
(
dataframe['preparechangetrendconfirm'] &
dataframe['minusdi'].lt(dataframe['plusdi']) &
dataframe['close'].gt(dataframe['lowsma']) &
dataframe['slowsma'].gt(0)
)
),
'exit_long'] = 1
return dataframe

At the moment it is a little bit unclear to me if all these different buy and sell conditions really add value to this strategy. It is known that algorithmic strategies can get very complex and I have seen some that makes this code look bleak.

Anyway, let me test this initially and see which potential profits it could brings us.

Initial backtest results

The initial backtest results for this strategy are quite different when you compare these timeframes.

The best scoring timeframe according to my pairs and setup is the 5 minute timeframe. But if you take a more close look at the individual scores you see that it also scores poorly on some of these factors. Like for example the drawdown on the 5 minute.

The second best scoring timeframe is the 1 day. And there the drawdown is better, but now the amount of pairs that react on this strategy goes down.

And if you move further down the scoring table at some point there is even no hypothetical profit. So no use to use this algo.

I can take a closer look at the enter and exit reasons to get a small clue on whats going on with these signals.

As you see the multitude of exit signals indeed score the higherst in comparison to the other exit types the bot has. The most remarkable thing here is that this algo strategy only has 2 exit signals based on it’s stop loss. I don’t think I have ever seen that before in any other test.

So the exit signals configured here are prety tight in comparison the configured minus 50% stoploss setting. Nonetheless, there were still 2 trades that suffered a 50% loss here though.

Now because there are so many intentional exit signals configured in this algo strategy I find it difficult to still optimize the ROI and stoploss. I think that the author clearly had in mind to not use these types of exit signals.

Also optimizing all those configured parameters also looks like an impossible task. There can be so many variations that finding the best one is certainly a matter of overfitting to past performance.

So I could leave it like this and show you the strategy league as it is now. But I am still curious to see if adjusting ROI and stoploss settings would give better exit signals than the intended signals the author configured.

Hyperparameter optimizing

Ai ai ai,

I am starting to wonder if optimizing 5 minute strategies is the way to go at the moment. It could be that the authors of the code just did a wonderfull job right away. Or that I just don’t use the right parameters to optimize this strategy correctly. Maybe there is some other mistake I made.

Or it’s just so that these many programmed buy and sell conditions just are in the way of fully making use of also the ROI and stop loss setting. It is not by accident that the author did not fully make use of them in the first place. And maybe this is just the confirmation that the author did a great thing by coding the algo the way it is at the moment.

Initially one might think that having an optimized ROI will automatically improve a Freqtrade algo strategy. And this is also the case with the adjusted stop loss settings. Which you will find on my Patreon page by the way.

What if these additional ROI and exit signals get in the way of the actual profitable exit signals, configured in the original strategy. And these original exit are now mostly exiting at the wrong moments because of this. In other words with trades that are under water at the moment but still get the exit signal from the original configured strategies code.

In these cases it is just better to leave the strategy as it is. Maybe try to optimize the configured indicator settings manually and test how it improves or not.

But just not more than that.

Plots

5 minute charts, together with the time range my backtests for this timeframe, are just too much too handle (as I have said before). I will leave these files in my Patreon addendum post for those that could afford a computer with enough resources to buy a PC that can handle these files…

The strategy league

So with the original strategies settings, this algo finishes higher than it’s earlier tested, and almost similar named BinHV45 algo.

A 61 percent winrate is admirable but something has to be done to that large drawdown. If this would improve, this code would end up a bit higher on the league in comparison with its similar scoring neighbors.

The most interesting part of this algorithm is the use of multiple entry signal and exit signal triggers. It could be a great source of inspiration for those who have similar thoughts of using this technique. You are always welcome to be inspired by this code.

If you found out how this strategy could be improved and by how much, I invite you to share this with the community, the original author and the Freqtrade developers.

For me, this is enough because I have yet another algorithmic trading strategy that I am curious about how it performance would be.

See you the next post!

Files

As always, the output from these tests and hyperopts will be available as addendums on my Patreon page for my supporters. The exact link to the files are here: 

https://www.patreon.com/posts/81217100

Comments

Wojtek Chruścicki

Could you test any strategy that uses fvg and bos/choch?

Dutch Algotrading

What a true coincidence. Im currently studying these. However as far as I know there are no libraries (or limited) that support these. Maybe Illl incorporate these into my personal library. Also a tip for you if you are interested. This is a very underrated channel: https://www.youtube.com/watch?v=qj3nl5cJuMw

Wojtek Chruścicki

Another thing that I'm curious about are strategies which use previous extremes of ohlc or macd etc - not only shift(number_of_candles) method. HH, HL, LH and LL. I don't want to only depend on rolling means or higher periods.

Dutch Algotrading

Do you have any examples of actual existing strategies with these requirements? Sometimes another example makes things clear.

Wojtek Chruścicki

Actually I haven't seen implemented strats like that. Only neurotrader888 seems to be doing it, but just for price action https://github.com/neurotrader888/TechnicalAnalysisAutomation I think of using his directional change to look for indicators extremes and SMC for fvg (https://github.com/joshyattridge/smart-money-concepts it uses shift(-1) so be careful)