# source: https://raw.githubusercontent.com/RoboticAutomations/freqtradestrategies/52c4bd40414645f079a980cf4486dc7af4061efb/AIRA%20-%20open%20code%20v2/strategies/orderbook-volume/VolatileCoins.py
# directory_url: https://github.com/RoboticAutomations/freqtradestrategies/blob/main/AIRA - open code v2/strategies/orderbook-volume/
# User: RoboticAutomations
# Repository: freqtradestrategies
# --------------------import logging
from freqtrade.strategy import IStrategy
from typing import Dict, List, Optional
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np
from functools import reduce
from datetime import datetime
import pandas_ta as pta
from freqtrade.persistence import Trade

logger = logging.getLogger(__name__)

class Github_RoboticAutomations_freqtradestrategies__VolatileCoins__20260915_233254(IStrategy):
    timeframe = '5m'
    startup_candle_count = 100
    minimal_roi = {"0": 0.3}
    
    # Stoploss configuration
    stoploss = -0.3
    trailing_stop = True
    trailing_stop_positive = 0.18
    trailing_stop_positive_offset = 0.24
    trailing_only_offset_is_reached = True

    # Risk management
    position_adjustment_enable = True
    max_entry_position_adjustment = 3
    use_custom_stoploss = True
    use_exit_signal = True

    # Strategy parameters
    buy_params = {
        'volume_multiplier': 3.5,
        'rsi_buy': 32,
        'adx_threshold': 22,
        'vwap_deviation': 0.025,
        'atr_multiplier': 1.8,
        'max_listing_age': 72,
        'min_volume_growth': 1.8,
        'price_drop_threshold': 0.25,
        'recovery_factor': 0.4
    }

    sell_params = {
        'rsi_sell': 68,
        'trail_atr_multiplier': 3.2,
        'profit_threshold': 0.15
    }

    def __init__(self, config: dict) -> None:
        super().__init__(config)
        self.pattern_db = {}

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Core indicators
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['bollinger_upper'] = ta.BBANDS(dataframe, timeperiod=20)['upperband']
        dataframe['bollinger_lower'] = ta.BBANDS(dataframe, timeperiod=20)['lowerband']
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)
        dataframe['macd'], dataframe['macdsignal'], dataframe['macdhist'] = ta.MACD(dataframe)
        
        # Volume analysis
        dataframe['volume_ma'] = ta.SMA(dataframe['volume'], timeperiod=20)
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_ma']
        
        # VWAP calculations
        vwap = qtpylib.rolling_vwap(dataframe, window=20)
        dataframe['vwap_deviation'] = (dataframe['close'] - vwap) / vwap
        
        # Historical pattern analysis
        hist_analysis = self.analyze_pair_history(dataframe)
        for key, value in hist_analysis.items():
            dataframe[key] = value
            
        # Time since listing calculations
        dataframe['listing_age'] = (len(dataframe) - dataframe.index) * 5 / 60  # Hours
        
        # Price deviation from listing price
        dataframe['first_close'] = dataframe['close'].iloc[0]
        dataframe['price_deviation'] = (dataframe['close'] - dataframe['first_close']) / dataframe['first_close']
        
        # Data quality checks
        dataframe['clean_data'] = (
            (dataframe['volume'] > 0) &
            (dataframe['high'] != dataframe['low']) &
            (dataframe['close'].pct_change().abs() < 0.5)
        )
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        
        # Historical pattern conditions
        conditions.append(
            (dataframe['listing_age'] < self.buy_params['max_listing_age']) &
            (dataframe['volume_growth'] > self.buy_params['min_volume_growth']) &
            (dataframe['price_deviation'] < -self.buy_params['price_drop_threshold']) &
            (dataframe['price_deviation'] > -self.buy_params['price_drop_threshold'] * self.buy_params['recovery_factor']) &
            (dataframe['hist_volatility'] > 2.0) &
            (dataframe['consolidation'])
        )

        # Technical indicators
        conditions.append(
            (dataframe['volume_ratio'] > self.buy_params['volume_multiplier']) &
            (dataframe['close'] < dataframe['bollinger_upper']) &
            (dataframe['vwap_deviation'].abs() > self.buy_params['vwap_deviation']) &
            (dataframe['adx'] > self.buy_params['adx_threshold']) &
            (dataframe['rsi'] < self.buy_params['rsi_buy']) &
            (dataframe['macd'] > dataframe['macdsignal']) &
            (dataframe['clean_data'])
        )

        # Log conditions for debugging
        if len(dataframe) > 0:
            logger.info(f"Conditions for {metadata['pair']}:")
            logger.info(f"Volume ratio: {dataframe['volume_ratio'].iloc[-1]}")
            logger.info(f"RSI: {dataframe['rsi'].iloc[-1]}")
            logger.info(f"ADX: {dataframe['adx'].iloc[-1]}")
            logger.info(f"MACD: {dataframe['macd'].iloc[-1]}")
            logger.info(f"VWAP deviation: {dataframe['vwap_deviation'].iloc[-1]}")
            logger.info(f"Price deviation: {dataframe['price_deviation'].iloc[-1]}")

        dataframe.loc[
            reduce(lambda x, y: x & y, conditions),
            'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        
        # Technical exit conditions
        conditions.append(
            (dataframe['rsi'] > self.sell_params['rsi_sell']) &
            (dataframe['macd'] < dataframe['macdsignal'])
        )

        # Profit target condition
        conditions.append(
            (dataframe['close'] >= (1 + self.sell_params['profit_threshold']) * dataframe['open'])
        )

        # Trailing stop calculation
        dataframe['trailing_stop'] = dataframe['close'] * (1 - (dataframe['atr'] * self.sell_params['trail_atr_multiplier'] / dataframe['close']))
        
        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:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        
        # Dynamic stoploss calculation
        base_sl = last_candle['hist_volatility'] * 0.5
        if last_candle['recent_recovery']:
            base_sl *= 0.8
        if last_candle['consolidation']:
            base_sl *= 1.2
            
        return min(-0.15, -abs(base_sl))

    def notify_trade(self, trade: Trade) -> None:
        """Store successful trade patterns"""
        if trade.is_successful:
            pair_data = self.dp.get_pair_dataframe(trade.pair, self.timeframe)
            analysis = self.analyze_pair_history(pair_data)
            self.pattern_db[trade.pair] = {
                'entry_candle': trade.entry_data[0],
                'patterns': analysis
            }

    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration": 3
            },
            {
                "method": "MaxDrawdown",
                "lookback_period": 24,
                "trade_limit": 4,
                "stop_duration": 12,
                "max_allowed_drawdown": 0.25
            },
            {
                "method": "StoplossGuard",
                "lookback_period": 24,
                "trade_limit": 2,
                "stop_duration": 6,
                "only_per_pair": True
            }
        ]