# source: https://raw.githubusercontent.com/songjonghyun11/freqtrade-custom/471cbae168c24f6dbce9e533e3801c47f387bdf0/src/strategies/hybrid_alligator_atr_relaxed.py
from freqtrade.strategy import IStrategy
from freqtrade.strategy.parameters import RealParameter, IntParameter
import talib.abstract as ta
import pandas as pd
from pandas import DataFrame

from modules.entry_signal import EntrySignal
from modules.exit_signal import ExitSignal
from modules.risk_manager import RiskManager

class Github_songjonghyun11_freqtrade_custom__hybrid_alligator_atr_relaxed__20250621_094452(IStrategy):
    # 기본 설정
    timeframe = '5m'
    startup_candle_count = 50

    # Dynamic stop-loss ATR multiplier (Hyperopt 대상)
    sl_atr_multiplier = RealParameter(
        0.5, 3.0, default=1.5, space='sell', optimize=True, load=True
    )

    minimal_roi = {
        "0":   0.245,
        "26":  0.048,
        "50":  0.021,
        "121": 0
    }

    # 기본 stoploss (백업용)
    stoploss = -0.23
    stoploss_param = RealParameter(
        -0.10, -0.01, default=-0.02441,
        space='sell', optimize=True, load=True
    )

    trailing_stop = True
    trailing_stop_positive = 0.03
    trailing_stop_positive_offset = 0.05
    trailing_only_offset_is_reached = False

    process_only_new_candles = True
    use_custom_stoploss = True
    # can_long / can_short 는 __init__에서 trading_mode에 따라 설정

    # Entry signal 파라미터 (Hyperopt 대상)
    atr_period      = IntParameter(8, 21,  default=14,  space='buy', optimize=True, load=True)
    vol_multiplier  = RealParameter(1.0, 3.0, default=1.2,  space='buy', optimize=True, load=True)
    volat_threshold = RealParameter(0.003,0.02, default=0.005, space='buy', optimize=True, load=True)
    high_lookback   = IntParameter(1, 7,   default=3,    space='buy', optimize=True, load=True)

    def __init__(self, config: dict):
        super().__init__(config)
        # Spot vs Futures 분기: futures 모드면 숏 허용, 아니면 롱만
        trading_mode = config.get("trading_mode", "spot").lower()
        self.can_long  = True
        self.can_short = trading_mode == "futures"

        # 모듈에 전달할 파라미터 딕셔너리 생성
        params = {
            "atr_period":      int(self.atr_period.value),
            "vol_multiplier":  float(self.vol_multiplier.value),
            "volat_threshold": float(self.volat_threshold.value),
            "high_lookback":   int(self.high_lookback.value),
            "sl_atr_multiplier": float(self.sl_atr_multiplier.value),
        }
        self.entry_module = EntrySignal(params)
        self.exit_module  = ExitSignal(params)
        self.risk_module  = RiskManager(params["sl_atr_multiplier"])

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Alligator 지표 계산
        hl2 = (dataframe['high'] + dataframe['low']) / 2
        dataframe['jaw']   = pd.Series(ta.EMA(hl2, timeperiod=13), index=dataframe.index).shift(8)
        dataframe['teeth'] = pd.Series(ta.EMA(hl2, timeperiod=8),  index=dataframe.index).shift(5)
        dataframe['lips']  = pd.Series(ta.EMA(hl2, timeperiod=5),  index=dataframe.index).shift(3)

        # ADX/DI
        dataframe['adx']     = ta.ADX(dataframe, timeperiod=14)
        dataframe['plusdi']  = ta.PLUS_DI(dataframe, timeperiod=14)
        dataframe['minusdi'] = ta.MINUS_DI(dataframe, timeperiod=14)

        # ATR 및 볼륨 MA
        dataframe['atr']    = ta.ATR(dataframe, timeperiod=self.atr_period.value)
        dataframe['vol_ma'] = dataframe['volume'].rolling(10).mean()
        return dataframe

    # ... (생략) 기존 설정 및 클래스 정의

    def populate_entry_trend(self, data: dict, symbols: list, params: dict) -> dict:
        results = {}
        for symbol in symbols:
            long_signals = self.entry_module.generate_long(data[symbol], symbol, params)
            results[symbol] = {"long_signal": long_signals}
            if self.can_short:
                short_signals = self.entry_module.generate_short(data[symbol], symbol, params)
                results[symbol]["short_signal"] = short_signals
        return results

    def populate_exit_trend(self, data: dict, symbols: list, params: dict, positions: dict) -> dict:
        results = {}
        for symbol in symbols:
            exit_long_signals = self.exit_module.generate_long(data[symbol], symbol, params, positions.get(symbol))
            results[symbol] = {"exit_long_signal": exit_long_signals}
            if self.can_short:
                exit_short_signals = self.exit_module.generate_short(data[symbol], symbol, params, positions.get(symbol))
                results[symbol]["exit_short_signal"] = exit_short_signals
        return results

    def custom_stoploss(self, symbol, trade, current_time, current_rate, current_profit, **kwargs) -> float:
        df = self.dp.get_pair_dataframe(symbol)
        period = int(self.atr_period.value)
        atr_series = ta.ATR(df, timeperiod=period)
        atr = atr_series.iloc[-1]
        stoploss_price = self.risk_module.calculate_stoploss(trade.open_rate, atr)
        return stoploss_price / trade.open_rate - 1
