# source: https://raw.githubusercontent.com/genieys/vwap1/f59869c32c272d1961c8e420d8837c8f913d4e58/VwapDmiAlgo.py
# pylint: disable=C0301, E1101
# Import all necessary modules
from freqtrade.strategy import IStrategy, merge_informative_pair
from pandas import DataFrame
import talib.abstract as ta
import numpy as np
from datetime import datetime
from freqtrade.persistence import Trade

class Github_genieys_vwap1__VwapDmiAlgo__20251208_091756(IStrategy):
    """
    Freqtrade strategy implementing the VWAP + DMI algorithm described
    in the video: VWAP cross, DMI cross, and ADX filter.
    """
    # Strategy settings
    timeframe = '1h'
    can_short: bool = True  # Strategy has both long and short entry rules

    # Setting a very high stoploss to allow custom_stoploss to override
    stoploss = -0.99 
    startup_candle_count = 200 

    # We set minimal_roi to a high value so it doesn't trigger a default take-profit,
    # as the TP is handled dynamically via custom_stoploss and custom_exit.
    minimal_roi = {
        "0": 0.99  
    }

    # VWAP settings (window size mentioned in the video)
    VWAP_WINDOW = 200
    # DMI settings (default 14)
    DMI_WINDOW = 14
    # ADX filter threshold
    ADX_THRESHOLD = 20
    # ATR multiplier for stop-loss distance
    ATR_MULTIPLIER = 2.5
    # R:R ratio for Take-Profit
    RR_RATIO = 3.0


    # --- Indicator Population ---
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # 1. DMI (Directional Movement Index) Indicators
        # FIX: The original ta.ADX call was incorrect for returning all three indicators.
        # We must call ADX, PLUS_DI, and MINUS_DI separately.
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=self.DMI_WINDOW)
        dataframe['plus_di'] = ta.PLUS_DI(dataframe, timeperiod=self.DMI_WINDOW)
        dataframe['minus_di'] = ta.MINUS_DI(dataframe, timeperiod=self.DMI_WINDOW)

        # 2. ATR (Average True Range) for stop-loss
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=self.DMI_WINDOW)

        # 3. Rolling VWAP
        tp_vol = (dataframe['volume'] * (dataframe['high'] + dataframe['low'] + dataframe['close']) / 3).rolling(self.VWAP_WINDOW).sum()
        total_vol = dataframe['volume'].rolling(self.VWAP_WINDOW).sum()
        dataframe['vwap_200'] = tp_vol / total_vol

        return dataframe


    # --- Entry Logic ---
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Long Entry Conditions
        long_conditions = [
            # 1. Price crosses above VWAP
            (dataframe['close'] > dataframe['vwap_200']),
            (dataframe['close'].shift(1) <= dataframe['vwap_200'].shift(1)),
            # 2. +DI is above -DI (Bullish bias)
            (dataframe['plus_di'] > dataframe['minus_di']),
            # 3. ADX is above 20 (Trend Confirmation)
            (dataframe['adx'] > self.ADX_THRESHOLD),
        ]
        
        dataframe.loc[
            (
                (dataframe['volume'] > 0) &
                np.all(long_conditions, axis=0)
            ),
            'enter_long'] = 1

        # Short Entry Conditions
        short_conditions = [
            # 1. Price crosses below VWAP
            (dataframe['close'] < dataframe['vwap_200']),
            (dataframe['close'].shift(1) >= dataframe['vwap_200'].shift(1)),
            # 2. -DI is above +DI (Bearish bias)
            (dataframe['minus_di'] > dataframe['plus_di']),
            # 3. ADX is above 20 (Trend Confirmation)
            (dataframe['adx'] > self.ADX_THRESHOLD),
        ]
        
        dataframe.loc[
            (
                (dataframe['volume'] > 0) &
                np.all(short_conditions, axis=0)
            ),
            'enter_short'] = 1

        return dataframe


    # --- Exit Logic (SL/TP) ---
 def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float,
                        initial: float, **kwargs) -> float:
        
        # --- 1. Calculate and Store Risk/Reward on the first run ---
        if 'initial_risk_distance' not in trade.data:
            # Get the dataframe for the ATR value at the entry candle.
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            # Find the last complete candle *before* the trade was opened
            entry_candle = dataframe.loc[dataframe.index < trade.open_date].iloc[-1]
            entry_atr = entry_candle['atr']
            
            # Calculate Risk (SL distance) and Reward (TP distance)
            risk_distance = self.ATR_MULTIPLIER * entry_atr
            reward_distance = risk_distance * self.RR_RATIO
            
            # Store the distance for future use
            trade.data['initial_risk_distance'] = risk_distance

            # Calculate the Stop-Loss Price
            stoploss_price = (
                trade.open_rate - risk_distance if trade.is_open_long
                else trade.open_rate + risk_distance
            )
            trade.data['stop_loss_price'] = stoploss_price

            # Calculate the Take-Profit Price
            take_profit_price = (
                trade.open_rate + reward_distance if trade.is_open_long
                else trade.open_rate - reward_distance
            )
            # Store the TP price on the trade object for later assignment
            trade.data['take_profit_price'] = take_profit_price
            
        # --- 2. Set the Take-Profit price (THE FIX) ---
        # Freqtrade uses the 'limit' property to track the Take-Profit price.
        trade.limit = trade.data['take_profit_price']

        # --- 3. Calculate and Return the Stop-Loss Percentage ---
        stoploss_price = trade.data['stop_loss_price']
        
        # Convert the SL price back into a negative percentage from the open rate
        sl_percentage = (stoploss_price / trade.open_rate) - 1.0
        
        return sl_percentage


    # --- Exit Logic (Must be kept empty to rely on trade.adjust_trade_entry for TP) ---
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Exit logic is handled dynamically via `custom_stoploss` which sets the TP price.
        """
        return dataframe
