# source: https://raw.githubusercontent.com/Logashan/freqtrade-news-ml/ea2203157448cacf407f6ea6c1df2e17be5f11d5/strategies/NewsHeliusBitqueryML_Hyperopt.py
import logging
from typing import Any, Dict, List, Optional, Tuple

import numpy as np
import pandas as pd
from pandas import DataFrame
from datetime import datetime, timedelta
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from freqtrade.strategy.parameters import CategoricalParameter
from freqtrade.persistence import Trade
from freqtrade.exchange import timeframe_to_minutes
import talib.abstract as ta
import pandas_ta as pta
from freqtrade.strategy import DecimalParameter, IntParameter, CategoricalParameter
import logging
from collections import defaultdict
import warnings
warnings.filterwarnings("ignore")
import talib as tal
import pandas_ta as pta

logger = logging.getLogger(__name__)

class Github_Logashan_freqtrade_news_ml__NewsHeliusBitqueryML_Hyperopt__20250814_154308(IStrategy):
    timeframe = "5m"
    can_short = False
    process_only_new_candles = True
    startup_candle_count = 240
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False
    position_adjustment_enable = False
    order_time_in_force = {
        "entry": "GTC",
        "exit": "GTC"
    }
    order_types = {
        "entry": "limit",
        "exit": "limit",
        "stoploss": "limit",
        "stoploss_on_exchange": False,
        "stoploss_on_exchange_interval": 60,
    }
    
    # Параметры для гипероптимизации ROI
    minimal_roi = {
        "0": 0.01,
        "30": 0.005,
        "90": 0.0
    }
    
    stoploss = -0.02
    
    trailing_stop = True
    trailing_stop_positive = 0.004
    trailing_stop_positive_offset = 0.009
    trailing_only_offset_is_reached = True
    
    ignore_buying_expired_candle_after = 1
    
    # Параметры для гипероптимизации
    buy_params = {
        "vol_min": 0.002,
        "rsi_long_th": 45,
        "adx_min": 14,
        "ema_fast": 12,
        "ema_slow": 26,
        "rsi_period": 14,
        "macd_fast": 12,
        "macd_slow": 26,
        "macd_signal": 9,
        "atr_period": 14
    }
    
    sell_params = {
        "rsi_exit_long": 70,
        "ema_exit_long": True
    }
    
    # Параметры для гипероптимизации ROI
    roi_params = {
        "roi_0": 0.01,
        "roi_30": 0.005,
        "roi_90": 0.0
    }
    
    # Параметры для гипероптимизации Stoploss
    stoploss_params = {
        "stoploss": -0.02
    }
    
    # Параметры для гипероптимизации Trailing Stop
    trailing_params = {
        "trailing_stop": True,
        "trailing_stop_positive": 0.004,
        "trailing_stop_positive_offset": 0.009,
        "trailing_only_offset_is_reached": True
    }

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        df = dataframe.copy()
        
        # --- EMA, MACD ---
        ema_fast = df["close"].ewm(span=self.buy_params['ema_fast'], adjust=False).mean()
        ema_slow = df["close"].ewm(span=self.buy_params['ema_slow'], adjust=False).mean()
        df["macd"] = ema_fast - ema_slow
        df["macd_sig"] = df["macd"].ewm(span=self.buy_params['macd_signal'], adjust=False).mean()
        df["macd_hist"] = df["macd"] - df["macd_sig"]
        
        # Дополнительные EMA для анализа тренда
        df["ema50"] = df["close"].ewm(span=50, adjust=False).mean()
        df["ema200"] = df["close"].ewm(span=200, adjust=False).mean()
        
        # Сохраняем быстрые EMA для совместимости с существующим кодом
        df['ema_fast'] = ema_fast
        df['ema_slow'] = ema_slow

        # --- RSI(14) ---
        delta = df["close"].diff()
        gain = delta.clip(lower=0)
        loss = -delta.clip(upper=0)
        avg_gain = gain.ewm(alpha=1/self.buy_params['rsi_period'], min_periods=self.buy_params['rsi_period'], adjust=False).mean()
        avg_loss = loss.ewm(alpha=1/self.buy_params['rsi_period'], min_periods=self.buy_params['rsi_period'], adjust=False).mean()
        rs = avg_gain / (avg_loss + 1e-10)
        df["rsi"] = 100 - (100 / (1 + rs))

        # --- ATR(14) ---
        prev_close = df["close"].shift()
        tr = pd.concat([
            (df["high"] - df["low"]),
            (df["high"] - prev_close).abs(),
            (df["low"] - prev_close).abs()
        ], axis=1).max(axis=1)
        df["atr"] = tr.ewm(alpha=1/self.buy_params['atr_period'], min_periods=self.buy_params['atr_period'], adjust=False).mean()
        
        # ADX calculation (simplified)
        df['adx'] = df['atr'].rolling(window=14).mean()  # Simplified ADX
        
        # Volume fraction
        df['vol_frac'] = df['volume'] / df['volume'].rolling(window=20).mean()
        
        # Подчищаем и убеждаемся, что всё числовое
        for c in ["macd", "macd_sig", "macd_hist", "ema50", "ema200", "rsi", "atr", "ema_fast", "ema_slow"]:
            df[c] = pd.to_numeric(df[c], errors="coerce")

        df.ffill(inplace=True)
        df.bfill(inplace=True)
        
        return df

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        df = dataframe.copy()
        
        vol_min = self.buy_params['vol_min']
        rsi_long_th = self.buy_params['rsi_long_th']
        adx_min = self.buy_params['adx_min']
        
        long_cond = (
            (df['ema_fast'] > df['ema_slow']) &
            (df['vol_frac'] > vol_min) &
            (df['macd'] > df['macd_sig']) &
            (df['adx'] >= adx_min) &
            (df['rsi'].shift(1) < rsi_long_th) & (df['rsi'] >= rsi_long_th)
        )
        
        df['enter_long'] = long_cond.astype(int)
        
        return df

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        df = dataframe.copy()
        
        rsi_exit_long = self.sell_params['rsi_exit_long']
        ema_exit_long = self.sell_params['ema_exit_long']
        
        exit_long = (
            (df['rsi'] > rsi_exit_long) | 
            (ema_exit_long and df['close'] < df['ema_fast'])
        )
        
        df['exit_long'] = exit_long.astype(int)
        
        return df
    
    # Методы для гипероптимизации ROI
    def custom_roi(self, dataframe: DataFrame, trade: Trade, current_time: datetime, **kwargs) -> float:
        # Динамический ROI на основе времени удержания позиции
        open_minutes = (current_time - trade.open_date_utc).total_seconds() / 60
        
        if open_minutes <= 30:
            return self.roi_params['roi_0']
        elif open_minutes <= 90:
            return self.roi_params['roi_30']
        else:
            return self.roi_params['roi_90']
    
    # Методы для гипероптимизации Stoploss
    def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
        return self.stoploss_params['stoploss']
    
    # Методы для гипероптимизации Trailing Stop
    def custom_trailing_stop(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
        if not self.trailing_params['trailing_stop']:
            return 0
        
        if current_profit > self.trailing_params['trailing_stop_positive_offset']:
            return self.trailing_params['trailing_stop_positive']
        
        return 0

    @property
    def protections(self):
        return [
            {"method": "CooldownPeriod", "stop_duration_candles": 5},
            {"method": "StoplossGuard", "lookback_period_candles": 288,
             "stop_duration_candles": 30, "only_per_pair": False, "trade_limit": 2},
            {"method": "MaxDrawdown", "lookback_period_candles": 288,
             "stop_duration_candles": 60, "max_allowed_drawdown": 8,
             "only_per_pair": False}
        ]
