# source: https://raw.githubusercontent.com/imadev26/crypto-trading-bot/4335d4bd136c42854e81def5ee01afa882707de2/user_data/strategies/TrendMasterPro.py
# directory_url: https://github.com/imadev26/crypto-trading-bot/blob/main/user_data/strategies/
# User: imadev26
# Repository: crypto-trading-bot
# --------------------# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
import numpy as np
import pandas as pd
from datetime import datetime, timedelta, timezone
from pandas import DataFrame
from typing import Optional, Union, Dict

from freqtrade.strategy import (
    IStrategy,
    Trade,
    Order,
    PairLocks,
    informative,
    BooleanParameter,
    CategoricalParameter,
    DecimalParameter,
    IntParameter,
    RealParameter,
)

import talib.abstract as ta
import pandas_ta as pta
from technical import qtpylib


class Github_imadev26_crypto_trading_bot__TrendMasterPro__20260831_150440(IStrategy):
    """
    ================================================================================
    Github_imadev26_crypto_trading_bot__TrendMasterPro__20260831_150440 - Institutional Multi-Timeframe Trend Following Strategy
    ================================================================================
    Designed for High Win-rate, strict Risk Management, and Capital Preservation.
    
    Key Mechanisms:
    1. Macro 1h Higher Timeframe Trend Filter:
       - 1h EMA 50 > 1h EMA 200 (Macro Golden Cross)
       - 1h Close > 1h EMA 200 (Above major support)
       - 1h ADX > 20 (Strong market trend confirmation)
    2. Micro 5m Entry Triggers:
       - 5m EMA 9 > 5m EMA 21 (Short-term bullish momentum)
       - 5m RSI Pullback into value zone (40 - 65)
       - 5m Volume Spike (Volume > 1.2x Volume SMA 20)
       - Price above 5m EMA 50
    3. Institutional Exits & Risk Protection:
       - Hard Stoploss: -4.0%
       - Trailing Stop: Activates at +2.5% profit, trails by 1.2%
       - Dynamic ROI Take-Profit Ladder
       - Custom Indicator Exit (EMA breakdown or RSI > 75 overbought reversal)
    """

    INTERFACE_VERSION = 3

    # Base Timeframe
    timeframe = "5m"
    informative_timeframe = "1h"

    # Spot (can_short = False) or Futures (can_short = True)
    can_short: bool = False

    # Startup candles to warm up 1h EMA 200 (200 * 12 = 2400 5m candles or 200 1h candles)
    startup_candle_count: int = 250

    # Process only new candles for speed and clean signals
    process_only_new_candles = True

    # -------------------------------------------------------------------------
    # Risk Management & ROI
    # -------------------------------------------------------------------------
    # Hard Stoploss (-4%)
    stoploss = -0.04

    # Trailing Stoploss (Locks in profit when price pumps)
    trailing_stop = True
    trailing_stop_positive = 0.012          # 1.2% trailing distance
    trailing_stop_positive_offset = 0.025   # Activates once +2.5% profit is reached
    trailing_only_offset_is_reached = True

    # Dynamic ROI Ladder (Takes profit automatically based on trade duration)
    minimal_roi = {
        "0": 0.05,       # 5.0% profit immediate exit
        "30": 0.03,      # 3.0% profit after 30 mins
        "60": 0.02,      # 2.0% profit after 60 mins
        "120": 0.012     # 1.2% profit after 2 hours
    }

    # Exit Signals
    use_exit_signal = True
    exit_profit_only = True                 # Exit signals trigger only if trade is in profit
    ignore_roi_if_entry_signal = False

    # Order Types
    order_types = {
        "entry": "limit",
        "exit": "limit",
        "emergency_exit": "market",
        "force_entry": "market",
        "force_exit": "market",
        "stoploss": "market",
        "stoploss_on_exchange": False
    }

    order_time_in_force = {
        "entry": "GTC",
        "exit": "GTC"
    }

    # Protections (Modern Freqtrade style)
    protections = [
        {
            "method": "CooldownPeriod",
            "stop_duration_candles": 5
        },
        {
            "method": "StoplossGuard",
            "lookback_period_candles": 60,
            "trade_limit": 2,
            "stop_duration_candles": 60,
            "only_per_pair": True
        },
        {
            "method": "MaxDrawdown",
            "lookback_period_candles": 240,
            "trade_limit": 4,
            "stop_duration_candles": 120,
            "max_allowed_drawdown": 0.10
        }
    ]

    # -------------------------------------------------------------------------
    # Hyperoptable Parameters (Can be tuned with 'freqtrade hyperopt')
    # -------------------------------------------------------------------------
    # Buy / Entry Parameters
    buy_rsi_min = IntParameter(30, 50, default=40, space="buy", optimize=True)
    buy_rsi_max = IntParameter(55, 75, default=65, space="buy", optimize=True)
    buy_volume_multiplier = DecimalParameter(1.0, 2.5, default=1.2, decimals=1, space="buy", optimize=True)
    buy_adx_1h_min = IntParameter(15, 35, default=20, space="buy", optimize=True)

    # Sell / Exit Parameters
    sell_rsi = IntParameter(70, 85, default=75, space="sell", optimize=True)

    # -------------------------------------------------------------------------
    # Informative Higher Timeframe (1 Hour) Indicators
    # -------------------------------------------------------------------------
    @informative("1h")
    def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Macro EMAs
        dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)
        dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200)
        
        # 1h RSI & ADX (Trend Strength)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)

        return dataframe

    # -------------------------------------------------------------------------
    # Base Timeframe (5 Min) Indicators
    # -------------------------------------------------------------------------
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Moving Averages (5m)
        dataframe["ema_9"] = ta.EMA(dataframe, timeperiod=9)
        dataframe["ema_21"] = ta.EMA(dataframe, timeperiod=21)
        dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)
        dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200)

        # Momentum & Oscillators
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
        
        # Volume Moving Average
        dataframe["volume_mean_20"] = dataframe["volume"].rolling(window=20).mean()

        # 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"]
        dataframe["bb_percent"] = (
            (dataframe["close"] - dataframe["bb_lowerband"]) /
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"])
        )

        # Average True Range (ATR) for volatility
        dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)

        return dataframe

    # -------------------------------------------------------------------------
    # Entry Signals (Long)
    # -------------------------------------------------------------------------
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []

        # 1. Macro 1h Trend Conditions (Market Alignment)
        conditions.append(dataframe["ema_50_1h"] > dataframe["ema_200_1h"])       # Macro Golden Cross
        conditions.append(dataframe["close_1h"] > dataframe["ema_200_1h"])        # Price above Macro 200 EMA
        conditions.append(dataframe["adx_1h"] >= self.buy_adx_1h_min.value)       # Macro Trend is strong

        # 2. Micro 5m Trend Conditions (Local Momentum)
        conditions.append(dataframe["ema_9"] > dataframe["ema_21"])               # Fast EMA cross
        conditions.append(dataframe["close"] > dataframe["ema_50"])               # Price above 50 EMA
        
        # 3. Pullback / Value Zone Trigger (Avoid buying overbought peaks)
        conditions.append(dataframe["rsi"] >= self.buy_rsi_min.value)
        conditions.append(dataframe["rsi"] <= self.buy_rsi_max.value)

        # 4. Volume Spike Confirmation (Institutional backing)
        conditions.append(
            dataframe["volume"] > (dataframe["volume_mean_20"] * self.buy_volume_multiplier.value)
        )

        # 5. Volume sanity check
        conditions.append(dataframe["volume"] > 0)

        if conditions:
            dataframe.loc[
                np.logical_and.reduce(conditions),
                "enter_long"
            ] = 1

        return dataframe

    # -------------------------------------------------------------------------
    # Exit Signals (Sell / Take-Profit / Trend Breakdown)
    # -------------------------------------------------------------------------
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []

        # 1. Overbought RSI Reversal Signal
        rsi_overbought = (dataframe["rsi"] > self.sell_rsi.value) & (
            qtpylib.crossed_below(dataframe["rsi"], self.sell_rsi.value)
        )

        # 2. Fast EMA Breakdown (EMA 9 crossed below EMA 21)
        ema_breakdown = qtpylib.crossed_below(dataframe["ema_9"], dataframe["ema_21"])

        conditions.append(rsi_overbought | ema_breakdown)
        conditions.append(dataframe["volume"] > 0)

        if conditions:
            dataframe.loc[
                np.logical_and.reduce(conditions),
                "exit_long"
            ] = 1

        return dataframe
