""" Realistic 10% Monthly Strategy Based on TemaAdxCmo but optimized for consistent monthly profits Improvements: - Remove SOL (worst performer) - Tighter stop loss (-6% instead of -8%) - 4x leverage (balanced risk/reward) - Faster ROI targets - Stricter entry filters (quality over quantity) - Only BTC/ETH (most liquid pairs) """ import numpy as np from pandas import DataFrame from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, BooleanParameter import talib.abstract as ta from datetime import datetime from typing import Optional class Realistic10Monthly(IStrategy): INTERFACE_VERSION = 3 can_short = False # Only longs (proven to work better) # Faster ROI for 10% monthly target minimal_roi = { "0": 0.06, # 6% immediate "10": 0.04, # 4% after 10 min "20": 0.025, # 2.5% after 20 min "40": 0.015, # 1.5% after 40 min "80": 0.01 # 1% after 80 min } # Tighter stop loss stoploss = -0.06 # 6% (was -8%) # Aggressive trailing trailing_stop = True trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.02 trailing_only_offset_is_reached = True timeframe = '15m' startup_candle_count = 100 # 4x leverage (balanced) leverage_num = IntParameter(3, 5, default=4, space='buy', optimize=False) # Stricter parameters for quality trades adx_threshold = IntParameter(25, 35, default=30, space='buy', optimize=True) cmo_threshold = IntParameter(15, 25, default=20, space='buy', optimize=True) rsi_long_threshold = IntParameter(40, 55, default=48, space='buy', optimize=True) volume_factor = DecimalParameter(1.3, 2.0, default=1.5, decimals=1, space='buy', optimize=True) # Trend filter use_ema_filter = BooleanParameter(default=True, space='buy') ema_period = IntParameter(40, 60, default=50, space='buy', optimize=True) # TEMA parameters tema_rolling_window = IntParameter(2, 4, default=3, space='buy', optimize=True) def leverage(self, pair: str, current_time, current_rate: float, proposed_leverage: float, max_leverage: float, side: str, **kwargs) -> float: return self.leverage_num.value def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # TEMA indicators dataframe['tema8'] = ta.TEMA(dataframe, timeperiod=8) dataframe['tema13'] = ta.TEMA(dataframe, timeperiod=13) dataframe['tema21'] = ta.TEMA(dataframe, timeperiod=21) # EMA trend filter dataframe['ema50'] = ta.EMA(dataframe, timeperiod=self.ema_period.value) # Momentum dataframe['adx'] = ta.ADX(dataframe, timeperiod=14) dataframe['cmo'] = ta.CMO(dataframe, timeperiod=14) dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) # ATR for volatility dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) # Volume dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean() # Bollinger Bands for additional confirmation bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0) dataframe['bb_lower'] = bollinger['lowerband'] dataframe['bb_middle'] = bollinger['middleband'] dataframe['bb_upper'] = bollinger['upperband'] # MACD macd = ta.MACD(dataframe) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Stricter entry conditions for quality trades """ long_conditions = ( # TEMA alignment (strong trend) (dataframe['tema8'] > dataframe['tema13']) & (dataframe['tema13'] > dataframe['tema21']) & # Strong momentum (dataframe['adx'] > self.adx_threshold.value) & (dataframe['cmo'] > self.cmo_threshold.value) & # Not overbought (dataframe['rsi'] > self.rsi_long_threshold.value) & (dataframe['rsi'] < 70) & # Good volume (dataframe['volume'] > dataframe['volume_mean'] * self.volume_factor.value) & # Trend filter (dataframe['close'] > dataframe['ema50']) & # MACD bullish (dataframe['macd'] > dataframe['macdsignal']) & # Price not extended (within reasonable range of EMA) (dataframe['close'] < dataframe['ema50'] * 1.05) & # Volatility filter (avoid choppy markets) (dataframe['atr'] > dataframe['atr'].rolling(window=20).mean() * 0.8) ) dataframe.loc[long_conditions, 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Exit when trend weakens """ dataframe.loc[ # ADX weakening (dataframe['adx'] < 20) | # TEMA reversal (dataframe['tema8'] < dataframe['tema13']) | # Below EMA (dataframe['close'] < dataframe['ema50'] * 0.98), 'exit_long' ] = 1 return dataframe def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: Optional[float], max_stake: float, leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float: """ Dynamic position sizing based on ADX strength """ dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) if dataframe.empty: return proposed_stake current_adx = dataframe.iloc[-1]['adx'] # Size positions based on trend strength if current_adx > 40: # Very strong trend multiplier = 1.1 elif current_adx > 30: # Strong trend multiplier = 1.0 else: # Moderate trend multiplier = 0.85 stake = proposed_stake * multiplier return min(stake, max_stake)