# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/hacimlifit1.py




import numpy as np  # noqa
import pandas as pd  # noqa
from pandas import DataFrame
from technical.indicators import fibma,vwma
from functools import reduce
from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal
from freqtrade.strategy import BooleanParameter,DecimalParameter,IStrategy,IntParameter


import talib.abstract as ta
from typing import List

def get_whale_volume_threshold(dataframe:DataFrame,mean_multiplier=3,volmean=200):
    data = dataframe.copy()
    data["mean_volume"] = data['volume'].rolling(volmean).mean()
    data["std_volume"] = data['volume'].rolling(volmean).std()

    data["std_threshold"] = round(data["mean_volume"] + mean_multiplier * data["std_volume"],2)
    return data["std_threshold"]

class Github_remiotore_freqtrade__hacimlifit1__20260111_210550(IStrategy):


    INTERFACE_VERSION = 3

    can_short: bool = False

    class HyperOpt:

        def stoploss_space():
            return [SKDecimal(-0.5, -0.15, decimals=2, name='stoploss')]
        def trailing_space() -> List[Dimension]:

            return [

                Categorical([True], name='trailing_stop'),

                SKDecimal(0.01, 0.35, decimals=3, name='trailing_stop_positive'),





                SKDecimal(0.05, 0.3, decimals=3, name='trailing_stop_positive_offset_p1'),

                Categorical([True, False], name='trailing_only_offset_is_reached'),
        ]

        def max_open_trades_space() -> List[Dimension]:
            return [
                Integer(-1, 8, name='max_open_trades'),
            ]


    minimal_roi = {


        "0": 10,
    }


    stoploss = -0.46

    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.152
    trailing_only_offset_is_reached = True

    timeframe = "5m"

    process_only_new_candles = True

    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    buy_dfma = DecimalParameter(1.01, 1.06, default=1.059, space="buy", optimize=True, load=True)

    upwneed = BooleanParameter(default=True, space="buy", optimize=True)
    buy_upw = DecimalParameter(1.001, 1.08, default=1.044, space="buy", optimize=True, load=True)  

    buy_cs = DecimalParameter(1.02, 1.05, default=1.03, space="buy", optimize=True, load=True)

    fibmaneeed = BooleanParameter(default=True, space="buy", optimize=True)

    buy_adx = IntParameter(15, 60, default=20, space="buy", optimize=True, load=True)

    emaneed = BooleanParameter(default=True, space="buy", optimize=True)

    vwmaneed = BooleanParameter(default=True, space="buy", optimize=True)

    badtime = BooleanParameter(default=True, space="buy", optimize=True)
    houra = IntParameter(0, 8, default=4, space="buy", optimize=True, load=True)
    hourb = IntParameter(9, 16, default=11, space="buy", optimize=True, load=True)
    hourc = IntParameter(17, 24, default=18, space="buy", optimize=True, load=True)

    lastvlneed = BooleanParameter(default=True, space="buy", optimize=True)
    buy_lastvl = IntParameter(2, 10, default=2, space="buy", optimize=True, load=True)
    last_lowest = IntParameter(10, 100, default=24, space="sell", optimize=True, load=True)

    cooldown_lookback = IntParameter(2, 48, default=15, space="protection", optimize=True)
    stop_duration = IntParameter(12, 200, default=135, space="protection", optimize=True)
    use_stop_protection = BooleanParameter(default=True, space="protection", optimize=True)
    stop_max_allowed_dd = DecimalParameter(0.05, 0.5, default=0.108, space="protection", optimize=True)
    use_MaxDrawdown_protection = BooleanParameter(default=True, space="protection", optimize=True)
    @property
    def protections(self):
        prot = []

        prot.append({
            "method": "CooldownPeriod",
            "stop_duration_candles": self.cooldown_lookback.value
        })
        if self.use_stop_protection.value:
            prot.append({
                "method": "StoplossGuard",
                "lookback_period_candles": 24 * 3,
                "trade_limit": 4,
                "stop_duration_candles": self.stop_duration.value,
                "only_per_pair": False
            })
        if self.use_MaxDrawdown_protection.value:
            prot.append({
                "method": "MaxDrawdown",
                "lookback_period_candles": 24 * 3,
                "trade_limit": 20,
                "stop_duration_candles": self.stop_duration.value,
                "max_allowed_drawdown": self.stop_max_allowed_dd.value
            })

        return prot

    startup_candle_count: int = 200

    order_types = {
        "entry": "limit",
        "exit": "limit",
        "stoploss": "market",
        "stoploss_on_exchange": False,
    }

    order_time_in_force = {"entry": "GTC", "exit": "GTC"}

    plot_config = {
        "main_plot": {
            "fibma": {"color": "white"},
            "ema200": {"color": "purple"},
            "vwma200": {"color": "green"},
        },
        "subplots": {
            "ADX": {
                "adx": {"color": "yellow"},
            },
            "RSI": {
                "rsi": {"color": "red"},
            },
        },
    }

    def informative_pairs(self):
        """
        Define additional, informative pair/interval combinations to be cached from the exchange.
        These pair/interval combinations are non-tradeable, unless they are part
        of the whitelist as well.
        For more information, please consult the documentation
        :return: List of tuples in the format (pair, interval)
            Sample: return [("ETH/USDT", "5m"),
                            ("BTC/USDT", "15m"),
                            ]
        """
        return []

    def leverage(self, pair: str,  current_rate: float,
                 proposed_leverage: float, max_leverage: float, side: str,
                 **kwargs) -> float:
 
        return 10.0        

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        dataframe['hour'] = dataframe['date'].dt.hour
        dataframe['minute'] = dataframe['date'].dt.minute
        dataframe["whale_volume_threshold"] = get_whale_volume_threshold(dataframe,volmean=200,mean_multiplier=5)
        dataframe['volinc'] = 0
        dataframe.loc[dataframe['volume'] > dataframe["whale_volume_threshold"], 'volinc'] = 1
        dataframe["volcheck"] = dataframe['volume']> dataframe["whale_volume_threshold"]
        dataframe['obv'] = ta.OBV(dataframe)
        dataframe['fibma'] = fibma(dataframe, MAtype=1, src=1)
        dataframe['obv_slope'] = dataframe['obv'] - dataframe['obv'].shift(1)
        dataframe['adx'] = ta.ADX(dataframe)
        dataframe['rsi'] = ta.RSI(dataframe["close"],14)
        dataframe['ema200'] = ta.EMA(dataframe["close"],200)
        dataframe["vwma200"] = vwma(dataframe,"close",200)

        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        longconditions = []

        longconditions.append(dataframe["close"] < dataframe["open"]*self.buy_cs.value)        
        longconditions.append(dataframe["close"] < dataframe["fibma"]*self.buy_dfma.value)
        if self.upwneed.value:
            longconditions.append(dataframe["close"] * self.buy_upw.value > dataframe["high"])
        if self.fibmaneeed.value:
            longconditions.append(dataframe["fibma"] > dataframe["fibma"].shift(1))

        longconditions.append(dataframe["close"] > dataframe["open"])

        longconditions.append(dataframe['adx'] > self.buy_adx.value)
        longconditions.append(dataframe['obv_slope'] > 0)
        if self.emaneed.value:
            longconditions.append(dataframe['close'] > dataframe['ema200'] )
        if self.vwmaneed.value:
            longconditions.append(dataframe['close'] > dataframe['vwma200'])
        if self.badtime.value:
            longconditions.append(~((dataframe['hour'] >= self.houra.value) & (dataframe['hour'] < self.houra.value+2)))
            longconditions.append(~((dataframe['hour'] >= self.hourb.value) & (dataframe['hour'] < self.hourb.value+2)))
            longconditions.append(~((dataframe['hour'] >= self.hourc.value) & (dataframe['hour'] < (self.hourc.value+2)%24)))

        longconditions.append(dataframe['volcheck'] == True )
        if self.lastvlneed.value:
            longconditions.append(dataframe['volinc'].rolling(window=24 ).sum() < self.buy_lastvl.value)

        longconditions.append(dataframe['volume'] > 0)
        if longconditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, longconditions),
                'enter_long'] = 1

        return dataframe
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (   
                    (dataframe['close'] < dataframe['low'].rolling(self.last_lowest.value).min().shift(1))  
                )
            ),

            'exit_long'] =1
        return dataframe
