# pragma pylint: disable=missing-docstring, invalid-name, too-few-public-methods """ MTFIchimoku — Ichimoku MULTI-TIMEFRAME (technique S/R inter-unités). Idée (méthode de l'utilisateur) : trader sur une unité basse (15m) en utilisant les Tenkan/Kijun de l'unité SUPÉRIEURE (1h) comme supports/résistances. Règles : - Tendance 1h donnée par tenkan_1h vs kijun_1h. - LONG : en tendance 1h haussière, le prix 15m RECLAIME le support (croise au-dessus de la Kijun 1h) → rebond sur support. - SHORT : en tendance 1h baissière, le prix 15m CASSE le support (croise sous la Kijun 1h) → rejet sur résistance. - Sortie : perte du niveau (close repasse de l'autre côté de la Kijun 1h) + ROI/stop. La Kijun 1h sert de S/R principal (niveau lent/fort), la Tenkan 1h de filtre de tendance. """ from __future__ import annotations import talib.abstract as ta from pandas import DataFrame from freqtrade.strategy import ( IStrategy, informative, IntParameter, DecimalParameter, ) class MTFIchimoku(IStrategy): INTERFACE_VERSION = 3 timeframe = "15m" # unité de trading can_short = True # Paramètres optimisables buy_adx_min = IntParameter(15, 40, default=25, space="buy", optimize=True) buy_pullback_pct = DecimalParameter(0.004, 0.03, default=0.012, decimals=3, space="buy", optimize=True) buy_rsi_max = IntParameter(50, 72, default=60, space="buy", optimize=True) sell_rsi_min = IntParameter(28, 50, default=40, space="sell", optimize=True) minimal_roi = {"0": 0.02, "60": 0.012, "180": 0.006, "360": 0} stoploss = -0.025 trailing_stop = True trailing_stop_positive = 0.008 trailing_stop_positive_offset = 0.014 trailing_only_offset_is_reached = True startup_candle_count: int = 240 process_only_new_candles = True use_exit_signal = True def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs) -> float: return 1.0 @informative("1h") def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Tenkan / Kijun sur l'unité supérieure (1h) → deviendront tenkan_1h / kijun_1h. high, low = dataframe["high"], dataframe["low"] dataframe["tenkan"] = (high.rolling(9).max() + low.rolling(9).min()) / 2 dataframe["kijun"] = (high.rolling(26).max() + low.rolling(26).min()) / 2 return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # tenkan_1h / kijun_1h sont injectés automatiquement (forward-fill sur le 15m). dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14) dataframe["adx"] = ta.ADX(dataframe, timeperiod=14) # Plus haut/bas récents (pour exiger un VRAI pullback vers le niveau) dataframe["hh8"] = dataframe["high"].rolling(8).max() dataframe["ll8"] = dataframe["low"].rolling(8).min() return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: kijun = dataframe["kijun_1h"] tenkan = dataframe["tenkan_1h"] close, op, low, high = ( dataframe["close"], dataframe["open"], dataframe["low"], dataframe["high"] ) rsi = dataframe["rsi"] adx = dataframe["adx"] adx_min = self.buy_adx_min.value pb = self.buy_pullback_pct.value uptrend_1h = (tenkan > kijun) & (close > kijun) # biais haussier 1h downtrend_1h = (tenkan < kijun) & (close < kijun) # biais baissier 1h # LONG : en tendance forte, VRAI pullback vers le support Kijun 1h puis rebond + momentum. dataframe.loc[ ( uptrend_1h & (adx > adx_min) # tendance forte & (dataframe["hh8"] > kijun * (1 + pb)) # le prix venait nettement au-dessus & (low <= kijun * 1.001) # mèche teste le support & (close > kijun) # clôture au-dessus (support tient) & (close > op) # bougie de rebond & (rsi > rsi.shift(1)) # momentum qui se retourne à la hausse & (rsi < self.buy_rsi_max.value) # pas déjà suracheté & (dataframe["volume"] > 0) ), "enter_long", ] = 1 # SHORT : miroir exact (rejet sur résistance en tendance baissière forte). dataframe.loc[ ( downtrend_1h & (adx > adx_min) & (dataframe["ll8"] < kijun * (1 - pb)) & (high >= kijun * 0.999) & (close < kijun) & (close < op) & (rsi < rsi.shift(1)) & (rsi > self.sell_rsi_min.value) & (dataframe["volume"] > 0) ), "enter_short", ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: kijun = dataframe["kijun_1h"] tenkan = dataframe["tenkan_1h"] close = dataframe["close"] # Sortie LONG : cassure NETTE du support (clôture sous la Kijun ET sous la Tenkan 1h) dataframe.loc[ ((close < kijun) & (close < tenkan) & (dataframe["volume"] > 0)), "exit_long", ] = 1 # Sortie SHORT : reprise NETTE au-dessus de la résistance dataframe.loc[ ((close > kijun) & (close > tenkan) & (dataframe["volume"] > 0)), "exit_short", ] = 1 return dataframe