# source: https://raw.githubusercontent.com/DeepeshKalura/project-orca/5c6fc6c9c07d52071ba1782e9db82bcc108d3381/bot/user_data/strategies/HybridBreakoutTrend.py
# --- Do not remove these libs ---
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
from functools import reduce
import operator
from freqtrade.persistence import Trade

# Technical analysis library
import talib.abstract as ta
# Quantstart technical library
import freqtrade.vendor.qtpylib.indicators as qtpylib


class Github_DeepeshKalura_project_orca__HybridBreakoutTrend__20250807_111036(IStrategy):
    """
    Hybrid Breakout & Trend Following Strategy - V10 (Final Version)
    - Two distinct sub-strategies for different market regimes.
    - TREND: Surgical entries via RSI & Proximity filters.
    - BREAKOUT: Momentum-confirmed entries to avoid false signals.
    - Advanced, tag-aware risk management for both strategies.
    """

    def __init__(self, config: dict) -> None:
        super().__init__(config)
        self.dp = None
        self.custom_info = {}

    # --- Strategy Hyperparameters ---
    adx_period = IntParameter(10, 20, default=14, space='buy', optimize=True)
    adx_threshold = IntParameter(20, 30, default=25, space='buy', optimize=True)
    bb_period = IntParameter(20, 40, default=20, space='buy', optimize=True)
    bb_stddev = DecimalParameter(1.5, 2.5, default=2.0, space='buy', optimize=True)
    ema_short_period = IntParameter(10, 30, default=21, space='buy', optimize=True)
    ema_long_period = IntParameter(40, 60, default=50, space='buy', optimize=True)
    atr_multiplier = DecimalParameter(1.5, 5.0, default=2.5, space='sell', optimize=True)

    # --- Strategy Configuration ---
    timeframe = '4h'
    minimal_roi = {"0": 1.0}
    stoploss = -0.99
    use_custom_stoploss = True

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=self.adx_period.value)
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=self.bb_period.value, stds=self.bb_stddev.value)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']
        dataframe['ema_short'] = ta.EMA(dataframe, timeperiod=self.ema_short_period.value)
        dataframe['ema_long'] = ta.EMA(dataframe, timeperiod=self.ema_long_period.value)
        dataframe['rsi'] = ta.RSI(dataframe)
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)
        dataframe['bb_width'] = (dataframe['bb_upperband'] - dataframe['bb_middleband']) / dataframe['bb_middleband']
        dataframe['bb_width_rolling_mean'] = dataframe['bb_width'].rolling(48).mean()
        dataframe['volume_mean_slow'] = dataframe['volume'].rolling(window=30).mean()
        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime',
                            current_rate: float, current_profit: float, **kwargs) -> float:
        if not self.dp:
            return -0.99
        
        if trade.enter_tag == 'breakout':
            return -0.02

        elif trade.enter_tag == 'trend':
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            entry_candle = dataframe.loc[dataframe['date'] == trade.open_date_utc]
            if entry_candle.empty: return -0.99
            entry_atr = entry_candle['atr'].iat[0]
            stop_price = trade.open_rate - (entry_atr * self.atr_multiplier.value)
            return (stop_price / trade.open_rate) - 1.0

        return -0.99

    def custom_exit(self, pair: str, trade: Trade, current_time: 'datetime', current_rate: float,
                        current_profit: float, **kwargs):
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        if current_profit > 0:
            if last_candle['rsi'] < 50:
                return 'rsi_profit_take'

            if trade.enter_tag == 'trend':
                stop_price = current_rate - (last_candle['atr'] * self.atr_multiplier.value)
                new_stop_loss = (stop_price / trade.open_rate) - 1.0
                return new_stop_loss
        return None
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        # --- Timeframe-Specific Parameter Adjustments ---
        if self.timeframe == '4h':
            # For 4h, we need different thresholds.
            # Breakouts need a QUIETER market, so a lower ADX.
            breakout_adx_threshold = 20
            # Trends need a STRONGER signal, so a higher ADX.
            trend_adx_threshold = 30
        else:
            # Default values for 1h timeframe
            breakout_adx_threshold = self.adx_threshold.value
            trend_adx_threshold = self.adx_threshold.value

        # --- SUB-STRATEGY 1: VOLATILITY BREAKOUT LOGIC ---
        breakout_conditions = []
        breakout_conditions.append(dataframe['adx'] < breakout_adx_threshold) # Using timeframe-specific value
        breakout_conditions.append(dataframe['bb_width'] < dataframe['bb_width_rolling_mean'] * 0.5)
        breakout_conditions.append(dataframe['rsi'] > 60)
        breakout_conditions.append(qtpylib.crossed_above(dataframe['close'], dataframe['bb_upperband']))
        breakout_conditions.append(dataframe['volume'] > dataframe['volume'].rolling(5).mean() * 2)
        
        if breakout_conditions:
            dataframe.loc[reduce(operator.and_, breakout_conditions), ['enter_long', 'enter_tag']] = (1, 'breakout')

        # --- SUB-STRATEGY 2: TREND FOLLOWING LOGIC ---
        trend_conditions = []
        trend_conditions.append(dataframe['close'] > dataframe['ema_200'])
        trend_conditions.append(dataframe['ema_short'] > dataframe['ema_long'])  
        trend_conditions.append(dataframe['adx'] > trend_adx_threshold) # Using timeframe-specific value
        trend_conditions.append(dataframe['rsi'].shift(1) < 45)
        trend_conditions.append(dataframe['rsi'] > dataframe['rsi'].shift(1))
        trend_conditions.append((dataframe['close'] / dataframe['ema_short']) < 1.015)
        trend_conditions.append(qtpylib.crossed_above(dataframe['close'], dataframe['ema_short']))
        trend_conditions.append(dataframe['bb_width'] < (dataframe['bb_width_rolling_mean'] * 2.0))
        trend_conditions.append(dataframe['volume_mean_slow'] > 0)
        
        if metadata['pair'] == 'BTC/USDT':
            trend_conditions.append(dataframe['adx'] >= 35) # Adjusted for 4h
            trend_conditions.append(dataframe['rsi'] < 75) 
        else:
            trend_conditions.append(dataframe['rsi'] < 70) 

        if trend_conditions:
            dataframe.loc[reduce(operator.and_, trend_conditions), ['enter_long', 'enter_tag']] = (1, 'trend')

        return dataframe

    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['exit_long'] = 0
        return dataframe