import freqtrade.vendor.qtpylib.indicators as qtpylib import numpy as np import talib.abstract as ta from freqtrade.persistence import Trade from freqtrade.strategy.interface import IStrategy from pandas import DataFrame from datetime import datetime, timedelta from freqtrade.strategy import merge_informative_pair, CategoricalParameter, DecimalParameter ########################################################################################################### ## CombinedBearBullUnifiedV1 by witrer (ε€ͺδΈ–ι€š) ## ## ## ## Combined strategy integrating V5, V6, V9 features with enhanced bull/bear/sideways detection ## ## Multi-dimensional signal analysis with adaptive position management ## ## Based on ilya's strategies: https://github.com/i1ya/freqtrade-strategies ## ## ## ## GitHub: https://github.com/witrer ## ## Version: 1.0.0 ## ## Date: 2024-12-19 ## ## ## ########################################################################################################### def SSLChannels(dataframe, length=7): """SSL Channels indicator for trend detection""" df = dataframe.copy() df['ATR'] = ta.ATR(df, timeperiod=14) df['smaHigh'] = df['high'].rolling(length).mean() + df['ATR'] df['smaLow'] = df['low'].rolling(length).mean() - df['ATR'] df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, np.where(df['close'] < df['smaLow'], -1, np.NAN)) df['hlv'] = df['hlv'].ffill() df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow']) df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh']) return df['sslDown'], df['sslUp'] def market_direction(dataframe): """Detect market direction: Bull (1), Bear (-1), Sideways (0)""" bull_condition = ( (dataframe['ema_50'] > dataframe['ema_200']) & (dataframe['ema_20'] > dataframe['ema_50']) & (dataframe['close'] > dataframe['ema_20']) & (dataframe['ssl_up'] > dataframe['ssl_down']) & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) ) bear_condition = ( (dataframe['ema_50'] < dataframe['ema_200']) & (dataframe['ema_20'] < dataframe['ema_50']) & (dataframe['close'] < dataframe['ema_20']) & (dataframe['ssl_up'] < dataframe['ssl_down']) & (dataframe['ema_50_1h'] < dataframe['ema_200_1h']) ) return np.where(bull_condition, 1, np.where(bear_condition, -1, 0)) class CombinedBearBullUnifiedV1(IStrategy): INTERFACE_VERSION = 2 minimal_roi = { "0": 0.029, "10": 0.021, "40": 0.008, "120": 0.005, "300": 0.002, } stoploss = -0.99 timeframe = '5m' inf_1h = '1h' inf_4h = '4h' use_sell_signal = True sell_profit_only = False sell_profit_offset = 0.001 ignore_roi_if_buy_signal = False trailing_stop = False trailing_only_offset_is_reached = False trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.025 use_custom_stoploss = True process_only_new_candles = False startup_candle_count: int = 200 order_types = { 'buy': 'market', 'sell': 'market', 'stoploss': 'market', 'stoploss_on_exchange': False } # Parameters buy_params = { "buy_condition_bull_1_enable": True, "buy_condition_bull_2_enable": True, "buy_condition_bull_3_enable": True, "buy_condition_bear_1_enable": True, "buy_condition_bear_2_enable": True, "buy_condition_bear_3_enable": True, "buy_condition_sideways_1_enable": True, "buy_condition_sideways_2_enable": True, "buy_condition_universal_1_enable": True, "buy_condition_universal_2_enable": True, "buy_condition_extreme_dip_enable": False, "buy_condition_breakout_enable": True, "buy_condition_diamond_hands_enable": True, } # Categorical parameters buy_condition_bull_1_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_bull_2_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_bull_3_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_bear_1_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_bear_2_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_bear_3_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_sideways_1_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_sideways_2_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_universal_1_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_universal_2_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_extreme_dip_enable = CategoricalParameter([True, False], default=False, space='buy', optimize=False, load=True) buy_condition_breakout_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_diamond_hands_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) # Bollinger Bands parameters buy_bb20_close_bblowerband_bull = DecimalParameter(0.98, 1.02, default=0.99, space='buy', optimize=False, load=True) buy_bb20_close_bblowerband_bear = DecimalParameter(0.94, 0.98, default=0.975, space='buy', optimize=False, load=True) buy_bb20_close_bblowerband_sideways = DecimalParameter(0.96, 1.00, default=0.985, space='buy', optimize=False, load=True) # Volume parameters buy_volume_pump_1 = DecimalParameter(0.1, 0.9, default=0.4, space='buy', decimals=1, optimize=False, load=True) buy_volume_drop_1 = DecimalParameter(1, 10, default=4, space='buy', decimals=1, optimize=False, load=True) # RSI parameters buy_rsi_1h_bull = DecimalParameter(20.0, 45.0, default=35.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1h_bear = DecimalParameter(10.0, 25.0, default=15.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1h_sideways = DecimalParameter(15.0, 35.0, default=25.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_bull = DecimalParameter(25.0, 45.0, default=35.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_bear = DecimalParameter(7.0, 25.0, default=14.2, space='buy', decimals=1, optimize=False, load=True) buy_rsi_sideways = DecimalParameter(15.0, 35.0, default=25.0, space='buy', decimals=1, optimize=False, load=True) # MACD parameters buy_macd_bull = DecimalParameter(0.01, 0.05, default=0.02, space='buy', decimals=2, optimize=False, load=True) buy_macd_bear = DecimalParameter(0.02, 0.08, default=0.03, space='buy', decimals=2, optimize=False, load=True) # Additional parameters buy_extreme_rsi_threshold = DecimalParameter(15.0, 25.0, default=20.0, space='buy', decimals=1, optimize=False, load=True) buy_breakout_volume_threshold = DecimalParameter(1.2, 2.0, default=1.5, space='buy', decimals=1, optimize=False, load=True) buy_diamond_hands_dip_threshold = DecimalParameter(1.02, 1.08, default=1.05, space='buy', decimals=2, optimize=False, load=True) # Sell parameters sell_bull_profit_threshold = DecimalParameter(1.01, 1.03, default=1.015, space='sell', decimals=3, optimize=False, load=True) sell_bear_profit_threshold = DecimalParameter(1.003, 1.01, default=1.005, space='sell', decimals=3, optimize=False, load=True) sell_sideways_profit_threshold = DecimalParameter(1.005, 1.02, default=1.01, space='sell', decimals=3, optimize=False, load=True) def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: """Enhanced stoploss function combining V6 and V9 features""" if current_profit > 0.05: return 0.03 elif current_profit > 0.02: return 0.01 elif current_profit > 0: return 0.99 else: if (current_time - timedelta(minutes=240) > trade.open_date_utc): return 0.01 trade_time_50 = current_time - timedelta(minutes=50) if (trade_time_50 > trade.open_date_utc): try: number_of_candle_shift = int((trade_time_50 - trade.open_date_utc).total_seconds() / 300) dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) candle = dataframe.iloc[-number_of_candle_shift].squeeze() if current_rate * 1.015 < candle['open']: return 0.01 except IndexError: return 0.01 return 0.99 def informative_pairs(self): pairs = self.dp.current_whitelist() informative_pairs = [(pair, '1h') for pair in pairs] informative_pairs += [(pair, '4h') for pair in pairs] return informative_pairs def informative_1h_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: assert self.dp, "DataProvider is required for multiple timeframes." informative_1h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_1h) informative_1h['ema_20'] = ta.EMA(informative_1h, timeperiod=20) informative_1h['ema_50'] = ta.EMA(informative_1h, timeperiod=50) informative_1h['ema_200'] = ta.EMA(informative_1h, timeperiod=200) informative_1h['rsi'] = ta.RSI(informative_1h, timeperiod=14) ssl_down_1h, ssl_up_1h = SSLChannels(informative_1h, 20) informative_1h['ssl_down'] = ssl_down_1h informative_1h['ssl_up'] = ssl_up_1h macd, macdsignal, macdhist = ta.MACD(informative_1h) informative_1h['macd'] = macd informative_1h['macdsignal'] = macdsignal informative_1h['macdhist'] = macdhist return informative_1h def informative_4h_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: assert self.dp, "DataProvider is required for multiple timeframes." informative_4h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_4h) informative_4h['ema_50'] = ta.EMA(informative_4h, timeperiod=50) informative_4h['ema_200'] = ta.EMA(informative_4h, timeperiod=200) informative_4h['rsi'] = ta.RSI(informative_4h, timeperiod=14) return informative_4h def normal_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Bollinger Bands bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] dataframe['bb_middleband'] = bollinger['mid'] dataframe['bb_upperband'] = bollinger['upper'] # BinHV45 indicators from V5 bb_40 = qtpylib.bollinger_bands(dataframe['close'], window=40, stds=2) dataframe['lower'] = bb_40['lower'] dataframe['mid'] = bb_40['mid'] dataframe['bbdelta'] = (bb_40['mid'] - dataframe['lower']).abs() dataframe['closedelta'] = (dataframe['close'] - dataframe['close'].shift()).abs() dataframe['tail'] = (dataframe['close'] - dataframe['low']).abs() # Volume analysis dataframe['volume_mean_slow'] = dataframe['volume'].rolling(window=30).mean() dataframe['volume_mean_fast'] = dataframe['volume'].rolling(window=10).mean() # Moving averages dataframe['ema_12'] = ta.EMA(dataframe, timeperiod=12) dataframe['ema_20'] = ta.EMA(dataframe, timeperiod=20) dataframe['ema_26'] = ta.EMA(dataframe, timeperiod=26) dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) dataframe['sma_5'] = ta.SMA(dataframe, timeperiod=5) dataframe['sma_20'] = ta.SMA(dataframe, timeperiod=20) # Momentum indicators dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=7) # SSL Channels ssl_down, ssl_up = SSLChannels(dataframe, 7) dataframe['ssl_down'] = ssl_down dataframe['ssl_up'] = ssl_up # MACD macd, macdsignal, macdhist = ta.MACD(dataframe) dataframe['macd'] = macd dataframe['macdsignal'] = macdsignal dataframe['macdhist'] = macdhist # Market direction detection dataframe['market_direction'] = market_direction(dataframe) return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: informative_1h = self.informative_1h_indicators(dataframe, metadata) dataframe = merge_informative_pair(dataframe, informative_1h, self.timeframe, self.inf_1h, ffill=True) informative_4h = self.informative_4h_indicators(dataframe, metadata) dataframe = merge_informative_pair(dataframe, informative_4h, self.timeframe, self.inf_4h, ffill=True) dataframe = self.normal_tf_indicators(dataframe, metadata) return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ # πŸ‚ BULL MARKET CONDITIONS ( self.buy_condition_bull_1_enable.value & (dataframe['market_direction'] == 1) & (dataframe['close'] > dataframe['ema_200']) & (dataframe['close'] > dataframe['ema_200_1h']) & (dataframe['ema_50_4h'] > dataframe['ema_200_4h']) & (dataframe['close'] < dataframe['bb_lowerband'] * self.buy_bb20_close_bblowerband_bull.value) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(30) * self.buy_volume_pump_1.value) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['rsi_1h'] < self.buy_rsi_1h_bull.value) & (dataframe['volume'] > 0) ) | ( self.buy_condition_bull_2_enable.value & (dataframe['market_direction'] == 1) & (dataframe['close'] > dataframe['ema_200']) & (dataframe['ssl_up_1h'] > dataframe['ssl_down_1h']) & (dataframe['ema_26'] > dataframe['ema_12']) & ((dataframe['ema_26'] - dataframe['ema_12']) > (dataframe['open'] * self.buy_macd_bull.value)) & (dataframe['close'] < dataframe['bb_lowerband']) & (dataframe['rsi'] < self.buy_rsi_bull.value) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume'] > 0) ) | ( self.buy_condition_bull_3_enable.value & (dataframe['market_direction'] == 1) & (dataframe['close'] < dataframe['sma_5']) & (dataframe['ssl_up_1h'] > dataframe['ssl_down_1h']) & (dataframe['ema_50'] > dataframe['ema_200']) & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & (dataframe['rsi'] < dataframe['rsi_1h'] - 20) & (dataframe['volume'] > 0) ) | # 🐻 BEAR MARKET CONDITIONS ( self.buy_condition_bear_1_enable.value & (dataframe['market_direction'] == -1) & # Bear market detected (dataframe['close'] < dataframe['bb_lowerband'] * self.buy_bb20_close_bblowerband_bear.value) & (dataframe['rsi_1h'] < self.buy_rsi_1h_bear.value) & (dataframe['rsi'] < self.buy_rsi_bear.value) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(30) * self.buy_volume_pump_1.value) & (dataframe['volume'] > 0) ) | ( self.buy_condition_bear_2_enable.value & (dataframe['market_direction'] == -1) & # Bear market (dataframe['ema_26'] > dataframe['ema_12']) & ((dataframe['ema_26'] - dataframe['ema_12']) > (dataframe['open'] * self.buy_macd_bear.value)) & (dataframe['close'] < dataframe['bb_lowerband']) & (dataframe['rsi_1h'] < self.buy_rsi_1h_bear.value) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume'] > 0) ) | ( self.buy_condition_bear_3_enable.value & (dataframe['market_direction'] == -1) & # Bear market (dataframe['close'] < dataframe['bb_lowerband']) & (dataframe['bbdelta'] > dataframe['close'] * 0.008) & # V5 BinHV45 condition (dataframe['closedelta'] > dataframe['close'] * 0.0175) & (dataframe['tail'] < dataframe['bbdelta'] * 0.25) & (dataframe['close'] < dataframe['lower'].shift()) & (dataframe['close'] <= dataframe['close'].shift()) & (dataframe['volume'] > 0) ) | # πŸ“Š SIDEWAYS MARKET CONDITIONS ( self.buy_condition_sideways_1_enable.value & (dataframe['market_direction'] == 0) & # Sideways market detected (dataframe['close'] < dataframe['bb_lowerband'] * self.buy_bb20_close_bblowerband_sideways.value) & (dataframe['rsi_1h'] < self.buy_rsi_1h_sideways.value) & (dataframe['rsi'] < self.buy_rsi_sideways.value) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume'] > 0) ) | ( self.buy_condition_sideways_2_enable.value & (dataframe['market_direction'] == 0) & # Sideways market (dataframe['close'] < dataframe['sma_20']) & (dataframe['rsi_fast'] < 30) & (dataframe['macdhist'] < 0) & (dataframe['volume_mean_fast'] > dataframe['volume_mean_slow']) & (dataframe['volume'] > 0) ) | # 🌐 UNIVERSAL CONDITIONS (work in any market) ( self.buy_condition_universal_1_enable.value & (dataframe['close'] < dataframe['bb_lowerband']) & (dataframe['rsi'] < 30) & (dataframe['rsi_1h'] < 40) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume'] > 0) ) | ( self.buy_condition_universal_2_enable.value & (dataframe['close'] < dataframe['sma_5']) & (dataframe['ssl_up'] > dataframe['ssl_down']) & (dataframe['rsi'] < 35) & (dataframe['volume'] > dataframe['volume_mean_slow'] * 1.2) & (dataframe['volume'] > 0) ) | # πŸ”₯ EXTREME DIP CONDITIONS (high risk, high reward) ( self.buy_condition_extreme_dip_enable.value & (dataframe['rsi'] < 20) & (dataframe['rsi_1h'] < 25) & (dataframe['close'] < dataframe['bb_lowerband'] * 0.95) & (dataframe['volume'] > dataframe['volume_mean_slow'] * 2) & (dataframe['close'] < dataframe['close'].shift(5) * 0.97) & # 3% drop in 5 candles (dataframe['volume'] > 0) ) | # 🎯 BREAKOUT CONDITIONS (momentum plays) ( self.buy_condition_breakout_enable.value & (dataframe['close'] > dataframe['bb_middleband']) & (dataframe['close'] > dataframe['close'].shift()) & (dataframe['volume'] > dataframe['volume_mean_slow'] * 1.5) & (dataframe['rsi'] > 50) & (dataframe['rsi'] < 65) & (dataframe['ssl_up'] > dataframe['ssl_down']) & (dataframe['ema_12'] > dataframe['ema_26']) & (dataframe['volume'] > 0) ) | # πŸ’Ž DIAMOND HANDS CONDITIONS (long-term holders) ( self.buy_condition_diamond_hands_enable.value & (dataframe['ema_200'] > dataframe['ema_200'].shift(10)) & # Long-term uptrend (dataframe['ema_50_4h'] > dataframe['ema_200_4h']) & # 4h uptrend (dataframe['close'] < dataframe['ema_200'] * 1.05) & # Near major support (dataframe['rsi_4h'] < 40) & # 4h oversold (dataframe['volume'] > 0) ), 'buy' ] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Enhanced market-adaptive sell logic with multiple exit strategies """ dataframe.loc[ ( # πŸ‚ BULL MARKET SELL CONDITIONS (less aggressive) ( (dataframe['market_direction'] == 1) & ( # Take profit conditions for bull market (dataframe['close'] > dataframe['bb_middleband'] * 1.015) | (dataframe['rsi'] > 75) | (dataframe['close'] > dataframe['bb_upperband']) | # Momentum reversal in bull market ( (dataframe['rsi'] > 65) & (dataframe['rsi'].shift() > dataframe['rsi']) & (dataframe['close'] > dataframe['bb_middleband']) ) ) ) | # 🐻 BEAR MARKET SELL CONDITIONS (more aggressive) ( (dataframe['market_direction'] == -1) & ( # Quick profit taking in bear market (dataframe['close'] > dataframe['bb_middleband'] * 1.005) | (dataframe['rsi'] > 60) | # Any sign of weakness ( (dataframe['close'] > dataframe['bb_middleband']) & (dataframe['volume'] > dataframe['volume_mean_slow'] * 1.5) ) | # Dead cat bounce protection ( (dataframe['rsi'] > 55) & (dataframe['close'] > dataframe['sma_20']) ) ) ) | # πŸ“Š SIDEWAYS MARKET SELL CONDITIONS (balanced) ( (dataframe['market_direction'] == 0) & ( # Standard profit taking (dataframe['close'] > dataframe['bb_middleband'] * 1.01) | (dataframe['rsi'] > 70) | # Range trading exits ( (dataframe['close'] > dataframe['bb_upperband'] * 0.98) & (dataframe['rsi'] > 60) ) ) ) | # 🚨 UNIVERSAL EMERGENCY SELLS ( # Extreme overbought (dataframe['rsi'] > 85) | (dataframe['rsi_1h'] > 80) | # Parabolic move (dataframe['close'] > dataframe['bb_upperband'] * 1.05) | # Volume spike with reversal ( (dataframe['volume'] > dataframe['volume_mean_slow'] * 3) & (dataframe['close'] < dataframe['open']) & (dataframe['rsi'] > 65) ) | # MACD bearish divergence ( (dataframe['macd'] < dataframe['macdsignal']) & (dataframe['rsi'] > 60) & (dataframe['close'] > dataframe['bb_middleband']) ) | # SSL channel reversal ( (dataframe['ssl_up'] < dataframe['ssl_down']) & (dataframe['close'] > dataframe['bb_middleband']) & (dataframe['rsi'] > 50) ) ) | # πŸ’° PROFIT PROTECTION SELLS ( # Protect profits in any market condition (dataframe['close'] > dataframe['ema_12'] * 1.02) & (dataframe['rsi'] > 65) & (dataframe['volume'] > dataframe['volume_mean_slow'] * 1.2) ) ) & (dataframe['volume'] > 0), # Always ensure volume > 0 'sell' ] = 1 return dataframe def leverage_tier(self, pair: str, current_time: datetime, current_rate: float) -> float: """ Dynamic leverage based on market conditions and volatility Note: This is for futures trading platforms that support leverage """ try: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) latest = dataframe.iloc[-1].squeeze() # Base leverage base_leverage = 1.0 # Market direction adjustment if latest['market_direction'] == 1: # Bull market base_leverage = 1.2 elif latest['market_direction'] == -1: # Bear market base_leverage = 0.8 else: # Sideways base_leverage = 1.0 # Volatility adjustment atr_pct = latest.get('atr', 0) / current_rate if current_rate > 0 else 0 if atr_pct > 0.05: # High volatility base_leverage *= 0.7 elif atr_pct < 0.02: # Low volatility base_leverage *= 1.1 # Volume confirmation volume_ratio = latest['volume'] / latest['volume_mean_slow'] if volume_ratio > 2: # High volume base_leverage *= 1.1 elif volume_ratio < 0.5: # Low volume base_leverage *= 0.8 return max(0.1, min(base_leverage, 2.0)) # Cap between 0.1 and 2.0 except Exception: return 1.0 # Default leverage def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, **kwargs) -> bool: """ Advanced trade entry confirmation with risk management """ try: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) latest = dataframe.iloc[-1].squeeze() # Market condition checks market_dir = latest['market_direction'] # Risk score calculation risk_score = 0 # Volume spike risk volume_ratio = latest['volume'] / latest['volume_mean_slow'] if volume_ratio > 3: risk_score += 30 elif volume_ratio > 2: risk_score += 15 # Volatility risk candle_size = abs(latest['close'] - latest['open']) / latest['open'] if candle_size > 0.05: risk_score += 25 elif candle_size > 0.03: risk_score += 10 # RSI extremes if latest['rsi'] < 10 or latest['rsi'] > 90: risk_score += 20 # Market timing risk hour = current_time.hour if hour in [0, 1, 2, 23]: # Low liquidity hours risk_score += 15 # Bear market additional caution if market_dir == -1: risk_score += 10 # Reject trade if risk score too high if risk_score > 60: return False # Additional quality checks quality_score = 0 # Technical setup quality if latest['close'] < latest['bb_lowerband']: quality_score += 20 if latest['rsi'] < 30: quality_score += 15 if latest['ssl_up'] > latest['ssl_down']: quality_score += 10 # Trend alignment if market_dir == 1 and latest['ema_50'] > latest['ema_200']: quality_score += 15 # Require minimum quality score return quality_score >= 25 except Exception: # If analysis fails, err on side of caution return True def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, current_time: datetime, **kwargs) -> bool: """ Confirm trade exit with additional logic """ try: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) latest = dataframe.iloc[-1].squeeze() # Always allow stoploss and ROI exits if sell_reason in ['stoploss', 'roi']: return True # Market-specific exit confirmation market_dir = latest['market_direction'] # In bear markets, be more aggressive with exits if market_dir == -1: return True # In bull markets, allow more room for growth if market_dir == 1 and trade.calc_profit_ratio(rate) < 0.02: # Don't exit early in bull market unless good reason if latest['rsi'] < 70 and latest['close'] < latest['bb_upperband']: return False return True except Exception: return True # Additional utility functions for enhanced strategy performance def calculate_support_resistance(dataframe, window=20): """ Calculate dynamic support and resistance levels """ # Rolling max/min for resistance/support dataframe['resistance'] = dataframe['high'].rolling(window=window).max() dataframe['support'] = dataframe['low'].rolling(window=window).min() # Pivot points dataframe['pivot'] = (dataframe['high'] + dataframe['low'] + dataframe['close']) / 3 dataframe['r1'] = 2 * dataframe['pivot'] - dataframe['low'] dataframe['s1'] = 2 * dataframe['pivot'] - dataframe['high'] return dataframe def market_structure_analysis(dataframe): """ Analyze market structure for better entries """ # Higher highs and higher lows detection dataframe['higher_high'] = ( (dataframe['high'] > dataframe['high'].shift(1)) & (dataframe['high'].shift(1) > dataframe['high'].shift(2)) ) dataframe['higher_low'] = ( (dataframe['low'] > dataframe['low'].shift(1)) & (dataframe['low'].shift(1) > dataframe['low'].shift(2)) ) # Lower highs and lower lows dataframe['lower_high'] = ( (dataframe['high'] < dataframe['high'].shift(1)) & (dataframe['high'].shift(1) < dataframe['high'].shift(2)) ) dataframe['lower_low'] = ( (dataframe['low'] < dataframe['low'].shift(1)) & (dataframe['low'].shift(1) < dataframe['low'].shift(2)) ) return dataframe # Performance monitoring and statistics class StrategyStats: """ Track strategy performance metrics """ def __init__(self): self.total_trades = 0 self.winning_trades = 0 self.losing_trades = 0 self.bull_market_trades = 0 self.bear_market_trades = 0 self.sideways_market_trades = 0 def update_trade(self, profit, market_direction): self.total_trades += 1 if profit > 0: self.winning_trades += 1 else: self.losing_trades += 1 if market_direction == 1: self.bull_market_trades += 1 elif market_direction == -1: self.bear_market_trades += 1 else: self.sideways_market_trades += 1 def get_win_rate(self): return self.winning_trades / self.total_trades if self.total_trades > 0 else 0 def get_market_distribution(self): total = self.total_trades if total == 0: return {"bull": 0, "bear": 0, "sideways": 0} return { "bull": self.bull_market_trades / total, "bear": self.bear_market_trades / total, "sideways": self.sideways_market_trades / total } # Configuration templates for different market conditions STRATEGY_CONFIGS = { "aggressive": { "max_open_trades": 5, "buy_conditions_enabled": 10, "roi_multiplier": 1.2, "risk_tolerance": "high" }, "conservative": { "max_open_trades": 2, "buy_conditions_enabled": 6, "roi_multiplier": 0.8, "risk_tolerance": "low" }, "balanced": { "max_open_trades": 3, "buy_conditions_enabled": 8, "roi_multiplier": 1.0, "risk_tolerance": "medium" } } # Additional parameters for the strategy class # (These would be added to the main class) # Additional buy conditions that were referenced buy_condition_extreme_dip_enable = CategoricalParameter([True, False], default=False, space='buy', optimize=False, load=True) buy_condition_breakout_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_diamond_hands_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) # Advanced parameters for fine-tuning buy_extreme_rsi_threshold = DecimalParameter(15.0, 25.0, default=20.0, space='buy', decimals=1, optimize=False, load=True) buy_breakout_volume_threshold = DecimalParameter(1.2, 2.0, default=1.5, space='buy', decimals=1, optimize=False, load=True) buy_diamond_hands_dip_threshold = DecimalParameter(1.02, 1.08, default=1.05, space='buy', decimals=2, optimize=False, load=True) # Sell parameters for fine-tuning sell_bull_profit_threshold = DecimalParameter(1.01, 1.03, default=1.015, space='sell', decimals=3, optimize=False, load=True) sell_bear_profit_threshold = DecimalParameter(1.003, 1.01, default=1.005, space='sell', decimals=3, optimize=False, load=True) sell_sideways_profit_threshold = DecimalParameter(1.005, 1.02, default=1.001, space='sell', decimals=3, optimize=False, load=True)