""" Minimal sample strategy for the 428 / Integral backtester scaffold. Replace this file (or add your own under user_data/strategies/) and set STRATEGY= when running scripts. Defaults in config / compose point here so a fresh clone backtests without proprietary logic. """ from __future__ import annotations from pandas import DataFrame from freqtrade.strategy import IStrategy, IntParameter import talib.abstract as ta class SampleStrategy(IStrategy): """EMA crossover + RSI filter — placeholder only, not a production system.""" INTERFACE_VERSION = 3 timeframe = "15m" can_short = True minimal_roi = {"0": 0.04, "60": 0.02, "180": 0.01, "360": 0} stoploss = -0.03 trailing_stop = False process_only_new_candles = True startup_candle_count = 50 buy_rsi = IntParameter(20, 40, default=30, space="buy", optimize=True) sell_rsi = IntParameter(60, 80, default=70, space="sell", optimize=True) def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe["ema_fast"] = ta.EMA(dataframe, timeperiod=12) dataframe["ema_slow"] = ta.EMA(dataframe, timeperiod=26) dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ (dataframe["ema_fast"] > dataframe["ema_slow"]) & (dataframe["rsi"] < self.buy_rsi.value) & (dataframe["volume"] > 0), "enter_long", ] = 1 dataframe.loc[ (dataframe["ema_fast"] < dataframe["ema_slow"]) & (dataframe["rsi"] > self.sell_rsi.value) & (dataframe["volume"] > 0), "enter_short", ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ (dataframe["ema_fast"] < dataframe["ema_slow"]) & (dataframe["volume"] > 0), "exit_long", ] = 1 dataframe.loc[ (dataframe["ema_fast"] > dataframe["ema_slow"]) & (dataframe["volume"] > 0), "exit_short", ] = 1 return dataframe