BollingerBounce
♡
Basics
mode: spot
outdated
Settings
trailing
startup candle count: 20
hyperopt
hyperopt params: 7
Indicators
EMA
MACD
MFI
RSI
SAR
SMA
Stochastic
talib
Concepts
trailing
Other
user_data
15 related strategies (⧉ identical code, ≈ similar name)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | # --- Do not remove these libs --- from freqtrade.strategy.interface 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 import numpy # noqa from freqtrade.strategy.hyper import CategoricalParameter, DecimalParameter, IntParameter from user_data.strategies import Config class BollingerBounce(IStrategy): """ Simple strategy based on Bollinger Band Bounce from bottom How to use it? > python3 ./freqtrade/main.py -s BollingerBounce """ # Hyperparameters # Buy hyperspace params: buy_params = { "buy_bb_gain": 0.04, "buy_fisher": -0.81, "buy_fisher_enabled": True, "buy_mfi": 13.0, "buy_mfi_enabled": False, } buy_mfi = DecimalParameter(10, 40, decimals=0, default=37.0, space="buy") buy_fisher = DecimalParameter(-1, 1, decimals=2, default=0.15, space="buy") # Bollinger Band 'gain' (% difference between current price and upper band). # Since we are looking for potential swings of >2%, we look for potential of more than that buy_bb_gain = DecimalParameter(0.01, 0.10, decimals=2, default=0.05, space="buy") # Categorical parameters that control whether a trend/check is used or not buy_mfi_enabled = CategoricalParameter([True, False], default=True, space="buy") buy_fisher_enabled = CategoricalParameter([True, False], default=True, space="buy") sell_fisher = DecimalParameter(-1, 1, decimals=2, default=-0.62, space="sell") sell_hold = CategoricalParameter([True, False], default=True, space="sell") # set the startup candles count to the longest average used (SMA, EMA etc) startup_candle_count = 20 # set common parameters minimal_roi = Config.minimal_roi trailing_stop = Config.trailing_stop trailing_stop_positive = Config.trailing_stop_positive trailing_stop_positive_offset = Config.trailing_stop_positive_offset trailing_only_offset_is_reached = Config.trailing_only_offset_is_reached stoploss = Config.stoploss timeframe = Config.timeframe process_only_new_candles = Config.process_only_new_candles use_sell_signal = Config.use_sell_signal sell_profit_only = Config.sell_profit_only ignore_roi_if_buy_signal = Config.ignore_roi_if_buy_signal order_types = Config.order_types def informative_pairs(self): """ Define additional, informative pair/interval combinations to be cached from the exchange. These pair/interval combinations are non-tradeable, unless they are part of the whitelist as well. For more information, please consult the documentation :return: List of tuples in the format (pair, interval) Sample: return [("ETH/USDT", "5m"), ("BTC/USDT", "15m"), ] """ return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Adds several different TA indicators to the given DataFrame Performance Note: For the best performance be frugal on the number of indicators you are using. Let uncomment only the indicator you are using in your strategies or your hyperopt configuration, otherwise you will waste your memory and CPU usage. """ # MFI dataframe['mfi'] = ta.MFI(dataframe) # SMA - Simple Moving Average dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) # MACD macd = ta.MACD(dataframe) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] # Stoch fast stoch_fast = ta.STOCHF(dataframe) dataframe['fastd'] = stoch_fast['fastd'] dataframe['fastk'] = stoch_fast['fastk'] # RSI dataframe['rsi'] = ta.RSI(dataframe) # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) rsi = 0.1 * (dataframe['rsi'] - 50) dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) # Bollinger bands #bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) bollinger = qtpylib.weighted_bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) # A little different than normal - adjust band values based on buy_bb_ratio #dataframe['bb_upperband'] = bollinger['mid'] + (bollinger['upper']-bollinger['mid'])*self.buy_bb_uratio.value #dataframe['bb_middleband'] = bollinger['mid'] #dataframe['bb_lowerband'] = bollinger['mid'] - (bollinger['mid']-bollinger['lower'])*self.buy_bb_lratio.value dataframe['bb_upperband'] = bollinger['upper'] dataframe['bb_middleband'] = bollinger['mid'] dataframe['bb_lowerband'] = bollinger['lower'] dataframe["bb_gain"] = ((dataframe["bb_upperband"] - dataframe["close"]) / dataframe["close"]) # EMA - Exponential Moving Average dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) # SAR Parabol dataframe['sar'] = ta.SAR(dataframe) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # GUARDS AND TRENDS if self.buy_mfi_enabled.value: conditions.append(dataframe['mfi'] <= self.buy_mfi.value) if self.buy_fisher_enabled.value: conditions.append(dataframe['fisher_rsi'] < self.buy_fisher.value) # TRIGGERS # potential gain > goal conditions.append(dataframe['bb_gain'] >= self.buy_bb_gain.value) # current candle is green conditions.append(dataframe['close'] > dataframe['open']) # candle crosses lower BB boundary conditions.append( (dataframe['open'] < dataframe['bb_lowerband']) & (dataframe['close'] >= dataframe['bb_lowerband']) ) # check that volume is not 0 conditions.append(dataframe['volume'] > 0) # build the dataframe using the conditions if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), 'buy'] = 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 """ if self.sell_hold.value: dataframe.loc[(dataframe['close'].notnull() ), 'sell'] = 0 return dataframe # Exit long position if price is above upper band or strong sell signal dataframe.loc[ ( ( (dataframe['open'] > dataframe['bb_upperband']) | (dataframe['close'] > dataframe['bb_upperband']) ) | ( (dataframe['fisher_rsi'] > self.sell_fisher.value) & (dataframe['sar'] > dataframe['close']) #(dataframe['mfi'] > 70) ) ), 'sell'] = 1 return dataframe |
Strategy League — fixed backtest that feeds the ranking
The fixed-params backtest (33 pairs · 20210101-20260101) — the only run that feeds the Strategy League ranking.
Backtests — over a market period
Backtest this strategy over a chosen crypto-cycle period. These don't affect the League ranking, and need that period's candle data downloaded.
Log in or sign up to run backtests.
| Period | Range | Total % | Win % | Max DD | Trades | |
|---|---|---|---|---|---|---|
| 2020 · DeFi Summer & Pre-Halving Rally | 20200101-20210101 | not run | ||||
| 2021 · Institutional Bull Market | 20210101-20220101 | not run | ||||
| 2022 · Post-Bull Crash & Macro Tightening | 20220101-20230101 | not run | ||||
| 2023–2024 · Recovery & ETF Anticipation | 20230101-20250101 | not run | ||||
| 2025–2026 · Current Cycle | 20250101-20260101 | not run | ||||
Walk forward
Out-of-sample backtest on recent data · 33 pairs · 20260101-20260701.
Backtest trust check
Static source analysis — instant, does not run the strategy. Flags future-data leaks, backtest-realism problems, and indicators worth a second look.
Lookahead analysis
freqtrade lookahead-analysis: detects strategies peeking at future candles.