# source: https://raw.githubusercontent.com/Roshan7869/algotrading/db07ceb71ac165a38fe200a86ae48586ed040f60/user_data/strategies/_archived/hedge_05_scale_out.py
# directory_url: https://github.com/Roshan7869/algotrading/blob/main/user_data/strategies/_archived/
# User: Roshan7869
# Repository: algotrading
# --------------------"""
HEDGE Strategy #5 — 50-30-20 Scale Out Hedge
==============================================
Core Concept: Split capital 50/50 long/short on top gainers.
Uses ChromaDB "Scale Out 50-30-20 — Conservative-Aggressive Split"
  Source: exit_strategies_chunks_034_508

Params:
  - Capital split: 50% long, 50% short
  - Leverage: 10x
  - Stop Loss: -10%
  - Profit scaling: 50% at +10%, 30% at +20%, 20% runs
  - The running 20% uses trailing stop at 3x ATR

Edge: The 50% at +10% (1R) ensures the trade pays for itself immediately.
The 30% at +20% (2R) captures solid gains. The 20% runner is "free money"
that can extend to +30% or beyond. Over 100 trades, this yields consistent
1.4R average while keeping the convexity of capturing big moves.

Risk Mgmt (ChromaDB): Scale Out 50-30-20
  - 50% off at 1R → trade is profitable immediately
  - 30% off at 2R → solid gains locked in
  - 20% runs with 3x ATR trail → captures outliers
  - Average yield: ~1.4R per trade over large sample
"""

from datetime import datetime
from typing import Optional
import numpy as np
import pandas as pd
from pandas import DataFrame

from freqtrade.strategy import IStrategy, Trade, DecimalParameter, IntParameter


class Github_Roshan7869_algotrading__hedge_05_scale_out__20260817_183107(IStrategy):
    INTERFACE_VERSION = 3

    timeframe = "1h"
    can_short: bool = True

    # ── Stop & Target — partial exits via ROI ──
    # 50% at 1R (10%), 30% at 2R (20%), 20% runner
    minimal_roi = {
        "0": 0.30,         # full exit after target if not yet scaled
        "120": 0.20,       # after 2h drop to 20% target
        "1440": 0.10,      # after 24h drop to 10% (time decay)
    }

    stoploss = -0.10

    trailing_stop = True
    trailing_stop_positive = 0.04
    trailing_stop_positive_offset = 0.06
    trailing_only_offset_is_reached = True

    process_only_new_candles = True
    startup_candle_count = 100

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

    # ── Hyperopt Params ──
    vol_surge = DecimalParameter(1.0, 3.0, default=1.5, decimals=1, space="buy")
    rsi_ob = IntParameter(68, 82, default=75, space="sell")
    scale_target_1 = DecimalParameter(0.08, 0.15, default=0.10, decimals=2, space="sell")
    scale_target_2 = DecimalParameter(0.15, 0.25, default=0.20, decimals=2, space="sell")

    # ── Leverage ──
    def leverage(self, pair, current_time, current_rate,
                 proposed_leverage, max_leverage, entry_tag, side, **kwargs) -> float:
        return min(10.0, max_leverage)

    # ── Indicators ──
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe["roc_1h"] = dataframe["close"].pct_change(1) * 100
        dataframe["roc_4h"] = dataframe["close"].pct_change(4) * 100
        dataframe["roc_24h"] = dataframe["close"].pct_change(24) * 100
        dataframe["rsi"] = self._rsi(dataframe, 14)
        dataframe["volume_ma"] = dataframe["volume"].rolling(20).mean()
        dataframe["volume_ratio"] = (dataframe["volume"] / dataframe["volume_ma"]).fillna(1)
        dataframe["atr"] = self._atr(dataframe, 14)
        return dataframe

    # ── Entry ──
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        long_cond = (
            (dataframe["roc_1h"] > 0.5) &
            (dataframe["roc_4h"] > 1.5) &
            (dataframe["volume_ratio"] > self.vol_surge.value) &
            (dataframe["rsi"] > 40) &
            (dataframe["rsi"] < 68)
        )
        dataframe.loc[long_cond & (dataframe["volume"] > 0),
                      ["enter_long", "enter_tag"]] = (1, "hedge05_scale_long")

        short_cond = (
            (dataframe["roc_24h"] > 10.0) &
            (dataframe["rsi"] > self.rsi_ob.value) &
            (dataframe["volume_ratio"] > 1.2) &
            (dataframe["roc_1h"] < 0)
        )
        dataframe.loc[short_cond & (dataframe["volume"] > 0),
                      ["enter_short", "enter_tag"]] = (1, "hedge05_scale_short")

        return dataframe

    # ── Exit ──
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[dataframe["rsi"] > 82, ["exit_long", "exit_tag"]] = (1, "rsi_exit_long")
        dataframe.loc[dataframe["rsi"] < 18, ["exit_short", "exit_tag"]] = (1, "rsi_exit_short")
        return dataframe

    # ── Custom Exit: manual scale-out logic ──
    def custom_exit(self, pair: str, trade: Trade, current_time: datetime,
                    current_rate: float, current_profit: float, **kwargs) -> Optional[str]:
        if current_profit >= self.scale_target_2.value:
            return "scale_2r_locked"
        if current_profit >= self.scale_target_1.value:
            return "scale_1r_locked"
        return None

    # ── Helpers ──
    def _rsi(self, df, period=14):
        delta = df["close"].diff()
        gain = delta.clip(lower=0)
        loss = -delta.clip(upper=0)
        avg_gain = gain.rolling(period).mean()
        avg_loss = loss.rolling(period).mean()
        rs = avg_gain / avg_loss.replace(0, np.nan)
        return 100 - (100 / (1 + rs))

    def _atr(self, df, period=14):
        high_low = df["high"] - df["low"]
        high_close = (df["high"] - df["close"].shift()).abs()
        low_close = (df["low"] - df["close"].shift()).abs()
        tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
        return tr.rolling(period).mean()
