# source: https://raw.githubusercontent.com/Aidenkopec/crypto-bot-trading/d07ebc99e97d393d0453e6a45486cdcf61d8905c/user_data/strategies/BTCSwingStrategy.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__BTCSwingStrategy__20250730_214930(IStrategy):
    """
    BTC-optimized swing trading strategy for BTC/USD on Kraken.
    
    Designed for Bitcoin's 7.55% volatility and steady trend patterns.
    Uses 4h primary timeframe with 1h entry timing for precision.
    
    Target: 4-6% profit moves, 65%+ win rate
    Based on research from Strategy Analysis Report.
    """

    # Strategy interface version
    INTERFACE_VERSION = 3

    # BTC-optimized timeframe (4h primary for trend identification)
    timeframe = '4h'
    
    # Use 1h for entry timing precision
    informative_timeframe = '1h'

    # Can this strategy go short?
    can_short = False

    # BTC-specific ROI targeting 4-6% moves with scale-out approach
    minimal_roi = {
        "0": 0.08,      # 8% aggressive target
        "60": 0.06,     # 6% after 4 hours (optimal target) 
        "120": 0.04,    # 4% after 8 hours (minimum for fees)
        "240": 0.01     # 1% after 16 hours (safety exit)
    }

    # BTC-optimized stoploss (tighter due to lower volatility)
    stoploss = -0.025  # 2.5% maximum loss (BTC's steadier patterns)

    # Trailing stoploss
    trailing_stop = False

    # BTC-specific parameters
    # Trend confirmation parameters
    ema_short_period = 20
    ema_long_period = 50
    
    # RSI parameters for BTC's momentum patterns
    rsi_period = 14
    rsi_buy_threshold = 40  # Less oversold for BTC
    rsi_sell_threshold = 65  # Earlier exit for BTC
    
    # Volume confirmation
    volume_sma_period = 20
    
    # ADX trend strength
    adx_min_strength = 25

    def informative_pairs(self):
        """
        Define additional informative pairs for 1h entry timing
        """
        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, metadata):
        """
        Populate indicators optimized for BTC swing trading
        """
        
        # EMA trend indicators (primary for BTC)
        dataframe['ema_short'] = ta.EMA(dataframe, timeperiod=self.ema_short_period)
        dataframe['ema_long'] = ta.EMA(dataframe, timeperiod=self.ema_long_period)
        dataframe['ema_trend'] = dataframe['ema_short'] > dataframe['ema_long']
        
        # RSI for momentum
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.rsi_period)
        
        # Bollinger Bands for volatility
        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']
        dataframe['bb_percent'] = (
            (dataframe['close'] - dataframe['bb_lowerband']) /
            (dataframe['bb_upperband'] - dataframe['bb_lowerband'])
        )
        
        # MACD for trend confirmation
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']
        
        # Volume indicators
        dataframe['volume_sma'] = ta.SMA(dataframe['volume'], timeperiod=self.volume_sma_period)
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_sma']
        
        # ADX for trend strength
        dataframe['adx'] = ta.ADX(dataframe)
        
        # Support/Resistance levels (simple approach)
        dataframe['resistance'] = dataframe['high'].rolling(window=10).max()
        dataframe['support'] = dataframe['low'].rolling(window=10).min()
        
        # Price position relative to support/resistance
        dataframe['price_position'] = (dataframe['close'] - dataframe['support']) / (dataframe['resistance'] - dataframe['support'])
        
        return dataframe

    @informative('1h')
    def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        1h timeframe indicators for precise entry timing
        """
        # Fast RSI for entry timing
        dataframe['rsi_1h'] = ta.RSI(dataframe, timeperiod=14)
        
        # Fast EMA for immediate trend
        dataframe['ema_fast_1h'] = ta.EMA(dataframe, timeperiod=12)
        dataframe['ema_slow_1h'] = ta.EMA(dataframe, timeperiod=26)
        
        # Volume surge detection
        dataframe['volume_sma_1h'] = ta.SMA(dataframe['volume'], timeperiod=20)
        dataframe['volume_surge_1h'] = dataframe['volume'] > dataframe['volume_sma_1h'] * 1.5
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        BTC-optimized entry conditions for 4-6% swing moves
        
        Entry Logic:
        1. 4h trend confirmation (EMA alignment)
        2. RSI recovery from oversold (but not too deep)
        3. MACD bullish alignment
        4. Volume confirmation
        5. 1h timing confirmation
        6. Support/resistance positioning
        """
        
        conditions = []
        
        # Primary trend confirmation (4h timeframe)
        conditions.append(dataframe['ema_trend'])  # Short EMA > Long EMA
        
        # RSI in favorable zone (not too oversold for BTC)
        conditions.append(
            (dataframe['rsi'] > self.rsi_buy_threshold) &
            (dataframe['rsi'] < 60)  # Not overbought
        )
        
        # MACD bullish alignment
        conditions.append(
            (dataframe['macd'] > dataframe['macdsignal']) &
            (dataframe['macdhist'] > 0)
        )
        
        # Volume confirmation (institutional interest)
        conditions.append(dataframe['volume_ratio'] > 1.2)
        
        # ADX shows trend strength
        conditions.append(dataframe['adx'] > self.adx_min_strength)
        
        # Price position favorable (not at resistance)
        conditions.append(dataframe['price_position'] < 0.8)
        
        # 1h timeframe confirmation for entry timing
        conditions.append(dataframe['rsi_1h'] > 35)  # 1h RSI recovery
        conditions.append(dataframe['ema_fast_1h'] > dataframe['ema_slow_1h'])  # 1h trend aligned
        
        # Bollinger Band positioning (near lower band for entry)
        conditions.append(dataframe['bb_percent'] < 0.3)
        
        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:
        """
        BTC-optimized exit conditions for profit-taking
        
        Exit Logic:
        1. RSI overbought (momentum exhausted)
        2. MACD bearish divergence
        3. Volume declining
        4. Price at upper Bollinger Band
        5. 1h timeframe shows weakness
        """
        
        conditions = []
        
        # RSI overbought (BTC-specific threshold)
        conditions.append(dataframe['rsi'] > self.rsi_sell_threshold)
        
        # MACD bearish signal
        conditions.append(
            (dataframe['macd'] < dataframe['macdsignal']) |
            (dataframe['macdhist'] < 0)
        )
        
        # Volume declining (profit-taking phase)
        conditions.append(dataframe['volume_ratio'] < 0.8)
        
        # Price at upper Bollinger Band
        conditions.append(dataframe['bb_percent'] > 0.8)
        
        # 1h timeframe showing weakness
        conditions.append(dataframe['rsi_1h'] > 70)
        
        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:
        """
        BTC-optimized stoploss logic
        """
        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]]:
        """
        BTC-optimized profit-taking logic
        
        Scale-out approach: 4%, 6%, 8% targets
        """
        
        # Scale out at 8% (aggressive target for BTC)
        if current_profit >= 0.08:
            return 'btc_profit_8pct'
            
        # Scale out at 6% (optimal target)
        if current_profit >= 0.06:
            return 'btc_profit_6pct'
            
        # Consider exit at 4% minimum if other conditions met
        if current_profit >= 0.04:
            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