# source: https://raw.githubusercontent.com/tsacekay2000-cameron/github-trading/c908c94de04819da38e20b2c92937fc921fae207/user_data/strategies/MeanReversionStrategy.py
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
import talib.abstract as ta
from freqtrade.persistence import Trade
from datetime import datetime


class Github_tsacekay2000_cameron_github_trading__MeanReversionStrategy__20260116_045654(IStrategy):
    """
    Mean reversion strategy that buys oversold conditions 
    and sells overbought conditions in ranging markets.
    """
    
    minimal_roi = {
        "0": 0.06,
        "10": 0.03,
        "20": 0.02,
        "40": 0.01,
    }
    
    stoploss = -0.05
    
    timeframe = '5m'
    
    trailing_stop = False
    
    can_short: bool = False
    
    # Hyperparameters (optimized via hyperopt)
    buy_rsi = IntParameter(15, 30, default=21, space='buy')
    sell_rsi = IntParameter(70, 85, default=78, space='sell')
    buy_bb_width = DecimalParameter(0.015, 0.04, default=0.02, space='buy')
    cci_buy = IntParameter(-150, -80, default=-101, space='buy')
    adx_max = IntParameter(15, 30, default=28, space='buy')  # Max ADX for ranging market
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Indicators for mean reversion.
        """
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        
        # Bollinger Bands
        bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
        dataframe['bb_lower'] = bollinger['lowerband']
        dataframe['bb_middle'] = bollinger['middleband']
        dataframe['bb_upper'] = bollinger['upperband']
        
        # BB Width (for volatility)
        dataframe['bb_width'] = (
            (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_middle']
        )
        
        # Price position in BB
        dataframe['bb_percent'] = (
            (dataframe['close'] - dataframe['bb_lower']) / 
            (dataframe['bb_upper'] - dataframe['bb_lower'])
        )
        
        # Moving averages
        dataframe['sma_20'] = ta.SMA(dataframe, timeperiod=20)
        dataframe['sma_50'] = ta.SMA(dataframe, timeperiod=50)
        dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)
        
        # Trend detection - price vs 200 EMA
        dataframe['trend'] = (dataframe['close'] - dataframe['ema_200']) / dataframe['ema_200']
        
        # Stochastic
        stoch = ta.STOCH(dataframe)
        dataframe['slowk'] = stoch['slowk']
        dataframe['slowd'] = stoch['slowd']
        
        # Williams %R
        dataframe['willr'] = ta.WILLR(dataframe, timeperiod=14)
        
        # CCI (Commodity Channel Index)
        dataframe['cci'] = ta.CCI(dataframe, timeperiod=20)
        
        # Volume
        dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean()
        
        # ATR for volatility
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        
        # ADX for trend strength (lower = ranging = better for mean reversion)
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)
        
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Buy on oversold conditions with mean reversion signals.
        Only in ranging or slight uptrend markets.
        """
        dataframe.loc[
            (
                # Multiple oversold indicators - more extreme
                (dataframe['rsi'] < self.buy_rsi.value) &
                (dataframe['slowk'] < 15) &
                (dataframe['slowd'] < 15) &
                (dataframe['willr'] < -85) &
                (dataframe['cci'] < self.cci_buy.value) &
                
                # Price near lower BB - more extreme
                (dataframe['bb_percent'] < 0.1) &
                
                # Not in high volatility (better for mean reversion)
                (dataframe['bb_width'] < self.buy_bb_width.value) &
                
                # Price below moving average (oversold)
                (dataframe['close'] < dataframe['sma_20']) &
                
                # Trend filter: avoid strong downtrends
                (dataframe['trend'] > -0.15) &  # Not more than 15% below 200 EMA
                
                # ADX filter: only ranging markets (mean reversion works in ranging)
                (dataframe['adx'] < self.adx_max.value) &
                
                # Volume confirmation - stronger requirement
                (dataframe['volume'] > dataframe['volume_mean'] * 1.2) &
                
                # Stochastic oversold cross
                (dataframe['slowk'] > dataframe['slowd']) &  # Starting to turn up
                
                (dataframe['volume'] > 0)
            ),
            'enter_long'] = 1
        
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Exit ONLY on strong overbought - let ROI handle most exits.
        Be very conservative to lock in profits.
        """
        dataframe.loc[
            (
                # Only exit on VERY strong overbought conditions
                (
                    (dataframe['rsi'] > 80) &
                    (dataframe['slowk'] > 85) &
                    (dataframe['bb_percent'] > 0.95)
                ) &
                
                (dataframe['volume'] > 0)
            ),
            'exit_long'] = 1
        
        return dataframe
