# source: https://raw.githubusercontent.com/labanux/freq/b573987c312315003c6ed9cd1bcf8cd009ec500b/user_data/strategies/bak.sekka-hour.py
# ================================================================
# SekkaStrat v14n – Fixed DCA trigger not executing (Freqtrade 2025.10)
# ---------------------------------------------------------------
# Fixes:
# - DCA now executes correctly by returning actual stake amount (est_stake)
# - Preserves 3-step recursive DCA at -3% from avg price
# - Fully compatible with Freqtrade 2025.10 wallet API
# ================================================================

from freqtrade.strategy import IStrategy
from pandas import DataFrame
import pandas as pd
import numpy as np
import talib.abstract as ta
import logging


class Github_labanux_freq__bak_sekka_hour__20260505_044916(IStrategy):
    timeframe = "1h"
    informative_timeframes = []  # Single timeframe only - no multi-timeframe
    process_only_new_candles = True
    can_short = False  # Long-only strategy

    use_exit_signal = True
    exit_profit_only = False
    ignore_buying_expiry = True
    position_adjustment_enable = True  # ✅ Ensure DCA works

    startup_candle_count = 300

    TP_THRESHOLD = 0.04
    DCA_THRESHOLD = 0.01
    DCA_STEP = 5
    RSI_PERIOD = 14
    VWAP_WINDOW = 14

    VWAP_GAP = -0.03
    RSI_THRESHOLD = 48
    RSI_TP = 72

    minimal_roi = {}
    stoploss = -0.99
    #max_entry_position_adjustment = -1

    logger = logging.getLogger(__name__)
    _last_dca_stage = None

    # ------------------ Plot Config ------------------
    plot_config = {
        "main_plot": {
            "vwap_1h": {"color": "orange"},
            "ema_fast": {"color": "yellow"},
            "ema_slow": {"color": "blue"},
        },
        "subplots": {
            "RSI": {
                "rsi_1h": {"color": "red"},
            },
            "MACD": {
                "macd": {"color": "blue"},
                "macdsignal": {"color": "orange"},
                "macdhist": {"color": "green", "type": "bar"},
            },
        },
    }

    # ------------------ Indicators ------------------
    def hlc3(self, df: DataFrame) -> pd.Series:
        return (df["high"] + df["low"] + df["close"]) / 3.0

    def compute_vwap(self, df: DataFrame, window: int) -> pd.Series:
        hlc3 = self.hlc3(df)
        pv = hlc3 * df["volume"]
        pv_sum = pv.rolling(window, min_periods=1).sum()
        vol_sum = df["volume"].rolling(window, min_periods=1).sum()
        vwap = (pv_sum / vol_sum.replace(0, np.nan)).ffill().fillna(df["close"])
        return vwap

    def populate_indicators(self, df: DataFrame, metadata: dict) -> DataFrame:
        # Single timeframe (1h) only - no multi-timeframe dependencies
        df["rsi_1h"] = ta.RSI(df, timeperiod=self.RSI_PERIOD)
        df["vwap_1h"] = self.compute_vwap(df, self.VWAP_WINDOW)
        df["vwap_gap_1h"] = np.where(df["vwap_1h"] > 0, (df["close"] / df["vwap_1h"]) - 1.0, 0.0)
        # EMAs
        #df["ema_fast"] = ta.EMA(df, timeperiod=6)
        #df["ema_slow"] = ta.EMA(df, timeperiod=24)

        # MACD
        #macd = ta.MACD(df)
        #df["macd"] = macd["macd"]
        #df["macdsignal"] = macd["macdsignal"]
        #df["macdhist"] = macd["macdhist"]
        return df

    # Freqtrade 2025.10+ requires populate_entry_trend/populate_exit_trend
    def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
        df["enter_long"] = 0
        df.loc[
            (df["rsi_1h"] <= self.RSI_THRESHOLD) 
            & (df["vwap_gap_1h"] < self.VWAP_GAP)
            #& (df['open'] > df['close'].shift(1))
            & (df['open'].shift(1) > df['close'].shift(2))
            & (df['open'].shift(2) > df['close'].shift(3)),
            #& (df["ema_fast"] > df["ema_slow"]), 
            # EMA cross upward
            #& (df["macdhist"] > 0), 
            # Histogram positive
            # (df["macd"] > df["macdsignal"]),
            "enter_long",
        ] = 1
        return df

    def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
        df["exit_long"] = 0
        return df

    # ------------------ Custom Stake ------------------
    def custom_stake_amount(self, pair: str, current_time: pd.Timestamp, current_rate: float, **kwargs) -> float:
        balance = self.wallets.get_total_stake_amount()
        free = self.wallets.get_available_stake_amount()
        used = max(balance - free, 0)
        remaining = max(balance - used, 0)

        trade = kwargs.get("trade", None)
        stage = trade.nr_of_successful_entries if trade else 0
        
        # Total entries = DCA_STEP + 1 (Initial), since no cut loss, no need to add 1
        total_steps = self.DCA_STEP

        if stage == 0:
            stake = balance / total_steps
        else:
            # Remaining steps including this one
            remaining_steps = total_steps - stage
            if remaining_steps > 0:
                stake = remaining / remaining_steps
            else:
                stake = 0

        return float(max(min(stake, remaining), 0.0))

    # ------------------ DCA Logic ------------------
    def adjust_trade_position(self, trade, current_time, current_rate, current_profit, **kwargs):
        self.logger.info(f"[{current_time}] {trade.pair} | DCA check stage={trade.nr_of_successful_entries}")
        if self._last_dca_stage is None:
            self._last_dca_stage = {}

        trade_id = getattr(trade, "id", None) or f"{trade.pair}_{getattr(trade, 'open_date', None)}"
        current_stage = trade.nr_of_successful_entries
        recorded = self._last_dca_stage.get(trade_id)

        if recorded is not None and recorded == current_stage:
            return 0
        
        # Stop if we reached max entries (Initial + DCA_STEP)
        if current_stage >= (self.DCA_STEP + 1):
            return 0

        avg_rate = trade.open_rate
        drop_ratio = (current_rate / avg_rate) - 1.0
        next_dca_trigger = -self.DCA_THRESHOLD

        free_balance = self.wallets.get_available_stake_amount()
        est_stake = self.custom_stake_amount(trade.pair, current_time, current_rate, trade=trade)

        if drop_ratio <= next_dca_trigger and free_balance >= est_stake:
            next_stage = current_stage + 1
            tag = f"DCA_{next_stage}"
            self._last_dca_stage[trade_id] = current_stage

            self.logger.info(
                f"[{current_time}] {trade.pair} | Triggering {tag} at {current_rate:.4f} "
                f"({drop_ratio*100:.2f}%) | Free={free_balance:.2f} Stake={est_stake:.2f}"
            )
            trade.enter_tag = tag
            return est_stake  # ✅ FIXED: execute with actual stake

        return 0

    # ------------------ Exit Logic ------------------
    def custom_exit(self, pair: str, trade, current_time, current_rate, **kwargs):
        avg_price = trade.open_rate
        dca_stage = trade.nr_of_successful_entries
        rel = (current_rate / avg_price) - 1.0

        try:
            df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            rsi_1h = df.iloc[-1]["rsi_1h"]
        except Exception:
            rsi_1h = 50
            
        if rel >= self.TP_THRESHOLD and rsi_1h >= self.RSI_TP: 
            self.logger.info(f"[{current_time}] {pair} | TAKE_PROFIT reached +{rel*100:.2f}%")
            self._last_dca_stage.pop(f"{pair}_{trade.open_date}", None)
            return "TAKE_PROFIT"

        # Stop loss after all DCAs are used
        #if dca_stage >= (self.DCA_STEP + 1) and rel <= -self.DCA_THRESHOLD:
        #    self.logger.info(f"[{current_time}] {pair} | STOP_LOSS_AFTER_DCA triggered {rel*100:.2f}%")
        #    self._last_dca_stage.pop(f"{pair}_{trade.open_date}", None)
        #    return "STOP_LOSS_AFTER_DCA"

        return None