# source: https://raw.githubusercontent.com/mmartel86/freqtrade-setup/6c6a77a72199143ffbfc135f5f66de60778e87fb/strategies/FreqaiStrategy_v2.py
import logging
import numpy as np
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta

logger = logging.getLogger(__name__)

class Github_mmartel86_freqtrade_setup__FreqaiStrategy_v2__20260714_223939(IStrategy):
    can_short = True
    minimal_roi = {"5": 0.05, "15": 0.03, "30": 0.02, "60": 0}
    timeframe = "15m"
    process_only_new_candles = True
    startup_candle_count = 250

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

    def feature_engineering_expand_all(self, dataframe: DataFrame, period, **kwargs) -> DataFrame:
        dataframe[f"%-rsi-{period}"] = ta.RSI(dataframe, timeperiod=period)
        dataframe[f"%-sma-{period}"] = ta.SMA(dataframe, timeperiod=period)
        dataframe[f"%-atr-{period}"] = ta.ATR(dataframe, timeperiod=period)

        bb = ta.BBANDS(dataframe, timeperiod=period, nbdevup=2.0, nbdevdn=2.0)
        bb_upper = bb["upperband"]
        bb_lower = bb["lowerband"]
        dataframe[f"%-bb_pctb-{period}"] = (dataframe["close"] - bb_lower) / (bb_upper - bb_lower)

        macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
        dataframe[f"%-macd_hist-{period}"] = macd["macdhist"]

        dataframe[f"%-adx-{period}"] = ta.ADX(dataframe, timeperiod=period)
        return dataframe

    def feature_engineering_standard(self, dataframe: DataFrame, **kwargs) -> DataFrame:
        dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7
        dataframe["%-hour_of_day"] = (dataframe["date"].dt.hour + 1) / 25
        dataframe["%-obv"] = ta.OBV(dataframe)
        ema_50 = ta.EMA(dataframe, timeperiod=50)
        ema_200 = ta.EMA(dataframe, timeperiod=200)
        dataframe["%-ema_50"] = ema_50
        dataframe["%-ema_200"] = ema_200
        dataframe["%-close_ema50_ratio"] = dataframe["close"] / ema_50
        dataframe["%-close_ema200_ratio"] = dataframe["close"] / ema_200

        candle_range = dataframe["high"] - dataframe["low"] + 0.0001
        dataframe["%-body_pct"] = abs(dataframe["close"] - dataframe["open"]) / candle_range
        dataframe["%-upper_wick_pct"] = (dataframe["high"] - np.maximum(dataframe["close"], dataframe["open"])) / candle_range
        dataframe["%-lower_wick_pct"] = (np.minimum(dataframe["close"], dataframe["open"]) - dataframe["low"]) / candle_range

        bb = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
        dataframe["%-bb_width"] = (bb["upperband"] - bb["lowerband"]) / bb["middleband"]

        dataframe["%-vol_sma_20"] = dataframe["volume"].rolling(window=20).mean()
        dataframe["%-vol_ratio"] = dataframe["volume"] / (dataframe["%-vol_sma_20"] + 0.0001)

        up_candle = (dataframe["close"] > dataframe["open"]).astype(float)
        dataframe["%-up_candle"] = up_candle
        dataframe["%-up_ratio_10"] = up_candle.rolling(window=10).mean()

        dataframe["%-ema50_slope"] = (ema_50 / ema_50.shift(5)) - 1
        return dataframe

    def set_freqai_targets(self, dataframe: DataFrame, **kwargs) -> DataFrame:
        future_price = dataframe["close"].shift(
            -self.freqai_info["feature_parameters"]["label_period_candles"]
        )
        dataframe["&-s_close"] = (future_price > dataframe["close"]).astype("float")
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe = self.freqai.start(dataframe, metadata, self)
        return dataframe

    def adjust_trade_position(self, trade, current_time, current_rate, current_profit, min_stake, max_stake, **kwargs):
        if current_profit < 0.05:
            return None
        if trade.nr_of_successful_entries >= 2:
            return None
        if (current_time - trade.open_date_utc).total_seconds() > 900:
            return None
        return trade.stake_amount

    def custom_exit(self, pair: str, trade, current_time, current_rate, current_profit, **kwargs):
        if trade.amount == 0 and (current_time - trade.open_date_utc).total_seconds() > 300:
            return "cancel_unfilled"
        
        if (current_time - trade.open_date_utc).total_seconds() < 300:
            return None
        
        max_profit = getattr(trade, 'max_profit', current_profit)
        
        if current_profit > max_profit:
            trade.max_profit = current_profit
            max_profit = current_profit
        
        if max_profit > 0.02 and current_profit < (max_profit * 0.5):
            return "profit_reversal"
        
        if current_profit < -0.06:
            return "stop_loss_custom"
        
        if current_profit > 0.03:
            if current_profit < (max_profit - 0.008):
                return "trailing_aggressive"
        
        return None

    PAIR_DIRECTIONS = {
        "FET/USDT:USDT": "long",
        "HYPE/USDT:USDT": "both",
        "SOL/USDT:USDT": "both",
        "TAO/USDT:USDT": "short",
        "AVAX/USDT:USDT": "long",
        "DOGE/USDT:USDT": "short",
        "SUI/USDT:USDT": "long",
        "NEAR/USDT:USDT": "long",
        "TRUMP/USDT:USDT": "short",
        "XRP/USDT:USDT": "long",
    }

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        pair = metadata["pair"]
        direction = self.PAIR_DIRECTIONS.get(pair, "both")

        if direction in ("long", "both"):
            dataframe.loc[
                (
                    (dataframe['&-s_close'] > 0.65) &
                    (dataframe['volume'] > 0)
                ),
                'enter_long'] = 1

        if direction in ("short", "both"):
            dataframe.loc[
                (
                    (dataframe['&-s_close'] < 0.35) &
                    (dataframe['volume'] > 0)
                ),
                'enter_short'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        exit_long = (
            (dataframe['&-s_close'] < 0.40) &
            (dataframe['volume'] > 0)
        )

        exit_short = (
            (dataframe['&-s_close'] > 0.60) &
            (dataframe['volume'] > 0)
        )

        dataframe.loc[exit_long, 'exit_long'] = 1
        dataframe.loc[exit_short, 'exit_short'] = 1
        return dataframe
