# source: https://raw.githubusercontent.com/Aurang-z3b/Freqtrade-Manual-strategy/f521da5954d7bef8a35f56c3b07be3b0e7091b57/user_data/strategies/MyAdvancedStrategy.py
import logging
from datetime import datetime
from functools import reduce

import numpy as np
import pandas as pd
import talib.abstract as ta
from pandas import DataFrame

from freqtrade.persistence import Trade
from freqtrade.strategy import IStrategy


logger = logging.getLogger(__name__)
class Github_Aurang_z3b_Freqtrade_Manual_strategy__MyAdvancedStrategy__20250619_140610(IStrategy):
    INTERFACE_VERSION = 3
    timeframe = "5m"
    stoploss = -1.0
    risk_per_trade = 0.01
    minimal_roi = {
        "0": 0.005,
        "5": 0.005,
        "15": 0.01,
        "120": 0.05,
    }
    trailing_stop = True
    trailing_stop_positive = 0.005
    trailing_stop_positive_offset = 0.035
    trailing_only_offset_is_reached = True
    min_quote_volume = 100000
    min_volume = 1000
    max_open_trades = 6
    startup_candle_count = 50
    buy_rsi_threshold = 30
    sell_rsi_threshold = 65
    bb_period = 20
    macd_fast = 12
    macd_slow = 26
    macd_signal = 9
    confluence_threshold = 75
    market_volatility_threshold = 0.02
    min_profit_ratio = 2.5
    volume_spike_threshold = 2
    trend_strength_threshold = 1

    def populate_indicators(self, df: DataFrame, metadata: dict) -> DataFrame:
        df["rsi"] = ta.RSI(df, timeperiod=14)
        df["rsi_smooth"] = df["rsi"].rolling(window=3).mean()
        df["rsi_fast"] = ta.RSI(df, timeperiod=7)
        def generate_signal(row):
            if pd.isna(row["rsi_smooth"]):
                return None
            if row["rsi_smooth"] < 30:
                return "BUY"
            elif row["rsi_smooth"] > 70:
                return "SELL"
            else:
                return None
        df["rsi_signal"] = df.apply(generate_signal, axis=1)
        macd = ta.MACD(
            df, fastperiod=self.macd_fast, slowperiod=self.macd_slow, signalperiod=self.macd_signal
        )
        df["macd"] = macd["macd"]
        df["macdsignal"] = macd["macdsignal"]
        df["macd_histogram"] = macd["macdhist"]
        df["macd_momentum"] = df["macd_histogram"] - df["macd_histogram"].shift(1)
        df["macd_crossover"] = (df["macd"] > df["macdsignal"]) & (
            df["macd"].shift(1) <= df["macdsignal"].shift(1)
        )
        bb_upper, bb_middle, bb_lower = ta.BBANDS(df["close"], timeperiod=self.bb_period)
        df["bb_upper"] = bb_upper
        df["bb_middle"] = bb_middle
        df["bb_lower"] = bb_lower
        df["bb_width"] = (df["bb_upper"] - df["bb_lower"]) / df["bb_middle"]
        df["bb_position"] = (df["close"] - df["bb_lower"]) / (df["bb_upper"] - df["bb_lower"])
        df["bb_squeeze"] = (
            df["bb_width"] < df["bb_width"].rolling(20).mean() * 0.8
        )
        df["quote_volume"] = df["volume"] * df["close"]
        df["volume_sma"] = df["volume"].rolling(window=20).mean()
        df["volume_ratio"] = df["volume"] / df["volume_sma"]
        df["volume_spike"] = df["volume_ratio"] > self.volume_spike_threshold
        df["ema_fast"] = ta.EMA(df["close"], timeperiod=8)
        df["ema_slow"] = ta.EMA(df["close"], timeperiod=21)
        df["ema_very_slow"] = ta.EMA(df["close"], timeperiod=50)
        df["trend"] = np.where(
            df["ema_fast"] > df["ema_slow"], 1, np.where(df["ema_fast"] < df["ema_slow"], -1, 0)
        )
        df["trend_strength"] = abs(df["ema_fast"] - df["ema_slow"]) / df["ema_slow"]
        df["long_term_trend"] = np.where(df["ema_slow"] > df["ema_very_slow"], 1, -1)


        df["atr"] = ta.ATR(df, timeperiod=14)
        df["atr_normalized"] = df["atr"] / df["close"]
        df["volatility_filter"] = df["atr_normalized"] > self.market_volatility_threshold
        stoch = ta.STOCH(df)
        df["stoch_k"] = stoch["slowk"]
        df["stoch_d"] = stoch["slowd"]
        df["stoch_oversold"] = df["stoch_k"] < 20
        df["higher_low"] = (df["low"] > df["low"].shift(1)) & (
            df["low"].shift(1) < df["low"].shift(2)
        )
        df["bullish_engulfing"] = (
            (df["open"] < df["close"])
            & (df["open"].shift(1) > df["close"].shift(1))
            & (df["open"] <= df["close"].shift(1))
            & (df["close"] >= df["open"].shift(1))
        )
        df["confluence_score"] = self.calculate_enhanced_confluence_score(df)
        df["risk_reward_ratio"] = self.calculate_risk_reward(df)
        df["market_regime"] = self.detect_market_regime(df)
        return df

    def calculate_enhanced_confluence_score(self, df: DataFrame) -> pd.Series:
        score = pd.Series(0.0, index=df.index)
        score += np.where(df["rsi"] < 20, 30, 0)
        score += np.where((df["rsi"] >= 20) & (df["rsi"] < 30), 20, 0)
        score += np.where((df["rsi"] >= 30) & (df["rsi"] < 40), 12, 0)
        score += np.where((df["rsi"] >= 40) & (df["rsi"] < 50), 5, 0)
        score += np.where(df["rsi_fast"] < 30, 8, 0)
        score += np.where(df["macd_crossover"], 15, 0)
        score += np.where(df["macd"] > df["macdsignal"], 10, 0)
        score += np.where(df["macd_momentum"] > 0, 8, 0)
        score += np.where(df["bb_position"] < 0.1, 20, 0)
        score += np.where((df["bb_position"] >= 0.1) & (df["bb_position"] < 0.25), 12, 0)
        score += np.where(df["bb_squeeze"], 8, 0)
        score += np.where(df["volume_spike"], 15, 0)
        score += np.where(df["volume_ratio"] > 1.5, 10, 0)
        score += np.where(df["volume_ratio"] > 1.2, 5, 0)
        score += np.where(df["trend"] == 1, 12, 0)
        score += np.where(df["long_term_trend"] == 1, 8, 0)
        score += np.where(df["trend_strength"] > self.trend_strength_threshold, 5, 0)
        score += np.where(df["stoch_oversold"], 10, 0)
        score += np.where(df["higher_low"], 8, 0)
        score += np.where(df["bullish_engulfing"], 12, 0)
        return score

    def calculate_risk_reward(self, df: DataFrame) -> pd.Series:
        target_distance = np.maximum(
            df["atr"] * 3,
            (df["bb_upper"] - df["close"]) * 0.8,
        )
        reward = target_distance / df["close"]
        risk_distance = np.maximum(
            df["atr"] * 2,
            (df["close"] - df["bb_lower"]) * 0.5,
        )
        risk = risk_distance / df["close"]
        return np.where(risk > 0, reward / risk, 0)

    def detect_market_regime(self, df: DataFrame) -> pd.Series:
        """Detect market regime: 1=trending, 0=ranging, -1=volatile"""
        adx = ta.ADX(df, timeperiod=14)
        regime = pd.Series(0, index=df.index)
        regime = np.where(adx > 25, 1, regime)
        regime = np.where(df["volatility_filter"] & (adx < 20), -1, regime)
        return regime

    def populate_buy_trend(self, df: DataFrame, metadata: dict) -> DataFrame:

        pair = metadata["pair"].replace("/", "")
        macd_strict_condition = (
            (df["macd"] > df["macdsignal"])
            & (df["macd"].shift(1) <= df["macdsignal"].shift(1))
            & (df["macd_histogram"] > 0.03)
            & (df["macd_momentum"] > 0)
            & (df["macd"] > df["macd"].shift(1))
            & (df["macdsignal"] > df["macdsignal"].shift(1))
            & (df["macd"] > 0)
            & (df["macdsignal"] > 0)
        )
        df["condition_score"] = (
            (df["rsi_smooth"] < self.buy_rsi_threshold).astype(int)
            + macd_strict_condition.astype(int)
            + (df["quote_volume"] > self.min_quote_volume).astype(int)
            + (df["volume_ratio"] > self.volume_spike_threshold).astype(int)
            + (df["confluence_score"] > self.confluence_threshold).astype(int)
            + (df["risk_reward_ratio"] > self.min_profit_ratio).astype(int)
            + (df["market_regime"] > 0).astype(int)
            + (df["trend_strength"] > self.trend_strength_threshold).astype(int)
            + (df["bb_position"] < 0.95).astype(int)
        )
        df["buy"] = 0
        condition = (df["condition_score"] >= 6) & (df["rsi_smooth"] <= self.buy_rsi_threshold)
        df.loc[condition, "buy"] = 1


        overlap = df[(df["condition_score"] >= 6) & (df["rsi_smooth"] <= self.buy_rsi_threshold)]
        logger.info(f"Overlapping candles passing buy criteria {pair} : {len(overlap)}")

        logger.info(
            f"Buy condition breakdown: RSI < {self.buy_rsi_threshold} passes: {(df['rsi_smooth'] <= self.buy_rsi_threshold).sum()}"  # noqa: E501
        )
        logger.info(
            f"MACD condition passes: {((df['macd'] > df['macdsignal']) | (df['macd_crossover'])).sum()}"  # noqa: E501
        )
        logger.info(
            f"Volume spike passes: {(df['volume_ratio'] > self.volume_spike_threshold).sum()}"
        )
        logger.info(
            f"Confluence passes: {(df['confluence_score'] > self.confluence_threshold).sum()}"
        )
        logger.info(
            f"Risk/reward passes: {(df['risk_reward_ratio'] > self.min_profit_ratio).sum()}"
        )
        logger.info(f"Market regime passes: {(df['market_regime'] > 0).sum()}")
        logger.info(f"Total condition_score passes >=6: {(df['condition_score'] >= 6).sum()}")

        return df

    def populate_sell_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
        conditions = [
            (df["rsi"] > self.sell_rsi_threshold) | (df["rsi_fast"] > 75),

            (df["macd"] < df["macdsignal"]),

            (df["confluence_score"] < self.confluence_threshold * 0.8),
            (
                (df["bb_position"] > 0.9)
                | (df["trend"] == -1)
                | (df["stoch_k"] > 80)
            ),
        ]
        df.loc[reduce(lambda x, y: x & y, conditions), "sell"] = 1
        return df

        #   def custom_stoploss(
        #       self,
        #      pair: str,
        #     trade: Trade,
        #    current_time: datetime,
        #   current_rate: float,
        #     current_profit: float,
        #    **kwargs,
        # ) -> float:
        """
        Dynamic stop loss based on ATR and trade duration
        """

    # if current_profit < -0.02:  # Hard stop at -2%
    #    return -0.02

    # Get the latest dataframe
    # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
    # if dataframe.empty:
    #    return self.stoploss

    # Get current ATR
    # current_atr = dataframe["atr_normalized"].iloc[-1]

    # Calculate dynamic stop based on ATR
    # if current_profit > 0.01:  # If in profit > 1%
    # Tighter stop using ATR
    #   dynamic_stop = -(current_atr * 1.5)
    # else:
    #     # Wider stop for early stages
    ##       dynamic_stop = -(current_atr * 2.5)

    ##  return max(dynamic_stop, self.stoploss)

    def custom_stoploss(
        self,
        pair: str,
        trade: Trade,
        current_time: datetime,
        current_rate: float,
        current_profit: float,
        **kwargs,
    ) -> float:
        """
        Dynamic stoploss:
        - Hard stop at -5%
        - ATR-based dynamic stoploss for adaptive risk management
        """
        if current_profit < -0.05:
            return -0.05

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe is None or dataframe.empty:
            return self.stoploss
        current_atr = dataframe["atr_normalized"].iloc[-1]
        if pd.isna(current_atr) or current_atr <= 0:
            return self.stoploss
        if current_profit > 0.02:

            dynamic_stop = -(current_atr * 1.5)
        else:

            dynamic_stop = -(current_atr * 2.5)
        return max(dynamic_stop, self.stoploss)

    def confirm_trade_entry(
        self,
        pair: str,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        current_time: datetime,
        entry_tag: str,
        side: str,
        **kwargs,
    ) -> bool:
        """
        Additional trade entry confirmation
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if dataframe.empty:
            return True
        if dataframe["volatility_filter"].iloc[-1]:
            logger.info(f"Skipping {pair} due to high volatility")
            return False
        current_confluence = dataframe["confluence_score"].iloc[-1]
        if current_confluence < self.confluence_threshold * 0.9:
            logger.info(f"Skipping {pair} due to low confluence score: {current_confluence}")
            return False
        return True

plot_config = {
    "main_plot": {
        "close": {"color": "blue"},
        "ema_fast": {"color": "orange"},
        "ema_slow": {"color": "red"},
        "ema_very_slow": {"color": "purple"},
        "bb_upper": {"color": "grey"},
        "bb_middle": {"color": "lightgrey"},
        "bb_lower": {"color": "grey"},
    },
    "subplots": {
        "RSI": {
            "rsi": {"color": "blue"},
            "rsi_smooth": {"color": "purple"},
            "rsi_fast": {"color": "orange"},
        },
        "MACD": {
            "macd": {"color": "blue"},
            "macdsignal": {"color": "red"},
            "macd_histogram": {"color": "grey"},
        },
        "Bollinger": {
            "bb_width": {"color": "green"},
            "bb_position": {"color": "brown"},
        },
        "Volume": {
            "volume": {"color": "grey"},
            "volume_sma": {"color": "orange"},
            "volume_ratio": {"color": "blue"},
        },
        "Stochastic": {
            "stoch_k": {"color": "blue"},
            "stoch_d": {"color": "red"},
        },
        "Confluence": {
            "confluence_score": {"color": "black"},
        },
        "Risk/Reward": {
            "risk_reward_ratio": {"color": "gold"},
        },
        "Market Regime": {
            "market_regime": {"color": "cyan"},
        },
    },
}
