# pragma pylint: disable=missing-docstring, invalid-name, too-few-public-methods """ IchimokuLS — Ichimoku long/short avec FILTRE DE TENDANCE MACRO (EMA200). Reprend les paramètres optimisés d'IchimokuHyper (figés), et ajoute : - LONG uniquement si close > EMA200 (tendance de fond haussière) - SHORT uniquement si close < EMA200 (tendance de fond baissière) But : réparer le côté long, qui perdait en entrant à contre-tendance macro. On compare A/B contre IchimokuHyper (mêmes params, sans le filtre). """ from __future__ import annotations import talib.abstract as ta from pandas import DataFrame from freqtrade.strategy import IStrategy class IchimokuLS(IStrategy): INTERFACE_VERSION = 3 timeframe = "1h" can_short = True # --- Paramètres figés (issus de l'hyperopt d'IchimokuHyper) --- minimal_roi = {"0": 0.488, "213": 0.136, "639": 0.05, "2021": 0} stoploss = -0.232 trailing_stop = True trailing_stop_positive = 0.341 trailing_stop_positive_offset = 0.441 trailing_only_offset_is_reached = False buy_adx_min = 36 buy_cloud_min_pct = 0.56 startup_candle_count: int = 220 # EMA200 + marge 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 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: high, low, close = dataframe["high"], dataframe["low"], dataframe["close"] tenkan = (high.rolling(9).max() + low.rolling(9).min()) / 2 kijun = (high.rolling(26).max() + low.rolling(26).min()) / 2 dataframe["tenkan"] = tenkan dataframe["kijun"] = kijun dataframe["senkou_a"] = ((tenkan + kijun) / 2).shift(26) dataframe["senkou_b"] = ( (high.rolling(52).max() + low.rolling(52).min()) / 2 ).shift(26) dataframe["cloud_top"] = dataframe[["senkou_a", "senkou_b"]].max(axis=1) dataframe["cloud_bot"] = dataframe[["senkou_a", "senkou_b"]].min(axis=1) dataframe["cloud_width_pct"] = ( (dataframe["cloud_top"] - dataframe["cloud_bot"]) / close * 100 ) dataframe["close_prev26"] = close.shift(26) dataframe["adx"] = ta.ADX(dataframe, timeperiod=14) dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200) # filtre macro return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (dataframe["close"] > dataframe["cloud_top"]) & (dataframe["tenkan"] > dataframe["kijun"]) & (dataframe["close"] > dataframe["close_prev26"]) & (dataframe["adx"] > self.buy_adx_min) & (dataframe["cloud_width_pct"] > self.buy_cloud_min_pct) & (dataframe["close"] > dataframe["ema200"]) # ← filtre macro LONG & (dataframe["volume"] > 0) ), "enter_long", ] = 1 dataframe.loc[ ( (dataframe["close"] < dataframe["cloud_bot"]) & (dataframe["tenkan"] < dataframe["kijun"]) & (dataframe["close"] < dataframe["close_prev26"]) & (dataframe["adx"] > self.buy_adx_min) & (dataframe["cloud_width_pct"] > self.buy_cloud_min_pct) & (dataframe["close"] < dataframe["ema200"]) # ← filtre macro SHORT & (dataframe["volume"] > 0) ), "enter_short", ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( ((dataframe["tenkan"] < dataframe["kijun"]) | (dataframe["close"] < dataframe["cloud_bot"])) & (dataframe["volume"] > 0) ), "exit_long", ] = 1 dataframe.loc[ ( ((dataframe["tenkan"] > dataframe["kijun"]) | (dataframe["close"] > dataframe["cloud_top"])) & (dataframe["volume"] > 0) ), "exit_short", ] = 1 return dataframe