# source: https://raw.githubusercontent.com/kalebCard/MemeHunter/3fc6cb2e1a7fbb8cca56e6c66a0da411deab1027/user_data/strategies/MemeHunterFuturesV5_Ablation.py
# directory_url: https://github.com/kalebCard/MemeHunter/blob/main/user_data/strategies/
# User: kalebCard
# Repository: MemeHunter
# --------------------"""
Github_kalebCard_MemeHunter__MemeHunterFuturesV5_Ablation__20261013_014419 - Versión para prueba de ablación estricta (PB-61).
Elimina completamente la máscara de días de la semana y ventanas horarias
para evaluar el comportamiento estructural del motor puro sobre todos los días.
"""

from datetime import datetime
from typing import Optional
import logging

from freqtrade.strategy import IStrategy
from freqtrade.persistence import Trade
from pandas import DataFrame
import pandas_ta as ta
import numpy as np

LOG = logging.getLogger(__name__)


class Github_kalebCard_MemeHunter__MemeHunterFuturesV5_Ablation__20261013_014419(IStrategy):
    INTERFACE_VERSION = 3
    timeframe = '4h'
    can_short = True

    minimal_roi = {
        "0": 0.602,
        "1704": 0.141,
        "2255": 0.047,
        "7483": 0
    }

    stoploss = -0.10

    trailing_stop = True
    trailing_stop_positive = 0.040
    trailing_stop_positive_offset = 0.120
    trailing_only_offset_is_reached = True

    process_only_new_candles = True
    startup_candle_count: int = 200

    order_types = {
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'limit',
        'stoploss_on_exchange': False,
        'stoploss_on_exchange_interval': 60
    }

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

    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, entry_tag: Optional[str],
                 side: str, **kwargs) -> float:
        if entry_tag == 'short_trapped_auction':
            return 2.0
        elif entry_tag == 'short_structure_shift':
            return 1.75
        elif entry_tag == 'short_macro_breakdown':
            return 1.5
        return 1.5

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['ema_50'] = ta.ema(dataframe['close'], length=50)
        dataframe['ema_200'] = ta.ema(dataframe['close'], length=200)
        dataframe['dist_ema_50'] = (dataframe['close'] - dataframe['ema_50']) / dataframe['ema_50']

        dataframe['rsi'] = ta.rsi(dataframe['close'], length=14)

        macd = ta.macd(dataframe['close'], fast=12, slow=26, signal=9)
        dataframe['macd'] = macd['MACD_12_26_9']
        dataframe['macdsignal'] = macd['MACDs_12_26_9']
        dataframe['macdhist'] = macd['MACDh_12_26_9']

        dataframe['atr'] = ta.atr(dataframe['high'], dataframe['low'], dataframe['close'], length=14)

        # Teoría de subasta & mechas
        upper_wick = dataframe['high'] - dataframe[['close', 'open']].max(axis=1)
        candle_body = (dataframe['close'] - dataframe['open']).abs()
        candle_range = dataframe['high'] - dataframe['low']

        vol_sma20 = ta.sma(dataframe['volume'], length=20)
        vol_ratio = dataframe['volume'] / vol_sma20.replace(0, np.nan)

        is_wick_dominant = (upper_wick >= candle_body * 1.8) & (upper_wick >= candle_range * 0.45)
        is_volume_spike = vol_ratio >= 1.4
        is_closing_weak = dataframe['close'] <= (dataframe['low'] + candle_range * 0.35)

        dataframe['trapped_longs_signal'] = (is_wick_dominant & is_volume_spike & is_closing_weak).astype(int)

        # ABLACIÓN: seasonal_short_allowed siempre True (todos los días y horas)
        dataframe['seasonal_short_allowed'] = True

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[:, 'enter_long'] = 0
        dataframe.loc[:, 'enter_short'] = 0

        short_trapped_conditions = (
            (dataframe['seasonal_short_allowed']) &
            (dataframe['trapped_longs_signal'].rolling(window=3).max() == 1) &
            (dataframe['close'] < dataframe['low'].shift(1)) &
            (dataframe['macdhist'] < dataframe['macdhist'].shift(1)) &
            (dataframe['volume'] > 0)
        )
        dataframe.loc[short_trapped_conditions, 'enter_short'] = 1
        dataframe.loc[short_trapped_conditions, 'enter_tag'] = 'short_trapped_auction'

        short_structure_shift = (
            (dataframe['seasonal_short_allowed']) &
            (dataframe['dist_ema_50'].rolling(window=3).max() >= 0.12) &
            (dataframe['close'] < dataframe['low'].shift(1)) &
            (dataframe['rsi'] < 68) &
            (dataframe['rsi'].shift(1) >= 65) &
            (dataframe['macdhist'] < dataframe['macdhist'].shift(1)) &
            (dataframe['volume'] > 0)
        )
        dataframe.loc[short_structure_shift, 'enter_short'] = 1
        dataframe.loc[short_structure_shift, 'enter_tag'] = 'short_structure_shift'

        short_macro_conditions = (
            (dataframe['seasonal_short_allowed']) &
            (dataframe['close'] < dataframe['ema_200']) &
            (dataframe['macd'] < dataframe['macdsignal']) &
            (dataframe['macd'] < 0) &
            (dataframe['rsi'] <= 44) &
            (dataframe['rsi'] >= 22) &
            (dataframe['volume'] > 0)
        )
        dataframe.loc[short_macro_conditions, 'enter_short'] = 1
        dataframe.loc[short_macro_conditions, 'enter_tag'] = 'short_macro_breakdown'

        return dataframe

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

        exit_short = (
            (dataframe['close'] > dataframe['ema_200']) |
            ((dataframe['macd'] > dataframe['macdsignal']) & (dataframe['rsi'] > 58))
        )
        dataframe.loc[exit_short, 'exit_short'] = 1

        return dataframe
