Home Artists Posts Import Register

Downloads

Content

Intro

In this blog series I will be searching for the best trading strategy for cryptocurrency trading with a trading bot. These posts are complementary to the video’s I make on Youtube, which you can watch below.

The trading bot I use is the Freqtrade (https://www.freqtrade.io/en/latest/) trading bot which not only has good options for bot trading, but also is excellent in backtesting and optimization of trading parameters. Therefore I will use this program to not only trade automatically, but also look for the best strategies and setups.

All code that is specifically written for this strategy or for hyperparameter optimisation is available at the bottom of this post.

If you appreciate the effort I take to create this strategy or optimisation code, then please consider a tip for this post!

Strategy

In this post I will show you the results of the backtests I did on the “Bollinger bands in combination with RSI strategy”.

It is a pretty simple strategy that only has 64 lines of code  and it contains the following configurations:

  • The strategy will take profit at a minimum Roi of 10 %
  • The strategy will sell it’s position if it suffers a loss of 25%. So this is the stop loss.
  • Apparently this strategy was build for the one hour timeframe, so lets find out later if this is indeed the best timeframe to trade on.
  • Then there is the parameter configuration section where the Bollinger bands with a 20 simple moving average and a standard deviation of 2 is used.
  • Next the RSI over 14 periods is used.

Buy and sell signals

BUY signal

A said, this is a supersimple strategy. The bot will buy a pair when the rsi is below 30 and the closeprice of the pair is below the lower bollinger band.

SELL signal

The sell signal occurs when the RSI get’s above 70, which indicates that the asset could be overbought.

So in this case you could say that the strategy will buy a pair when the asset is heavily oversold, which is indicated by a low rsi and close price below the lower bollinger band. And will sell the pair when the rsi is above 70 which indicates that the pair is overbought.

Initial backtest results

After backtesting, the initial backtest results are as follows:

  • Best timeframe: 1 Day
  • Total profit of strategy: 35 %
  • Drawdown: 383 %
  • Winrate 75 %
  • Risk of ruin of strategy: 0.02 %

Hyperparameter optimisation

The original strategy did not have hyperoptable parameters to optimise it, besides the generic roi and stop loss optimisation.

Therefore I made some minor adjustments to the code to also optimise the roi buy and sell levels.

Also I wanted to investigate if adjusting the standard deviation number to increase or decrease the lower bollinger band would improve this strategy by giving better signals.

So what I did was add three optimisation spaces, one for the buy rsi line, one for the sell rsi line and one for the bollinger standard deviation calculation.

Next I added the RSI lines buy and sell levels to the buy and sell conditions of the strategy so that every optimisation run there will be a different value selected for testing.

Finally I created a for loop to make a new bollinger band with a different standard deviation calculation. This calculation directly creates the lower bollinger line in the dataframe and not the middle and upper line since we do not use these in the strategy.

Then this lower line will be used in the buy condition every time an optimisation run is performed.

Conclusions

I did two optimization runs. One where the adjusted standard deviation of the lower bollinger bands and the rsi buy level determined the buy signal. And where the rsi sell level determined the sell.

And in the end, because this gave me disappointing returns, I also tested an optimisation where only the rsi buy and sell level were optimised.

The first hyperopt session gave the advice to use a standard deviation factor of 4 for the lower bollinger band and a buy rsi level of 25. As you can see on this chart, this gives very peculiar signals to buy and these are also very rare. However they proved to have a 100% win rate. But nonetheless I did not find this setup very useful so I decided to do a second hyperopt session with only a search for the best rsi buy and sell levels.

This second session proved to be more realistic and also gave better results. So in the end it improved the strategy and looked to be a better setup.

Strategy League

This strategy does not prove to be an outperformer in comparison with the other ones. It only takes the 13 place in our league.

The only thing that stands out positively is the win rate of 76% the other performance items, not so much.

The advantage of a strategy like this is that you exactly know what is going on and what the strategy does. The disadvantage is that there is not much to tweak to increase the performance. Also the probability that a bullish divergence can cause an unnecessary sell because of a stop loss is available, but the stop loss percentage of 27% to the downside looks to be enough to prevent this.

Files

Are included in this post

The strategy code

https://github.com/freqtrade/freqtrade-strategies/tree/master/user_data/strategies/berlinguyinca

The hyperopt code

Note: This is the complete code of hyperopt, including Bollinger band deviation optimisation. This code is the most complete one but not as profitable so therefore use this only as an example!

# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
# --------------------------------
class BbandRsi(IStrategy):
"""
author@: Gert Wohlgemuth
converted from:
https://github.com/sthewissen/Mynt/blob/master/src/Mynt.Core/Strategies/BbandRsi.cs
"""
# 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 = {"0": 0.1}
# Optimal stoploss designed for the strategy
stoploss = -0.25
# Optimal timeframe for the strategy
timeframe = '1h'
# Hyperopt spaces
rsi_buy_hline = IntParameter(20, 40, default=30, space="buy")
rsi_sell_hline = IntParameter(60, 80, default=70, space="sell")
bb_stds = IntParameter(1, 4, default=2, space="buy")
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
# Bollinger bands
for std in self.bb_stds.range:
dataframe[f'bb_lowerband_{std}'] = (qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=std))['lower']
# bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=val)
# dataframe['bb_lowerband'] = bollinger['lower']
# dataframe['bb_middleband'] = bollinger['mid']
# dataframe['bb_upperband'] = bollinger['upper']
return dataframe
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
conditions.append(
(
(dataframe['rsi'] < self.rsi_buy_hline.value ) &
(dataframe['close'] < dataframe[f'bb_lowerband_{self.std.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['rsi'] > self.rsi_sell_hline.value ) &
),
)
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'sell'] = 1
return dataframe

The hyperopt json results

Note: This json result is from the first hyperopt run which was not as profitable. The second run, without the Bollinger standard deviation optimisation proved to be better. This code is just an example.

{
"strategy_name": "BbandRsiHopt",
"params": {
"trailing": {
"trailing_stop": false,
"trailing_stop_positive": null,
"trailing_stop_positive_offset": 0.0,
"trailing_only_offset_is_reached": false
},
"buy": {
"bb_stds": 4,
"rsi_buy_hline": 25
},
"sell": {
"rsi_sell_hline": 76
},
"protection": {},
"roi": {
"0": 0.723,
"6382": 0.099,
"19074": 0.059,
"50282": 0
},
"stoploss": {
"stoploss": -0.334
}
},
"ft_stratparam_v": 1,
"export_time": "2022-04-22 16:23:12.916587+00:00"
}

Comments

Mark Whittaker

Do you have code to identify divergences that is not susceptible to look ahead bias?