# CiccioStrategyMTF - Multi-Timeframe Strategy
# Entry su 15m con conferma trend su 1h

from freqtrade.strategy import IStrategy, IntParameter, merge_informative_pair
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class CiccioStrategyMTF(IStrategy):
    """
    CiccioStrategy Multi-Timeframe

    Logica:
    - Timeframe principale: 15m (entry/exit veloci)
    - Timeframe conferma: 1h (trend direction)

    Entry (15m):
    - RSI < 35 (oversold)
    - MACD histogram positivo (momentum in ripresa)
    - Prezzo sotto BB middle
    - CONFERMA 1h: prezzo sopra EMA50 O RSI 1h < 40

    Exit (15m):
    - RSI > 65 (overbought)
    - O trailing stop
    - O ROI target
    """

    INTERFACE_VERSION = 3

    # Timeframe principale: 15 minuti
    timeframe = '15m'

    # Informative timeframe: 1 ora
    informative_timeframe = '1h'

    # Candele necessarie per calcolare indicatori
    startup_candle_count = 50

    # ROI - più aggressivo su 15m
    minimal_roi = {
        "0": 0.08,      # 8% subito
        "30": 0.04,     # 4% dopo 30 min
        "60": 0.02,     # 2% dopo 1 ora
        "120": 0.01     # 1% dopo 2 ore
    }

    # Stop loss
    stoploss = -0.04  # 4% (più stretto su 15m)

    # Trailing stop
    trailing_stop = True
    trailing_stop_positive = 0.015
    trailing_stop_positive_offset = 0.025
    trailing_only_offset_is_reached = True

    # Ordini
    order_types = {
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    # Protections
    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 2
            },
            {
                "method": "MaxDrawdown",
                "trade_limit": 20,
                "lookback_period_candles": 48,
                "stop_duration_candles": 8,
                "max_allowed_drawdown": 0.15
            },
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 3,
                "stop_duration_candles": 8,
                "only_per_pair": False
            }
        ]

    # Parametri ottimizzabili
    buy_rsi_15m = IntParameter(25, 40, default=35, space='buy')
    buy_rsi_1h = IntParameter(30, 50, default=45, space='buy')
    sell_rsi = IntParameter(60, 80, default=65, space='sell')

    def informative_pairs(self):
        """
        Definisce le coppie informative (1h) per ogni pair tradato
        """
        pairs = self.dp.current_whitelist()
        informative_pairs = [(pair, self.informative_timeframe) for pair in pairs]
        return informative_pairs

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Calcola indicatori su 15m
        """
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)

        # MACD
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']

        # 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']

        # EMA
        dataframe['ema9'] = ta.EMA(dataframe, timeperiod=9)
        dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)

        # Volume
        dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean()

        # Stochastic RSI
        stoch = ta.STOCH(dataframe)
        dataframe['slowk'] = stoch['slowk']
        dataframe['slowd'] = stoch['slowd']

        # === INFORMATIVE 1H ===
        if self.dp:
            informative_1h = self.dp.get_pair_dataframe(
                pair=metadata['pair'],
                timeframe=self.informative_timeframe
            )

            if not informative_1h.empty:
                # Indicatori 1h
                informative_1h['rsi_1h'] = ta.RSI(informative_1h, timeperiod=14)
                informative_1h['ema50_1h'] = ta.EMA(informative_1h, timeperiod=50)
                informative_1h['ema200_1h'] = ta.EMA(informative_1h, timeperiod=200)

                # MACD 1h
                macd_1h = ta.MACD(informative_1h)
                informative_1h['macd_1h'] = macd_1h['macd']
                informative_1h['macdsignal_1h'] = macd_1h['macdsignal']

                # Trend 1h
                informative_1h['trend_1h'] = (
                    (informative_1h['close'] > informative_1h['ema50_1h']).astype(int)
                )

                # Merge con dataframe principale
                dataframe = merge_informative_pair(
                    dataframe, informative_1h,
                    self.timeframe, self.informative_timeframe,
                    ffill=True
                )

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Entry su 15m con conferma 1h

        Condizioni 15m:
        - RSI < 35 (oversold)
        - MACD histogram > 0 O in crescita (momentum)
        - Prezzo < BB middle

        Conferma 1h (almeno una):
        - RSI 1h < 45 (non overbought)
        - Prezzo sopra EMA50 1h (trend ok)
        """

        # Colonne 1h potrebbero non esistere se merge fallisce
        has_1h_data = 'rsi_1h_1h' in dataframe.columns

        if has_1h_data:
            dataframe.loc[
                (
                    # === 15m CONDITIONS ===
                    # RSI oversold
                    (dataframe['rsi'] < self.buy_rsi_15m.value) &

                    # MACD momentum positivo o in ripresa
                    (
                        (dataframe['macdhist'] > 0) |
                        (dataframe['macdhist'] > dataframe['macdhist'].shift(1))
                    ) &

                    # Prezzo sotto BB middle (spazio per salire)
                    (dataframe['close'] < dataframe['bb_middleband']) &

                    # Volume presente
                    (dataframe['volume'] > dataframe['volume_mean'] * 0.5) &

                    # === 1H CONFIRMATION ===
                    (
                        # RSI 1h non overbought
                        (dataframe['rsi_1h_1h'] < self.buy_rsi_1h.value) |
                        # O trend 1h positivo
                        (dataframe['trend_1h_1h'] == 1)
                    ) &

                    # Volume presente
                    (dataframe['volume'] > 0)
                ),
                'enter_long'] = 1
        else:
            # Fallback senza dati 1h
            dataframe.loc[
                (
                    (dataframe['rsi'] < self.buy_rsi_15m.value) &
                    (dataframe['macdhist'] > dataframe['macdhist'].shift(1)) &
                    (dataframe['close'] < dataframe['bb_middleband']) &
                    (dataframe['volume'] > 0)
                ),
                'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Exit su 15m

        Condizioni:
        - RSI > 65 (overbought)
        - MACD bearish
        """
        dataframe.loc[
            (
                # RSI overbought
                (dataframe['rsi'] > self.sell_rsi.value) &

                # MACD bearish
                (dataframe['macd'] < dataframe['macdsignal']) &

                # Volume presente
                (dataframe['volume'] > 0)
            ),
            'exit_long'] = 1

        return dataframe

    def leverage(self, pair: str, current_time, current_rate: float,
                 proposed_leverage: float, max_leverage: float, entry_tag: str,
                 side: str, **kwargs) -> float:
        """
        No leverage per spot
        """
        return 1.0
