from datetime import datetime import talib.abstract as ta from freqtrade.strategy import ( IntParameter, IStrategy, ) from pandas import DataFrame class MacdAdxStrategy(IStrategy): """ Strategy adapted from paper: http://arxiv.org/abs/2511.00665 """ # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. INTERFACE_VERSION = 3 # Optimal timeframe for the strategy. timeframe = "5m" # Can this strategy go short? can_short: bool = False # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi". minimal_roi = {} # Optimal stoploss designed for the strategy. # This attribute will be overridden if the config file contains "stoploss". stoploss = -99 # Trailing stoploss trailing_stop = False # trailing_only_offset_is_reached = False # trailing_stop_positive = 0.01 # trailing_stop_positive_offset = 0.0 # Disabled / not configured # Run "populate_indicators()" only for new candle. process_only_new_candles = True # These values can be overridden in the config. use_exit_signal = True exit_profit_only = False ignore_roi_if_entry_signal = False # Number of candles the strategy requires before producing valid signals startup_candle_count: int = 30 # Strategy parameters macdFast = IntParameter(8, 15, default=15, space="buy") macdSlow = IntParameter(20, 30, default=26, space="buy") macdSignal = IntParameter(7, 15, default=9, space="buy") adxPeriod = IntParameter(10, 20, default=15, space="buy") def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: macd = ta.MACD( dataframe, fastperiod=self.macdFast.value, slowperiod=self.macdSlow.value, signalperiod=self.macdSignal.value ) dataframe["macd"] = macd["macd"] dataframe["macdsignal"] = macd["macdsignal"] dataframe["adx"] = ta.ADX(dataframe, timeperiod=self.adxPeriod.value) dataframe["mean-volume"] = dataframe["volume"].rolling(20).mean() return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (dataframe['macd'] > dataframe['macdsignal']) & (dataframe['adx'] > 25) & (dataframe["mean-volume"] > 0.75) ), "enter_long", ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (dataframe['macd'] < dataframe['macdsignal']) & (dataframe['adx'] > 25) & (dataframe["mean-volume"] > 0.75) ), "exit_long", ] = 1 return dataframe def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, entry_tag: str | None, side: str, **kwargs) -> bool: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_close = dataframe.iloc[-1]["close"] max_deviation = 0.01 # 1% deviation allowed deviation = abs(rate - last_close) / last_close if deviation > max_deviation: return False return True