# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/24ocak_2025_ozgur.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  # noqa
import pandas as pd  # noqa
from pandas import DataFrame
from typing import Optional, Union
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy import DecimalParameter, CategoricalParameter
from freqtrade.persistence import Trade
from datetime import datetime, timedelta
import logging

class Github_remiotore_freqtrade__24ocak_2025_ozgur__20260111_210550(IStrategy):
    """
    Revized version of the strategy with improved risk management, dynamic stop loss,
    multi-timeframe analysis, and additional filters for better signal quality.
    """

    # Strategy configuration
    INTERFACE_VERSION: int = 3
    timeframe = '15m'
    can_short: bool = True
    process_only_new_candles = True
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    # Risk management
    stoploss = -0.05  # Initial stop loss (5%)
    minimal_roi = {"0": 0.10}  # Take profit at 10%
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.15
    max_entry_position_adjustment = 3
    max_dca_multiplier = 4.6

    # Order types
    order_types = {
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    # Hyperparameters (optional, for optimization)
    buy_rsi = DecimalParameter(30, 70, default=50, space='buy', optimize=True)
    sell_rsi = DecimalParameter(70, 90, default=70, space='sell', optimize=True)
    atr_multiplier = DecimalParameter(1.5, 3.0, default=2.0, space='sell', optimize=True)

    def informative_pairs(self):
        # Add higher timeframe for trend confirmation
        return [(self.pair, "1h")]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Add indicators
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['mfi'] = ta.MFI(dataframe, timeperiod=14)
        dataframe['macd'] = ta.MACD(dataframe)['macd']
        dataframe['macdsignal'] = ta.MACD(dataframe)['macdsignal']
        dataframe['macdhist'] = ta.MACD(dataframe)['macdhist']
        dataframe['ema9'] = ta.EMA(dataframe, timeperiod=9)
        dataframe['ema26'] = ta.EMA(dataframe, timeperiod=26)
        dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
        dataframe['ema200'] = ta.EMA(dataframe, timeperiod=200)
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)

        # Multi-timeframe indicators (1h)
        informative = self.dp.get_pair_dataframe(pair=self.pair, timeframe="1h")
        dataframe['ema200_1h'] = ta.EMA(informative, timeperiod=200)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Long entry conditions
        dataframe.loc[
            (
                (dataframe['rsi'] < self.buy_rsi.value) &
                (dataframe['mfi'] < 30) &
                (dataframe['ema9'] > dataframe['ema26']) &
                (dataframe['ema26'] > dataframe['ema50']) &
                (dataframe['close'] > dataframe['ema200_1h'])  # Trend confirmation
            ),
            'enter_long'] = 1

        # Short entry conditions
        dataframe.loc[
            (
                (dataframe['rsi'] > self.sell_rsi.value) &
                (dataframe['mfi'] > 70) &
                (dataframe['ema9'] < dataframe['ema26']) &
                (dataframe['ema26'] < dataframe['ema50']) &
                (dataframe['close'] < dataframe['ema200_1h'])  # Trend confirmation
            ),
            'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Long exit conditions
        dataframe.loc[
            (
                (dataframe['rsi'] > 70) |
                (dataframe['ema9'] < dataframe['ema26'])  # Trend reversal
            ),
            'exit_long'] = 1

        # Short exit conditions
        dataframe.loc[
            (
                (dataframe['rsi'] < 30) |
                (dataframe['ema9'] > dataframe['ema26'])  # Trend reversal
            ),
            'exit_short'] = 1

        return dataframe

    def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:
        # Dynamic stop loss based on ATR
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        atr_stoploss = last_candle['atr'] * self.atr_multiplier.value
        return -atr_stoploss / current_rate

    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                            proposed_stake: float, min_stake: Optional[float], max_stake: float,
                            leverage: float, entry_tag: Optional[str], side: str,
                            **kwargs) -> float:
        # Adjust stake amount for DCA
        return proposed_stake / self.max_dca_multiplier

    def adjust_trade_position(self, trade: Trade, current_time: datetime,
                              current_rate: float, current_profit: float,
                              min_stake: Optional[float], max_stake: float,
                              current_entry_rate: float, current_exit_rate: float,
                              current_entry_profit: float, current_exit_profit: float,
                              **kwargs) -> Union[Optional[float], Optional[str]]:
        # DCA logic
        if current_profit < -0.10:  # Add to position if loss exceeds 10%
            filled_entries = trade.select_filled_orders(trade.entry_side)
            stake_amount = filled_entries[0].stake_amount * 1.5  # Increase stake by 50%
            return stake_amount, 'dca_adjustment'
        return None