# source: https://raw.githubusercontent.com/teguh11/botbot2/b6a117c10497dc2f567d40f8f5054691a76b4665/user_data/strategies/SupertrendSD.py
# pragma pylint: disable=missing-docstring, invalid-name, too-many-instance-attributes
"""
Github_teguh11_botbot2__SupertrendSD__20260713_092552 — SuperTrend + Supply/Demand multi-timeframe strategy.

Konsep (dari analisa chart TradingView):
  • SuperTrend 4H  → filter arah (bull → hanya LONG, bear → hanya SHORT)
  • SuperTrend 15m → trigger entry saat flip searah dengan 4H
  • Volume spike   → konfirmasi reversal (replika marker "R" volume-tinggi)
  • Supply/Demand  → swing high/low terdekat sebagai konteks S/R
"""
from datetime import datetime
from typing import Optional

import numpy as np
import pandas as pd
from pandas import DataFrame

import talib.abstract as ta
from freqtrade.strategy import (
    IStrategy, informative, DecimalParameter, IntParameter,
)
from freqtrade.persistence import Trade


def supertrend(dataframe: DataFrame, period: int = 10, multiplier: float = 3.0):
    """
    Return (st_line, direction).
    direction: +1 = bullish (garis hijau di bawah harga),
               -1 = bearish (garis merah di atas harga).
    """
    df  = dataframe
    atr = ta.ATR(df, timeperiod=period)
    hl2 = (df["high"] + df["low"]) / 2

    upper = np.asarray(hl2 + multiplier * atr, dtype="float64").copy()
    lower = np.asarray(hl2 - multiplier * atr, dtype="float64").copy()
    close = np.asarray(df["close"], dtype="float64")
    n     = len(df)

    in_up = np.ones(n, dtype=bool)
    for i in range(1, n):
        if close[i] > upper[i - 1]:
            in_up[i] = True
        elif close[i] < lower[i - 1]:
            in_up[i] = False
        else:
            in_up[i] = in_up[i - 1]
            # Band hanya boleh mengetat searah tren (trailing)
            if in_up[i] and lower[i] < lower[i - 1]:
                lower[i] = lower[i - 1]
            if (not in_up[i]) and upper[i] > upper[i - 1]:
                upper[i] = upper[i - 1]

    st_line   = np.where(in_up, lower, upper)
    direction = np.where(in_up, 1, -1)
    return (
        pd.Series(st_line,   index=df.index),
        pd.Series(direction, index=df.index),
    )


class Github_teguh11_botbot2__SupertrendSD__20260713_092552(IStrategy):
    INTERFACE_VERSION = 3

    timeframe = "15m"
    can_short = True

    # ROI & stoploss (dasar — bisa di-hyperopt nanti)
    minimal_roi = {
        "0":   0.05,
        "30":  0.03,
        "60":  0.02,
        "120": 0.0,
    }
    stoploss = -0.08

    trailing_stop = True
    trailing_stop_positive = 0.02
    trailing_stop_positive_offset = 0.03
    trailing_only_offset_is_reached = True

    process_only_new_candles = True
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    startup_candle_count = 200

    # ── Hyperparameters ───────────────────────────────────────
    st_period    = IntParameter(7, 20, default=10, space="buy")
    st_mult      = DecimalParameter(1.5, 4.0, default=3.0, decimals=1, space="buy")
    vol_factor   = DecimalParameter(1.0, 2.5, default=1.3, decimals=1, space="buy")
    adx_min      = IntParameter(15, 40, default=25, space="buy")
    leverage_num = IntParameter(1, 10, default=5, space="buy")

    # ── Higher timeframe: SuperTrend 4H (filter arah) ─────────
    @informative("4h")
    def populate_indicators_4h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        _, st_dir = supertrend(dataframe, self.st_period.value, self.st_mult.value)
        dataframe["st_dir"] = st_dir
        return dataframe

    # ── Base timeframe: 15m ───────────────────────────────────
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        st_line, st_dir = supertrend(dataframe, self.st_period.value, self.st_mult.value)
        dataframe["st_line"] = st_line
        dataframe["st_dir"]  = st_dir
        # Flip = titik reversal
        dataframe["st_flip_up"]   = (st_dir == 1)  & (st_dir.shift(1) == -1)
        dataframe["st_flip_down"] = (st_dir == -1) & (st_dir.shift(1) == 1)

        # Volume spike (konfirmasi "R")
        dataframe["vol_ma"]    = dataframe["volume"].rolling(20).mean()
        dataframe["vol_spike"] = dataframe["volume"] > (self.vol_factor.value * dataframe["vol_ma"])

        # Supply/Demand konteks — swing high/low 20 candle
        dataframe["demand"] = dataframe["low"].rolling(20).min()
        dataframe["supply"] = dataframe["high"].rolling(20).max()

        # Filter tren dasar
        dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)
        dataframe["rsi"]    = ta.RSI(dataframe, timeperiod=14)
        dataframe["adx"]    = ta.ADX(dataframe, timeperiod=14)  # kekuatan tren
        return dataframe

    # ── Entry ─────────────────────────────────────────────────
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        long_cond = (
            dataframe["st_flip_up"]                        # 15m flip bullish
            & (dataframe["st_dir_4h"] == 1)                # 4H tren bullish
            & dataframe["vol_spike"]                       # volume konfirmasi
            & (dataframe["adx"] > self.adx_min.value)      # tren cukup kuat (anti-chop)
            & (dataframe["rsi"] < 70)
            & (dataframe["volume"] > 0)
        )
        dataframe.loc[long_cond, ["enter_long", "enter_tag"]] = (1, "st_flip_long")

        short_cond = (
            dataframe["st_flip_down"]                      # 15m flip bearish
            & (dataframe["st_dir_4h"] == -1)               # 4H tren bearish
            & dataframe["vol_spike"]                       # volume konfirmasi
            & (dataframe["adx"] > self.adx_min.value)      # tren cukup kuat (anti-chop)
            & (dataframe["rsi"] > 30)
            & (dataframe["volume"] > 0)
        )
        dataframe.loc[short_cond, ["enter_short", "enter_tag"]] = (1, "st_flip_short")
        return dataframe

    # ── Exit ──────────────────────────────────────────────────
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Keluar saat SuperTrend 15m flip berlawanan
        dataframe.loc[dataframe["st_flip_down"], "exit_long"]  = 1
        dataframe.loc[dataframe["st_flip_up"],   "exit_short"] = 1
        return dataframe

    # ── Leverage ──────────────────────────────────────────────
    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, entry_tag: Optional[str],
                 side: str, **kwargs) -> float:
        return float(min(self.leverage_num.value, max_leverage))
