The “Low Bollinger band” algo trading strategy (Patreon)
Content
Introduction
Hello once again and thanks for visiting my site (youtube channel). It is much appreciated.
In this post I am going to test the Low Bollinger Band trading strategy that you can find on the Freqtrade github repository with algotrading strategy files. So let’s find out how if this algo will make us hypothetically very rich.
The ‘Low Bollinger Band’ is a pretty simple strategy that has not a lot of complicated rules or indicators. It only uses the lower Bollinger band for a buy signal. But before I tell too much right away, let’s first give credits where credits are due.
According tot the file the author is called Thorsten. I don’t know if this is yout first or your last name, but Thorsten, thanks for your code!
The strategy
And as said, this strategy is pretty simple to follow and explain. So let’s not waste anymore time and dive right into the code to see how it works. You can open the code by visiting the Freqtrade site and follow the links to the strategy repository. Or you can just follow this link you see here: freqtrade/freqtrade-strategies: Free trading strategies for Freqtrade bot (github.com)
First of all the necessary modules and libraries are loaded.
# --- Do not remove these libs ---
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 as np # noqa
Then the mandatory Freqtrade parameters are loaded.
The roi has three points in time defined where take profits are determined. The highest ROI is set to 90 percent profits. Which is a pretty bold target point. Otherwise 50 percent after 15 minutes or 5 and 4 percent profit points after respectively 1 and 10 minutes.
The stop loss is set to -1.5 percent loss. If that is reached, the bot sells the position again.
This strategy has a super tight stop loss and a very ambitious roi setting to say the least.
Also the 1 minute timeframe is set for this strategy. But the best I can do is 5 minutes. So hopefully I can see a little bit of this strategies potential at a timeframe that is 5 times larger.
class Low_BB(IStrategy):
"""
author@: Thorsten
works on new objectify branch!
idea:
buy after crossing .98 * lower_bb and sell if trailing stop loss is hit
"""
INTERFACE_VERSION: int = 3
# Minimal ROI designed for the strategy.
# This attribute will be overridden if the config file contains "minimal_roi"
minimal_roi = {
"0": 0.9,
"1": 0.05,
"10": 0.04,
"15": 0.5
}
# Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss"
stoploss = -0.015
# Optimal timeframe for the strategy
timeframe = '1m'
The parameters section is a little bit messy. The Bollinger bands are defined two times. And I see no differences between these two configurations.
Also the MACD is configured but it will not be used anywhere in the buy or sell triggers.
You will also see some remnants of other indicators but they are all commented out.
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
##################################################################################
# buy and sell indicators
bollinger = qtpylib.bollinger_bands(
qtpylib.typical_price(dataframe), window=20, stds=2
)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_middleband'] = bollinger['mid']
dataframe['bb_upperband'] = bollinger['upper']
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
# dataframe['cci'] = ta.CCI(dataframe)
# dataframe['mfi'] = ta.MFI(dataframe)
# dataframe['rsi'] = ta.RSI(dataframe, timeperiod=7)
# dataframe['canbuy'] = np.NaN
# dataframe['canbuy2'] = np.NaN
# dataframe.loc[dataframe.close.rolling(49).min() <= 1.1 * dataframe.close, 'canbuy'] == 1
# dataframe.loc[dataframe.close.rolling(600).max() < 1.2 * dataframe.close, 'canbuy'] = 1
# dataframe.loc[dataframe.close.rolling(600).max() * 0.8 > dataframe.close, 'canbuy2'] = 1
##################################################################################
# required for graphing
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper']
dataframe['bb_middleband'] = bollinger['mid']
return dataframe
The buy trigger is given when the close price of the pair is lower than 0.98 times the bollinger lowerband. Which means that the close price does not have to be completely below the lower band. But it is allowed to close 0.98 * the lower band.
So if the lower band has a value of 27920 USDT. There will be a buy signal if the close price is lower then 0.98 * 27920.0 = 27361.60 USDT.
If the price gets any lower, then the stop loss gets quickly hit because of its tights settings.
There is no other sell signal. The ROI and stop loss take care of the sell signals for the positions.
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(
(dataframe['close'] <= 0.98 * dataframe['bb_lowerband'])
)
,
'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(),
'exit_long'] = 1
return dataframe
For those people that start to think that they saw this strategy earlier, then you are correct. The Clucmay strategy that I discussed earlier has almost the same setting for its Bollinger band buy signal.
The only difference here is that there were two other indicators that also were part of the complete buy signal here.
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(
(dataframe['close'] < dataframe['ema100']) &
(dataframe['close'] < 0.985 * dataframe['bb_lowerband']) &
(dataframe['volume'] < (dataframe['volume'].rolling(window=30).mean().shift(1) *
20))
),
'buy'] = 1
return dataframe
Based on this, you can state that the same strategy like the ClucMay will be tested once again. But since the two other indicators are not part of the buy or sell triggers, I will still consider this another algorithm.
So therefore, let me test this strategy to see if only using the lower bollinger band as an indicator for buying will be enough. Hopefully it will provide us with successful trade signals and make us some hypothetical profits…
Initial backtest results
After the initial backtest, it seems that this strategy has a higher score on the 15 minute timeframe. But Surprisingly enough I have not seen an algorithm that performs good and still manages to have over 95% of losing trades. This is definitely a pure example of cutting your losses and let your winners run.
The ultra tight stop loss of minus 1.5 percent makes this algo cut it’s losses so quick that, of the 3239 trades, it exits with a loss on 3080 occasions.
The ROI still manages to make good money on 151 trades though. And with a drawdown of almost 37 percent on this timeframe it is actually not such of a bad performer. I have seen other algorithms that score lower with very high winrates.
This time it clearly shows that a high win rate does not say anything. It is here a matter of catching the right trades and quickly abandon those that prove to not give any direct results.
Looking at the plot of the initial backtest result, you can clearly see that the profit curve at some point has a nice upward slope. Especially after the first and highest drawdown period it picks up momentum and makes the highest profits. Which is logical because a lot of the coins that I use for this backtest were starting at the time of this period. They picked up momentum in the last bull cycle.
The highest drawdown moment happens around the peak of the last bull market and it reasonably kept its gains over there.
Initial 15 minute backtest plot
I am now curious to see what happens if I optimize the ROI and stop loss parameters. Will it still be as neat as this or will the performance become worse after that.
Let’s find out.
Hyperparameter optimizing
So, after optimizing the roi and stop loss parameters I got totally different roi and stop loss settings.
By the way, you can find this in the complete output package I leave behind for my patreons by the way.
And yes, lucky me, the 15 minute timeframe is manageable by my pc, so fairly quick calculations. Now let’s hope that these newer settings makes the strategy perform better.
In a certain way, the Lower Bollinger band strategy does manage to improve some of its ratio’s with the newer roi and stop loss settings.
However, what it improves on the win rate, drawdown, and Sharpe ratio, it mainly loses in profitability and the hypothetical end amount of money you could make in the past.
Looking at the buy and sell events of the backtest, we see that the stop loss and roi exit signals have a rough 3 to 1 ratio. Which means that for every win there are a little bit more of 2 losses.
Also every winning trade has an average profit of slightly less than 6 %, while the stop loss loses around 2 percent. In the original strategies settings these were quite different. Every win was around 50% and every loss around 1.5 percent. So a big difference here.
The plot of the optimized settings show a totally different picture here.
The profit plot shows a nice and steady upward line and only has the largest drawdown in the late period of 2022. Overall these lines are much smoother in comparison with the original line.
Also the drawdown chart, that was around 18 percent at its largest, shows that this strategy is negatively influences during the bearish periods. But my observation is that the drawdown relatively quickly gets negated and then starting to stay around the 0% line (which is always a nice feeling by the way).
Finally I notice that the parallelism graph has less black than in the initial graph. According to the results the amount of pairs that reacts positive on this algorithm is also 60%, just like the initial settings. But it seems that the bot has less "active" open trades.
Optimized 15 min backtest plot
Now because both results have a score of 195 percent, I have to make a choice on which version should enter the strategy league. Again I have to be the deciding factor that determines the final outcome.
Again it comes down to the psychology of a trading bot’s owner. Do I want to see my bot have losing trades day after day, knowing that the big winner will come someday (or perhaps even never). Or do I choose for a moderate incline of my trading balance and have a higher chance that I see a winning trade now and then.
Also, there will be losing trades, but in the optimized settings the drawdown is around half of the original version. But do not let yourself fooled by this though because the Calmar indicates that the original settings have a better ratio. But the drawdown chart in the initial settings has too much green. Which states that if your account suffers drawdown, it is more intense and also takes a lot longer to get into a profitable position again. Do I take more risk and have higher gains and also test my nerves when the market circumstances stay bad or do I choose for lower gains and also ease of mind…
The strategy league
After some thought I have chosen for ease of mind and will not be greedy. So therefore I have chosen for the settings that hyperoptimizaton gave me. Now for the strategy league it does not matter because with it’s score this algo strategy will end up at the same spot anyway.
Comparing it with some of its close neighbors it scores a bit bleak. But then again, there are also other algo’s that have an even lower profit percentage, but nonetheless score superhigh on the winrate.
So you see, that is why you should look at the big picture. Youtube movies that promote strategies with 100% winrate do not guarantee life changing wealth. And some strategies that have lower winrates just score awesome. There is no single truth and only a Sith deals in absolutes.
Just be inspired by the way this algorithm was build, how this could lead to these hypothetical results and see how you can apply this knowledge on your own crypto trading strategies.
And with this small nugget of knowledge I want to end this post.
I hope you enjoyed it and if you do, please like, subscribe and leave a comment.
My Patreons will get access to all these files and optimization results.
And in the mean time I will see you in the next video.
Goodbye!
Files
As always, the output from these tests and hyperopts will be available as addendums on my Patreon page for my supporters. In this case the files are located in this post:
https://www.patreon.com/posts/81705569
Thanks for visiting and see you the next time!