# pragma pylint: disable=missing-docstring, invalid-name, too-few-public-methods """ BBMeanRev — 2e moteur : mean-reversion (Bollinger + RSI), long/short futures. Logique CONTRAIRE au trend-follower (Ichimoku) → décorrélée par construction : LONG quand le prix casse SOUS la bande basse + RSI survendu (rebond attendu). SHORT quand le prix casse AU-DESSUS de la bande haute + RSI suracheté (repli attendu). Sortie : retour à la moyenne (bande médiane) + ROI/stop. But : gagner dans les marchés en range (là où l'Ichimoku perd), pour qu'en combinaison le ratio rendement/risque monte. Paramétrable pour hyperopt. """ from __future__ import annotations import talib.abstract as ta from pandas import DataFrame from freqtrade.strategy import IStrategy, IntParameter class BBMeanRev(IStrategy): INTERFACE_VERSION = 3 timeframe = "1h" can_short = True # Mean-reversion = sorties rapides ; valeurs par défaut, surchargées par hyperopt. minimal_roi = {"0": 0.03, "60": 0.02, "180": 0.01, "360": 0} stoploss = -0.05 trailing_stop = False startup_candle_count: int = 50 process_only_new_candles = True use_exit_signal = True buy_rsi = IntParameter(10, 40, default=30, space="buy", optimize=True) sell_rsi = IntParameter(60, 90, default=70, space="sell", optimize=True) def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs) -> float: return 1.0 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: bb = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0) dataframe["bb_lower"] = bb["lowerband"] dataframe["bb_mid"] = bb["middleband"] dataframe["bb_upper"] = bb["upperband"] dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # LONG : survente (RSI bas) dans la zone basse des bandes dataframe.loc[ ( (dataframe["rsi"] < self.buy_rsi.value) & (dataframe["close"] < dataframe["bb_mid"]) & (dataframe["volume"] > 0) ), "enter_long", ] = 1 # SHORT : surchauffe (RSI haut) dans la zone haute des bandes dataframe.loc[ ( (dataframe["rsi"] > self.sell_rsi.value) & (dataframe["close"] > dataframe["bb_mid"]) & (dataframe["volume"] > 0) ), "enter_short", ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Sortie LONG : RSI revenu vers la moyenne (réversion réalisée) dataframe.loc[ ((dataframe["rsi"] > 50) & (dataframe["volume"] > 0)), "exit_long", ] = 1 # Sortie SHORT : RSI revenu vers la moyenne dataframe.loc[ ((dataframe["rsi"] < 50) & (dataframe["volume"] > 0)), "exit_short", ] = 1 return dataframe