# source: https://raw.githubusercontent.com/Hans1361/MyTrade56/c803e4a99796fa5c5e8f966ab2cdda7882317693/strategies_backup/ScalpingStrategy.py
# --- Do not remove these libs ---
import numpy as np
import pandas as pd
from pandas import DataFrame
from datetime import datetime
from typing import Optional, Union

from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,
                                IStrategy, IntParameter)

# --------------------------------
# Add your lib to import here
import talib.abstract as ta
import pandas_ta as pta
from freqtrade.persistence import Trade
from freqtrade.exchange import timeframe_to_minutes
import logging
from freqtrade.strategy import stoploss_from_open

logger = logging.getLogger(__name__)


class Github_Hans1361_MyTrade56__ScalpingStrategy__20250620_122734(IStrategy):
    """
    Scalping Strategy for High Volume Coins
    
    This strategy is designed for quick trades on high volume cryptocurrencies.
    It uses multiple technical indicators to identify short-term momentum and
    volatility opportunities for scalping profits.
    
    Key Features:
    - RSI for oversold/overbought conditions
    - MACD for momentum confirmation
    - Bollinger Bands for volatility and price levels
    - Volume analysis for confirmation
    - Quick profit taking and stop loss
    """
    
    # Strategy interface version - allow new iterations of the strategy interface.
    # Check the documentation or the Sample strategy to get the latest version.
    INTERFACE_VERSION = 3

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi".
    minimal_roi = {
        "0": 0.01,    # 1% profit target for quick scalping
        "5": 0.008,   # 0.8% after 5 minutes
        "10": 0.006,  # 0.6% after 10 minutes
        "15": 0.004,  # 0.4% after 15 minutes
        "20": 0.002,  # 0.2% after 20 minutes
        "30": 0.001   # 0.1% after 30 minutes
    }

    # Optimal stoploss designed for the strategy.
    # This attribute will be overridden if the config file contains "stoploss".
    stoploss = -0.015  # 1.5% stop loss for scalping

    # Trailing stoploss
    trailing_stop = True
    trailing_stop_positive = 0.005  # 0.5% trailing stop
    trailing_stop_positive_offset = 0.01  # 1% offset
    trailing_only_offset_is_reached = True

    # Optimal timeframe for the strategy.
    timeframe = '5m'  # 5-minute timeframe for scalping

    # Run "populate_any_indicators()" only for new candle.
    process_only_new_candles = True

    # These values can be overridden in the config.
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 30

    # Strategy parameters
    buy_rsi = IntParameter(20, 40, default=30, space="buy", decimals=0, optimize=True)
    sell_rsi = IntParameter(60, 80, default=70, space="sell", decimals=0, optimize=True)
    
    # MACD parameters
    fast_macd = IntParameter(8, 16, default=12, space="buy", decimals=0, optimize=True)
    slow_macd = IntParameter(20, 30, default=26, space="buy", decimals=0, optimize=True)
    signal_macd = IntParameter(6, 12, default=9, space="buy", decimals=0, optimize=True)
    
    # Bollinger Bands parameters
    bb_period = IntParameter(15, 25, default=20, space="buy", decimals=0, optimize=True)
    bb_std = DecimalParameter(1.5, 2.5, default=2.0, space="buy", decimals=1, optimize=True)
    
    # Volume parameters
    volume_threshold = DecimalParameter(1.2, 2.0, default=1.5, space="buy", decimals=1, optimize=True)
    
    # Position sizing
    position_adjustment_enable = True
    max_entry_position_adjustment = 3

    def populate_any_indicators(self, pair: str, df: DataFrame, tf: str) -> DataFrame:
        """
        Generate additional indicators for the given pair and timeframe.
        """
        if self.dp:
            if (self.dp.runmode.value in ('live', 'dry_run')):
                # Get the informative timeframe
                informative = self.dp.get_pair_dataframe(pair=pair, timeframe=tf)
            else:
                # Get the informative timeframe
                informative = self.dp.get_pair_dataframe(pair=pair, timeframe=tf)

            # RSI
            df['rsi'] = ta.RSI(df, timeperiod=14)
            
            # MACD
            macd = ta.MACD(df, 
                          fastperiod=self.fast_macd.value, 
                          slowperiod=self.slow_macd.value, 
                          signalperiod=self.signal_macd.value)
            df['macd'] = macd['macd']
            df['macdsignal'] = macd['macdsignal']
            df['macdhist'] = macd['macdhist']
            
            # Bollinger Bands
            bollinger = ta.BBANDS(df, 
                                 timeperiod=self.bb_period.value, 
                                 nbdevup=self.bb_std.value, 
                                 nbdevdn=self.bb_std.value, 
                                 matype=0)
            df['bb_lowerband'] = bollinger['lowerband']
            df['bb_middleband'] = bollinger['middleband']
            df['bb_upperband'] = bollinger['upperband']
            df['bb_percent'] = (df['close'] - df['bb_lowerband']) / (df['bb_upperband'] - df['bb_lowerband'])
            
            # Volume indicators
            df['volume_sma'] = ta.SMA(df['volume'], timeperiod=20)
            df['volume_ratio'] = df['volume'] / df['volume_sma']
            
            # Price momentum
            df['price_change'] = df['close'].pct_change()
            df['price_change_3'] = df['close'].pct_change(3)
            
            # Volatility
            df['atr'] = ta.ATR(df, timeperiod=14)
            df['atr_percent'] = df['atr'] / df['close']
            
            # EMA for trend direction
            df['ema_9'] = ta.EMA(df, timeperiod=9)
            df['ema_21'] = ta.EMA(df, timeperiod=21)
            df['ema_trend'] = np.where(df['ema_9'] > df['ema_21'], 1, -1)
            
            # Stochastic RSI
            stoch_rsi = ta.STOCHRSI(df, timeperiod=14, fastk_period=5, fastd_period=3)
            df['stoch_rsi_k'] = stoch_rsi['fastk']
            df['stoch_rsi_d'] = stoch_rsi['fastd']
            
            # Williams %R
            df['williams_r'] = ta.WILLR(df, timeperiod=14)
            
            # CCI (Commodity Channel Index)
            df['cci'] = ta.CCI(df, timeperiod=14)
            
            # ADX for trend strength
            adx = ta.ADX(df, timeperiod=14)
            df['adx'] = adx['adx']
            df['plus_di'] = adx['plus_di']
            df['minus_di'] = adx['minus_di']

        return df

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Generate indicators for the given pair and timeframe.
        """
        return self.populate_any_indicators(metadata['pair'], dataframe, self.timeframe)

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the entry signal for the given dataframe
        :param dataframe: DataFrame
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with entry columns populated
        """
        dataframe.loc[
            (
                # RSI oversold condition
                (dataframe['rsi'] < self.buy_rsi.value) &
                
                # MACD bullish crossover or positive histogram
                ((dataframe['macd'] > dataframe['macdsignal']) | (dataframe['macdhist'] > 0)) &
                
                # Price near lower Bollinger Band (potential bounce)
                (dataframe['bb_percent'] < 0.3) &
                
                # Volume confirmation
                (dataframe['volume_ratio'] > self.volume_threshold.value) &
                
                # Positive price momentum
                (dataframe['price_change'] > 0) &
                
                # Trend is bullish or neutral
                (dataframe['ema_trend'] >= 0) &
                
                # Stochastic RSI oversold
                (dataframe['stoch_rsi_k'] < 20) &
                
                # Williams %R oversold
                (dataframe['williams_r'] < -80) &
                
                # CCI oversold
                (dataframe['cci'] < -100) &
                
                # ADX shows some trend strength
                (dataframe['adx'] > 20) &
                
                # Plus DI above Minus DI (bullish)
                (dataframe['plus_di'] > dataframe['minus_di']) &
                
                # Reasonable volatility
                (dataframe['atr_percent'] > 0.005) &
                (dataframe['atr_percent'] < 0.05)
            ),
            'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the exit signal for the given dataframe
        :param dataframe: DataFrame
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with exit columns populated
        """
        dataframe.loc[
            (
                # RSI overbought condition
                (dataframe['rsi'] > self.sell_rsi.value) |
                
                # MACD bearish crossover
                (dataframe['macd'] < dataframe['macdsignal']) |
                
                # Price near upper Bollinger Band
                (dataframe['bb_percent'] > 0.8) |
                
                # Negative price momentum
                (dataframe['price_change'] < -0.005) |
                
                # Trend turned bearish
                (dataframe['ema_trend'] < 0) |
                
                # Stochastic RSI overbought
                (dataframe['stoch_rsi_k'] > 80) |
                
                # Williams %R overbought
                (dataframe['williams_r'] > -20) |
                
                # CCI overbought
                (dataframe['cci'] > 100) |
                
                # Plus DI below Minus DI (bearish)
                (dataframe['plus_di'] < dataframe['minus_di'])
            ),
            'exit_long'] = 1

        return dataframe

    def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:
        """
        Custom stoploss logic, returning the new distance relative to current_rate
        (as ratio).
        """
        # If we have a profit, use trailing stop
        if current_profit > 0.005:  # 0.5% profit
            return 0.005  # 0.5% trailing stop
        
        # Otherwise use the default stoploss
        return 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: Optional[str],
                           side: str, **kwargs) -> bool:
        """
        Additional confirmation for trade entry.
        """
        # Get the dataframe for the pair
        df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = df.iloc[-1].squeeze()
        
        # Additional confirmation checks
        if side == 'long':
            # Check if volume is still high
            if last_candle['volume_ratio'] < 1.2:
                return False
            
            # Check if volatility is reasonable
            if last_candle['atr_percent'] > 0.08:
                return False
            
            # Check if we're not in a strong downtrend
            if last_candle['ema_trend'] < 0 and last_candle['adx'] > 25:
                return False
        
        return True

    def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
                          entry_tag: Optional[str], side: str, **kwargs) -> float:
        """
        Custom entry price logic.
        """
        # For scalping, we want to enter at market price to ensure quick execution
        return proposed_rate

    def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
                   current_profit: float, **kwargs) -> Optional[str]:
        """
        Custom exit logic.
        """
        # Get the dataframe for the pair
        df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = df.iloc[-1].squeeze()
        
        # Exit if we have a quick profit and momentum is fading
        if current_profit > 0.008 and last_candle['rsi'] > 65:
            return "quick_profit_exit"
        
        # Exit if we have a loss and trend is turning bearish
        if current_profit < -0.005 and last_candle['ema_trend'] < 0:
            return "trend_reversal_exit"
        
        # Exit if volatility becomes too high
        if last_candle['atr_percent'] > 0.1:
            return "high_volatility_exit"
        
        return None

    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:
        """
        Custom leverage logic.
        """
        # For spot trading, leverage is always 1
        return 1.0 