Combining Bollinger Band Reversals for Long Entries and Exits
Summary
This five-minute crypto strategy combines entry conditions drawn from two named Bollinger Band approaches. One setup looks for a sharp downward move below a 40-period lower band, with price change, candle tail, and closing-price conditions used to qualify the move. The other buys when price is below a 50-period exponential average and substantially below the lower band built from typical price, while filtering out unusually high volume.
The strategy sets a fixed profit target and stop loss, enables exit signals, and exits long positions when price rises above the middle Bollinger Band. The source offers backtesting-based opinions about limiting simultaneous open trades and adjusting stake size, but gives no performance figures, test period, market universe, or methodology. Its observations about results in a broadly rising market are not independently substantiated. The rules are implementation details rather than evidence of a durable edge; costs, slippage, parameter sensitivity, and behavior across other regimes are not discussed.
Key ideas
- The strategy combines two Bollinger Band based long-entry setups on a five-minute timeframe.
- The first setup qualifies a sharp price drop below a 40-period lower band using candle and price-change conditions.
- The second setup requires price to be below a slow exponential average and well beneath a 20-period lower band, with a volume filter.
- Long positions can exit above the Bollinger middle band, with a fixed profit target and stop loss also specified.
- The source makes backtesting claims about trade count and rising markets but supplies no supporting results or test details.
Tags
Full text
# CombinedBinHAndCluc.py
```py
# --- Do not remove these libs ---
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np
# --------------------------------
import talib.abstract as ta
from freqtrade.strategy import IStrategy
from pandas import DataFrame
def bollinger_bands(stock_price, window_size, num_of_std):
rolling_mean = stock_price.rolling(window=window_size).mean()
rolling_std = stock_price.rolling(window=window_size).std()
lower_band = rolling_mean - (rolling_std * num_of_std)
return np.nan_to_num(rolling_mean), np.nan_to_num(lower_band)
class CombinedBinHAndCluc(IStrategy):
# Based on a backtesting:
# - the best perfomance is reached with "max_open_trades" = 2 (in average for any market),
# so it is better to increase "stake_amount" value rather then "max_open_trades" to get more profit
# - if the market is constantly green(like in JAN 2018) the best performance is reached with
# "max_open_trades" = 2 and minimal_roi = 0.01
INTERFACE_VERSION: int = 3
minimal_roi = {
"0": 0.05
}
stoploss = -0.05
timeframe = '5m'
use_exit_signal = True
exit_profit_only = True
ignore_roi_if_entry_signal = False
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# strategy BinHV45
mid, lower = bollinger_bands(dataframe['close'], window_size=40, num_of_std=2)
dataframe['lower'] = lower
dataframe['bbdelta'] = (mid - dataframe['lower']).abs()
dataframe['closedelta'] = (dataframe['close'] - dataframe['close'].shift()).abs()
dataframe['tail'] = (dataframe['close'] - dataframe['low']).abs()
# strategy ClucMay72018
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_middleband'] = bollinger['mid']
dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=50)
dataframe['volume_mean_slow'] = dataframe['volume'].rolling(window=30).mean()
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
( # strategy BinHV45
dataframe['lower'].shift().gt(0) &
dataframe['bbdelta'].gt(dataframe['close'] * 0.008) &
dataframe['closedelta'].gt(dataframe['close'] * 0.0175) &
dataframe['tail'].lt(dataframe['bbdelta'] * 0.25) &
dataframe['close'].lt(dataframe['lower'].shift()) &
dataframe['close'].le(dataframe['close'].shift())
) |
( # strategy ClucMay72018
(dataframe['close'] < dataframe['ema_slow']) &
(dataframe['close'] < 0.985 * dataframe['bb_lowerband']) &
(dataframe['volume'] < (dataframe['volume_mean_slow'].shift(1) * 20))
),
'enter_long'
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
"""
dataframe.loc[
(dataframe['close'] > dataframe['bb_middleband']),
'exit_long'
] = 1
return dataframe
```Shown in full with attribution under the source's licence. Licence: GPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.