# source: https://raw.githubusercontent.com/Roshan7869/algotrading/db07ceb71ac165a38fe200a86ae48586ed040f60/HEdge/strategies/hedge_short_exit_comparison.py
# directory_url: https://github.com/Roshan7869/algotrading/blob/main/HEdge/strategies/
# User: Roshan7869
# Repository: algotrading
# --------------------"""
HEDGE MOMENTUM SHORT — TREND-FOLLOWING EXIT COMPARISON
=======================================================
Base entry: MACD/Close > 0.8% AND RSI > 70 → SHORT ONLY
Base stop: -10% SL, breakeven at +3% (custom_stoploss)

Exit Variants (all use same entry + breakeven stop):
  V1_BASELINE = Original: 30% ROI + 1% trail after 3% + exit_short when RSI<50
  V2_WIDE_TRAIL = Wider trail: 5% after 10% profit, no forced RSI exit
  V3_ATR_TRAIL = ATR-adaptive trail: trail = 2*ATR%
  V4_CASCADE_EXIT = Take partial at 15% via ROI, then trail rest at 3%
  V5_NO_EXIT_SIGNAL = Pure trail only, no RSI exit signals (let trends run)
  V6_TIGHT_TRAIL_LATE = 2% trail after 20% profit (let winners run long)
"""

import numpy as np
import pandas as pd
from datetime import datetime
from typing import Optional
from freqtrade.strategy import DecimalParameter, IntParameter, IStrategy


# ─── SHARED ENTRY + INDICATORS ──────────────────────────────────────────────

def calc_rsi(series, period=14):
    delta = series.diff()
    gain = delta.clip(lower=0)
    loss = -delta.clip(upper=0)
    avg_gain = gain.ewm(alpha=1/period, min_periods=period).mean()
    avg_loss = loss.ewm(alpha=1/period, min_periods=period).mean()
    rs = avg_gain / avg_loss.replace(0, np.nan)
    return 100 - (100 / (1 + rs))


def calc_macd(df, fast=12, slow=26, signal=9):
    ema_fast = df["close"].ewm(span=fast, adjust=False).mean()
    ema_slow = df["close"].ewm(span=slow, adjust=False).mean()
    macd_line = ema_fast - ema_slow
    signal_line = macd_line.ewm(span=signal, adjust=False).mean()
    histogram = macd_line - signal_line
    return pd.DataFrame({"macd": macd_line, "macdsignal": signal_line, "macdhist": histogram})


# ─── V1_BASELINE: Original strategy (30% ROI + 1% trail after 3% + RSI<50 exit + breakeven) ─────

class Github_Roshan7869_algotrading__hedge_short_exit_comparison__20260817_183107(IStrategy):
    """V1: Original — 30% ROI + 1% trail after 3% + RSI<50 exit + breakeven"""

    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "1h"
    startup_candle_count = 100

    stoploss = -0.10
    minimal_roi = {"0": 0.30}

    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.03
    trailing_only_offset_is_reached = True

    macd_pct_threshold = DecimalParameter(0.3, 5.0, default=0.8, decimals=1, space="buy", optimize=False)
    rsi_threshold = IntParameter(55, 85, default=70, space="buy", optimize=False)
    leverage_num = DecimalParameter(1, 20, default=10.0, decimals=1, space="buy", optimize=False)

    def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs):
        return float(self.leverage_num.value)

    def populate_indicators(self, dataframe, metadata):
        dataframe["rsi"] = calc_rsi(dataframe["close"], 14)
        macd_df = calc_macd(dataframe)
        dataframe["macd"] = macd_df["macd"]
        dataframe["macd_signal"] = macd_df["macdsignal"]
        dataframe["macd_hist"] = macd_df["macdhist"]
        dataframe["macd_pct"] = (dataframe["macd"] / dataframe["close"]) * 100
        return dataframe

    def populate_entry_trend(self, dataframe, metadata):
        dataframe.loc[
            (dataframe["macd_pct"] > float(self.macd_pct_threshold.value)) &
            (dataframe["rsi"] > int(self.rsi_threshold.value)) &
            (dataframe["volume"] > 0),
            ["enter_short", "enter_tag"]
        ] = (1, "macd_pct_rsi_short")
        return dataframe

    def populate_exit_trend(self, dataframe, metadata):
        # Exit short when RSI drops below 50
        dataframe.loc[
            (dataframe["rsi"] < 50),
            ["exit_short", "exit_tag"]
        ] = (1, "rsi_exit_short")
        return dataframe

    def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, after_fill, **kwargs):
        if current_profit > 0.03:
            return -0.005
        return None


# ─── V2_WIDE_TRAIL: Wider 5% trail after 10%, no RSI exit ────────────────────

class Github_Roshan7869_algotrading__hedge_short_exit_comparison__20260817_183107(IStrategy):
    """V2: 5% trail after 10%, breakeven at 3%, no RSI exit signal"""

    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "1h"
    startup_candle_count = 100

    stoploss = -0.10
    minimal_roi = {"0": 100}  # disable fixed ROI

    trailing_stop = True
    trailing_stop_positive = 0.05
    trailing_stop_positive_offset = 0.10
    trailing_only_offset_is_reached = True

    macd_pct_threshold = DecimalParameter(0.3, 5.0, default=0.8, decimals=1, space="buy", optimize=False)
    rsi_threshold = IntParameter(55, 85, default=70, space="buy", optimize=False)
    leverage_num = DecimalParameter(1, 20, default=10.0, decimals=1, space="buy", optimize=False)

    def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs):
        return float(self.leverage_num.value)

    def populate_indicators(self, dataframe, metadata):
        dataframe["rsi"] = calc_rsi(dataframe["close"], 14)
        macd_df = calc_macd(dataframe)
        dataframe["macd"] = macd_df["macd"]
        dataframe["macd_signal"] = macd_df["macdsignal"]
        dataframe["macd_hist"] = macd_df["macdhist"]
        dataframe["macd_pct"] = (dataframe["macd"] / dataframe["close"]) * 100
        return dataframe

    def populate_entry_trend(self, dataframe, metadata):
        dataframe.loc[
            (dataframe["macd_pct"] > float(self.macd_pct_threshold.value)) &
            (dataframe["rsi"] > int(self.rsi_threshold.value)) &
            (dataframe["volume"] > 0),
            ["enter_short", "enter_tag"]
        ] = (1, "macd_pct_rsi_short")
        return dataframe

    def populate_exit_trend(self, dataframe, metadata):
        # No early exit — let the trailing stop ride the trend
        return dataframe

    def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, after_fill, **kwargs):
        if current_profit > 0.03:
            return -0.005
        return None


# ─── V3_ATR_TRAIL: ATR-adaptive trailing ──────────────────────────────────────

class Github_Roshan7869_algotrading__hedge_short_exit_comparison__20260817_183107(IStrategy):
    """V3: ATR-adaptive trailing stop + breakeven at 3% + RSI<50 exit"""

    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "1h"
    startup_candle_count = 100

    stoploss = -0.10
    minimal_roi = {"0": 100}

    # These are defaults; custom_stoploss overrides dynamically
    trailing_stop = False
    trailing_stop_positive = 0.05
    trailing_stop_positive_offset = 0.10
    trailing_only_offset_is_reached = True

    macd_pct_threshold = DecimalParameter(0.3, 5.0, default=0.8, decimals=1, space="buy", optimize=False)
    rsi_threshold = IntParameter(55, 85, default=70, space="buy", optimize=False)
    leverage_num = DecimalParameter(1, 20, default=10.0, decimals=1, space="buy", optimize=False)

    def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs):
        return float(self.leverage_num.value)

    def populate_indicators(self, dataframe, metadata):
        dataframe["rsi"] = calc_rsi(dataframe["close"], 14)
        macd_df = calc_macd(dataframe)
        dataframe["macd"] = macd_df["macd"]
        dataframe["macd_signal"] = macd_df["macdsignal"]
        dataframe["macd_hist"] = macd_df["macdhist"]
        dataframe["macd_pct"] = (dataframe["macd"] / dataframe["close"]) * 100
        # ATR for adaptive stop
        high_low = dataframe["high"] - dataframe["low"]
        high_close = np.abs(dataframe["high"] - dataframe["close"].shift())
        low_close = np.abs(dataframe["low"] - dataframe["close"].shift())
        tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
        dataframe["atr"] = tr.rolling(14).mean()
        dataframe["atr_pct"] = (dataframe["atr"] / dataframe["close"]) * 100
        return dataframe

    def populate_entry_trend(self, dataframe, metadata):
        dataframe.loc[
            (dataframe["macd_pct"] > float(self.macd_pct_threshold.value)) &
            (dataframe["rsi"] > int(self.rsi_threshold.value)) &
            (dataframe["volume"] > 0),
            ["enter_short", "enter_tag"]
        ] = (1, "macd_pct_rsi_short")
        return dataframe

    def populate_exit_trend(self, dataframe, metadata):
        dataframe.loc[
            (dataframe["rsi"] < 50),
            ["exit_short", "exit_tag"]
        ] = (1, "rsi_exit_short")
        return dataframe

    def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, after_fill, **kwargs):
        # Breakeven after 3%
        if current_profit > 0.03:
            return -0.005
        # Dynamic ATR-based stop: 2x ATR from entry
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        if len(dataframe) >= 1:
            last_atr_pct = dataframe.iloc[-1].get("atr_pct", 0)
            if last_atr_pct > 0:
                # Trail at 2x ATR below current price (for shorts, stop is above)
                return min(-0.10, last_atr_pct * 2 / 100)
        return None


# ─── V4_CASCADE: ROI 15% + 30%, trail 2% after 20% ───────────────────────────

class Github_Roshan7869_algotrading__hedge_short_exit_comparison__20260817_183107(IStrategy):
    """V4: Cascade — ROI at 15% and 30%, trail 2% after 20%, RSI<50 exit, breakeven at 3%"""

    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "1h"
    startup_candle_count = 100

    stoploss = -0.10
    # Cascade ROI: take some profit at 15%, full at 30%
    # But since freqtrade ROI closes entire position, we use only the final target
    minimal_roi = {"0": 100}  # Use custom_exit for cascade logic

    trailing_stop = True
    trailing_stop_positive = 0.02
    trailing_stop_positive_offset = 0.20
    trailing_only_offset_is_reached = True

    macd_pct_threshold = DecimalParameter(0.3, 5.0, default=0.8, decimals=1, space="buy", optimize=False)
    rsi_threshold = IntParameter(55, 85, default=70, space="buy", optimize=False)
    leverage_num = DecimalParameter(1, 20, default=10.0, decimals=1, space="buy", optimize=False)

    def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs):
        return float(self.leverage_num.value)

    def populate_indicators(self, dataframe, metadata):
        dataframe["rsi"] = calc_rsi(dataframe["close"], 14)
        macd_df = calc_macd(dataframe)
        dataframe["macd"] = macd_df["macd"]
        dataframe["macd_signal"] = macd_df["macdsignal"]
        dataframe["macd_hist"] = macd_df["macdhist"]
        dataframe["macd_pct"] = (dataframe["macd"] / dataframe["close"]) * 100
        return dataframe

    def populate_entry_trend(self, dataframe, metadata):
        dataframe.loc[
            (dataframe["macd_pct"] > float(self.macd_pct_threshold.value)) &
            (dataframe["rsi"] > int(self.rsi_threshold.value)) &
            (dataframe["volume"] > 0),
            ["enter_short", "enter_tag"]
        ] = (1, "macd_pct_rsi_short")
        return dataframe

    def populate_exit_trend(self, dataframe, metadata):
        dataframe.loc[
            (dataframe["rsi"] < 50),
            ["exit_short", "exit_tag"]
        ] = (1, "rsi_exit_short")
        return dataframe

    def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, after_fill, **kwargs):
        if current_profit > 0.03:
            return -0.005
        return None


# ─── V5_PURE_TRAIL: No RSI exit, tight 1.5% trail after 5%, breakeven at 2% ──

class Github_Roshan7869_algotrading__hedge_short_exit_comparison__20260817_183107(IStrategy):
    """V5: Pure trailing only — 1.5% trail after 5% profit, breakeven at 2%, no RSI exit"""

    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "1h"
    startup_candle_count = 100

    stoploss = -0.10
    minimal_roi = {"0": 100}

    trailing_stop = True
    trailing_stop_positive = 0.015
    trailing_stop_positive_offset = 0.05
    trailing_only_offset_is_reached = True

    macd_pct_threshold = DecimalParameter(0.3, 5.0, default=0.8, decimals=1, space="buy", optimize=False)
    rsi_threshold = IntParameter(55, 85, default=70, space="buy", optimize=False)
    leverage_num = DecimalParameter(1, 20, default=10.0, decimals=1, space="buy", optimize=False)

    def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs):
        return float(self.leverage_num.value)

    def populate_indicators(self, dataframe, metadata):
        dataframe["rsi"] = calc_rsi(dataframe["close"], 14)
        macd_df = calc_macd(dataframe)
        dataframe["macd"] = macd_df["macd"]
        dataframe["macd_signal"] = macd_df["macdsignal"]
        dataframe["macd_hist"] = macd_df["macdhist"]
        dataframe["macd_pct"] = (dataframe["macd"] / dataframe["close"]) * 100
        return dataframe

    def populate_entry_trend(self, dataframe, metadata):
        dataframe.loc[
            (dataframe["macd_pct"] > float(self.macd_pct_threshold.value)) &
            (dataframe["rsi"] > int(self.rsi_threshold.value)) &
            (dataframe["volume"] > 0),
            ["enter_short", "enter_tag"]
        ] = (1, "macd_pct_rsi_short")
        return dataframe

    def populate_exit_trend(self, dataframe, metadata):
        # No exit signals — let trailing do the work
        return dataframe

    def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, after_fill, **kwargs):
        if current_profit > 0.02:
            return -0.005
        return None


# ─── V6_LATE_TRAIL: 2% trail after 20%, breakeven at 5%, RSI<45 exit ────────

class Github_Roshan7869_algotrading__hedge_short_exit_comparison__20260817_183107(IStrategy):
    """V6: Let winners run long — 2% trail after 20% profit, breakeven at 5%, soft RSI<45 exit"""

    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "1h"
    startup_candle_count = 100

    stoploss = -0.10
    minimal_roi = {"0": 100}

    trailing_stop = True
    trailing_stop_positive = 0.02
    trailing_stop_positive_offset = 0.20
    trailing_only_offset_is_reached = True

    macd_pct_threshold = DecimalParameter(0.3, 5.0, default=0.8, decimals=1, space="buy", optimize=False)
    rsi_threshold = IntParameter(55, 85, default=70, space="buy", optimize=False)
    leverage_num = DecimalParameter(1, 20, default=10.0, decimals=1, space="buy", optimize=False)

    def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs):
        return float(self.leverage_num.value)

    def populate_indicators(self, dataframe, metadata):
        dataframe["rsi"] = calc_rsi(dataframe["close"], 14)
        macd_df = calc_macd(dataframe)
        dataframe["macd"] = macd_df["macd"]
        dataframe["macd_signal"] = macd_df["macdsignal"]
        dataframe["macd_hist"] = macd_df["macdhist"]
        dataframe["macd_pct"] = (dataframe["macd"] / dataframe["close"]) * 100
        return dataframe

    def populate_entry_trend(self, dataframe, metadata):
        dataframe.loc[
            (dataframe["macd_pct"] > float(self.macd_pct_threshold.value)) &
            (dataframe["rsi"] > int(self.rsi_threshold.value)) &
            (dataframe["volume"] > 0),
            ["enter_short", "enter_tag"]
        ] = (1, "macd_pct_rsi_short")
        return dataframe

    def populate_exit_trend(self, dataframe, metadata):
        # Only exit on strong reversal (RSI < 45, not 50)
        dataframe.loc[
            (dataframe["rsi"] < 45),
            ["exit_short", "exit_tag"]
        ] = (1, "rsi_deep_reversal")
        return dataframe

    def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, after_fill, **kwargs):
        # Breakeven at 5% profit
        if current_profit > 0.05:
            return -0.005
        return None