"""Bot C: FreqAI adaptive ML strategy. A LightGBM regressor is continuously retrained on the last 30 days of data per pair and predicts the mean price move over the next 12 hours. Entries and exits follow the model's prediction — this is the statistical-learning counterpart to the LLM-driven bots. """ import talib.abstract as ta from freqtrade.strategy import IStrategy from pandas import DataFrame class FreqaiTrend(IStrategy): INTERFACE_VERSION = 3 timeframe = "1h" can_short = False process_only_new_candles = True startup_candle_count = 120 minimal_roi = {"0": 0.10, "720": 0.03, "1440": 0.01} stoploss = -0.05 trailing_stop = True trailing_stop_positive = 0.02 trailing_stop_positive_offset = 0.04 trailing_only_offset_is_reached = True entry_threshold = 0.01 # predicted 12h move must exceed +1% exit_threshold = -0.005 # exit when prediction turns below -0.5% def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame: dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period) dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period) dataframe["%-relative_volume-period"] = ( dataframe["volume"] / dataframe["volume"].rolling(period).mean() ) return dataframe def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame: dataframe["%-pct-change"] = dataframe["close"].pct_change() dataframe["%-raw_volume"] = dataframe["volume"] dataframe["%-raw_price"] = dataframe["close"] return dataframe def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame: dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek dataframe["%-hour_of_day"] = dataframe["date"].dt.hour return dataframe def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame: label_period = self.freqai_info["feature_parameters"]["label_period_candles"] dataframe["&-target"] = ( dataframe["close"].shift(-label_period).rolling(label_period).mean() / dataframe["close"] - 1 ) return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: return self.freqai.start(dataframe, metadata, self) def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ (dataframe["do_predict"] == 1) & (dataframe["&-target"] > self.entry_threshold) & (dataframe["volume"] > 0), "enter_long", ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ (dataframe["do_predict"] != 1) | (dataframe["&-target"] < self.exit_threshold), "exit_long", ] = 1 return dataframe