# source: https://raw.githubusercontent.com/Aidenkopec/crypto-bot-trading/d07ebc99e97d393d0453e6a45486cdcf61d8905c/user_data/strategies/ETHMomentumStrategy.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
# --- 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 (
    IStrategy,
    Trade,
    Order,
    PairLocks,
    informative,
    merge_informative_pair,
    stoploss_from_open,
    stoploss_from_absolute,
)

# --------------------------------
# Add your lib to import here
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class Github_Aidenkopec_crypto_bot_trading__ETHMomentumStrategy__20250730_214930(IStrategy):
    """
    ETH-optimized momentum trading strategy for ETH/USD on Kraken.
    
    Designed for Ethereum's 15.63% volatility and explosive move patterns.
    Uses 1h primary timeframe with 4h trend context for momentum plays.
    
    Target: 5-8% profit moves, 60-70% win rate
    Based on research from Strategy Analysis Report.
    """

    # Strategy interface version
    INTERFACE_VERSION = 3

    # ETH-optimized timeframe (1h primary for momentum)
    timeframe = '1h'
    
    # Use 4h for trend context
    informative_timeframe = '4h'

    # Can this strategy go short?
    can_short = False

    # ETH-specific ROI targeting 5-8% moves (higher volatility)
    minimal_roi = {
        "0": 0.09,      # 9% aggressive target (ETH's explosive potential)
        "30": 0.07,     # 7% after 30 minutes (momentum target)
        "60": 0.05,     # 5% after 1 hour (minimum for ETH)
        "120": 0.01     # 1% after 2 hours (safety exit)
    }

    # ETH-optimized stoploss (wider due to higher volatility)
    stoploss = -0.03  # 3% maximum loss (ETH's bigger swings)

    # Trailing stoploss for momentum plays
    trailing_stop = False

    # ETH-specific parameters
    # Bollinger Band breakout parameters
    bb_period = 20
    bb_std = 2
    bb_breakout_threshold = 0.02  # 2% above upper band
    
    # RSI parameters for momentum
    rsi_period = 14
    rsi_momentum_threshold = 50  # Higher for momentum entry
    rsi_exit_threshold = 75      # Higher exit for ETH volatility
    
    # Volume surge parameters (key for ETH momentum)
    volume_surge_multiplier = 2.0  # 2x average volume
    volume_sma_period = 20
    
    # MACD parameters for momentum confirmation
    macd_fast = 12
    macd_slow = 26
    macd_signal = 9

    def informative_pairs(self):
        """
        Define additional informative pairs for 4h trend context
        """
        pairs = self.dp.current_whitelist()
        informative_pairs = []
        for pair in pairs:
            informative_pairs.append((pair, self.informative_timeframe))
        return informative_pairs

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Populate indicators optimized for ETH momentum trading
        """
        
        # Bollinger Bands (primary for breakout detection)
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=self.bb_period, stds=self.bb_std)
        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'])
        )
        dataframe['bb_width'] = (
            (dataframe['bb_upperband'] - dataframe['bb_lowerband']) / dataframe['bb_middleband']
        )
        
        # Bollinger Band breakout signals
        dataframe['bb_breakout_up'] = dataframe['close'] > (dataframe['bb_upperband'] * (1 + self.bb_breakout_threshold))
        dataframe['bb_squeeze'] = dataframe['bb_width'] < 0.03  # Low volatility squeeze
        
        # RSI for momentum
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.rsi_period)
        dataframe['rsi_momentum'] = dataframe['rsi'] > self.rsi_momentum_threshold
        
        # MACD for momentum confirmation
        macd = ta.MACD(dataframe, fastperiod=self.macd_fast, slowperiod=self.macd_slow, signalperiod=self.macd_signal)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']
        dataframe['macd_momentum'] = (dataframe['macd'] > dataframe['macdsignal']) & (dataframe['macdhist'] > 0)
        
        # Volume surge detection (critical for ETH momentum)
        dataframe['volume_sma'] = ta.SMA(dataframe['volume'], timeperiod=self.volume_sma_period)
        dataframe['volume_surge'] = dataframe['volume'] > (dataframe['volume_sma'] * self.volume_surge_multiplier)
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_sma']
        
        # EMA for trend direction
        dataframe['ema_fast'] = ta.EMA(dataframe, timeperiod=12)
        dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=26)
        dataframe['ema_trend'] = dataframe['ema_fast'] > dataframe['ema_slow']
        
        # ATR for volatility measurement
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['atr_high'] = dataframe['atr'] > dataframe['atr'].rolling(window=20).mean() * 1.2
        
        # Momentum oscillator
        dataframe['momentum'] = ta.MOM(dataframe, timeperiod=10)
        dataframe['momentum_positive'] = dataframe['momentum'] > 0
        
        # Price velocity (rate of change)
        dataframe['roc'] = ta.ROC(dataframe, timeperiod=5)
        dataframe['roc_strong'] = dataframe['roc'] > 2  # 2% price velocity
        
        return dataframe

    @informative('4h')
    def populate_indicators_4h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        4h timeframe indicators for overall trend context
        """
        # Trend context from 4h
        dataframe['ema_trend_4h'] = ta.EMA(dataframe, timeperiod=20) > ta.EMA(dataframe, timeperiod=50)
        dataframe['rsi_4h'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['macd_4h'] = ta.MACD(dataframe)['macd']
        dataframe['macdsignal_4h'] = ta.MACD(dataframe)['macdsignal']
        dataframe['trend_strength_4h'] = dataframe['macd_4h'] > dataframe['macdsignal_4h']
        
        # 4h volume context
        dataframe['volume_trend_4h'] = dataframe['volume'] > ta.SMA(dataframe['volume'], timeperiod=10)
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        ETH-optimized entry conditions for momentum breakouts (5-8% moves)
        
        Entry Logic:
        1. Bollinger Band breakout with volume surge
        2. RSI momentum confirmation
        3. MACD bullish momentum
        4. 4h trend alignment
        5. High volatility environment (ATR)
        6. Strong price velocity
        """
        
        conditions = []
        
        # Primary momentum signal: BB breakout with volume
        conditions.append(
            (dataframe['bb_breakout_up'] | (dataframe['bb_percent'] > 0.8)) &
            dataframe['volume_surge']
        )
        
        # RSI momentum confirmation (not oversold, but building momentum)
        conditions.append(
            dataframe['rsi_momentum'] &
            (dataframe['rsi'] < 80)  # Not extremely overbought
        )
        
        # MACD momentum confirmation
        conditions.append(dataframe['macd_momentum'])
        
        # 4h trend context (don't fight the trend)
        conditions.append(dataframe['ema_trend_4h'])
        conditions.append(dataframe['trend_strength_4h'])
        
        # High volatility environment (momentum needs volatility)
        conditions.append(dataframe['atr_high'])
        
        # EMA trend alignment on 1h
        conditions.append(dataframe['ema_trend'])
        
        # Strong momentum indicators
        conditions.append(dataframe['momentum_positive'])
        conditions.append(dataframe['roc_strong'])
        
        # Volume confirmation (institutional/whale activity)
        conditions.append(dataframe['volume_ratio'] > 1.5)
        
        # Not in a BB squeeze (need volatility expansion)
        conditions.append(~dataframe['bb_squeeze'])
        
        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        ETH-optimized exit conditions for momentum exhaustion
        
        Exit Logic:
        1. RSI extremely overbought
        2. MACD momentum declining
        3. Volume declining (momentum fading)
        4. Bollinger Band extreme stretch
        5. Momentum indicators weakening
        """
        
        conditions = []
        
        # RSI extremely overbought (ETH-specific high threshold)
        conditions.append(dataframe['rsi'] > self.rsi_exit_threshold)
        
        # MACD momentum declining
        conditions.append(
            (dataframe['macd'] < dataframe['macdsignal']) |
            (dataframe['macdhist'] < dataframe['macdhist'].shift(1))  # Declining histogram
        )
        
        # Volume declining (momentum fading)
        conditions.append(dataframe['volume_ratio'] < 0.7)
        
        # Bollinger Band extreme stretch (overextended)
        conditions.append(dataframe['bb_percent'] > 1.1)  # Above upper band
        
        # Momentum weakening
        conditions.append(~dataframe['momentum_positive'])
        
        # Price velocity declining
        conditions.append(dataframe['roc'] < 0)
        
        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'exit_long'] = 1

        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:
        """
        ETH-optimized stoploss logic (wider for volatility)
        """
        return self.stoploss

    def custom_sell(self, pair: str, trade: 'Trade', current_time: datetime,
                   current_rate: float, current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
        """
        ETH-optimized profit-taking logic
        
        Scale-out approach: 5%, 7%, 9% targets (higher for ETH volatility)
        """
        
        # Scale out at 9% (aggressive target for ETH momentum)
        if current_profit >= 0.09:
            return 'eth_profit_9pct'
            
        # Scale out at 7% (momentum target)
        if current_profit >= 0.07:
            return 'eth_profit_7pct'
            
        # Consider exit at 5% minimum if momentum weakening
        if current_profit >= 0.05:
            return None  # Let normal exit signals handle this
            
        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:
        """
        No leverage for spot trading on Kraken
        """
        return 1.0


def reduce(function, iterable, initializer=None):
    """Python reduce function for combining conditions"""
    it = iter(iterable)
    if initializer is None:
        value = next(it)
    else:
        value = initializer
    for element in it:
        value = function(value, element)
    return value