""" Moving Average Crossover Strategy for Freqtrade Classic golden cross / death cross strategy with enhancements """ import talib.abstract as ta from pandas import DataFrame from freqtrade.strategy import IStrategy, IntParameter import logging logger = logging.getLogger(__name__) class MACrossoverStrategy(IStrategy): """ Moving Average Crossover Strategy Buy when: - Fast MA crosses above Slow MA (Golden Cross) - Volume is above average - ADX indicates trending market Sell when: - Fast MA crosses below Slow MA (Death Cross) """ INTERFACE_VERSION = 3 timeframe = '1h' can_short = False # ROI table minimal_roi = { "0": 0.15, "60": 0.10, "120": 0.05, "240": 0.02 } stoploss = -0.08 trailing_stop = True trailing_stop_positive = 0.015 trailing_stop_positive_offset = 0.03 trailing_only_offset_is_reached = True process_only_new_candles = True use_exit_signal = True # Hyperoptable parameters fast_ma = IntParameter(10, 30, default=20, space="buy", optimize=True) slow_ma = IntParameter(40, 100, default=50, space="buy", optimize=True) adx_threshold = IntParameter(20, 35, default=25, space="buy", optimize=True) startup_candle_count: int = 100 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """Add technical indicators""" # Moving Averages for period in [10, 20, 30, 50, 100, 200]: dataframe[f'sma_{period}'] = ta.SMA(dataframe, timeperiod=period) dataframe[f'ema_{period}'] = ta.EMA(dataframe, timeperiod=period) # ADX for trend strength dataframe['adx'] = ta.ADX(dataframe, timeperiod=14) # RSI for confirmation dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) # Volume SMA dataframe['volume_sma'] = ta.SMA(dataframe['volume'], timeperiod=20) # ATR dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """Entry signal logic""" fast_ma_col = f'sma_{self.fast_ma.value}' slow_ma_col = f'sma_{self.slow_ma.value}' # Ensure columns exist if fast_ma_col not in dataframe.columns: dataframe[fast_ma_col] = ta.SMA(dataframe, timeperiod=self.fast_ma.value) if slow_ma_col not in dataframe.columns: dataframe[slow_ma_col] = ta.SMA(dataframe, timeperiod=self.slow_ma.value) dataframe.loc[ ( # Golden Cross (dataframe[fast_ma_col] > dataframe[slow_ma_col]) & (dataframe[fast_ma_col].shift(1) <= dataframe[slow_ma_col].shift(1)) & # ADX indicates trending market (dataframe['adx'] > self.adx_threshold.value) & # Volume confirmation (dataframe['volume'] > dataframe['volume_sma']) & # RSI not overbought (dataframe['rsi'] < 70) & (dataframe['volume'] > 0) ), 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """Exit signal logic""" fast_ma_col = f'sma_{self.fast_ma.value}' slow_ma_col = f'sma_{self.slow_ma.value}' dataframe.loc[ ( # Death Cross (dataframe[fast_ma_col] < dataframe[slow_ma_col]) & (dataframe[fast_ma_col].shift(1) >= dataframe[slow_ma_col].shift(1)) & (dataframe['volume'] > 0) ), 'exit_long'] = 1 return dataframe