# source: https://raw.githubusercontent.com/nisrg77/trd-eng/9914fa458fc395207847d3cc0228708b706e2e9b/strategies/VolatilitySystem.py
# directory_url: https://github.com/nisrg77/trd-eng/blob/main/strategies/
# User: nisrg77
# Repository: trd-eng
# --------------------# Source: https://github.com/freqtrade/freqtrade-strategies
#         (user_data/strategies/futures/Github_nisrg77_trd_eng__VolatilitySystem__20260924_194040.py). Educational use only.
# Requires: "trading_mode": "futures", and the `technical` library (pip install technical).
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove these libs ---
from datetime import datetime
from typing import Optional

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

import talib.abstract as ta

from freqtrade.persistence import Trade
from freqtrade.strategy import (CategoricalParameter, DecimalParameter,
                                IntParameter, IStrategy)
from freqtrade.exchange import date_minus_candles
import freqtrade.vendor.qtpylib.indicators as qtpylib
from technical.util import resample_to_interval, resampled_merge


class Github_nisrg77_trd_eng__VolatilitySystem__20260924_194040(IStrategy):
    """
    Volatility System strategy.
    Based on https://www.tradingview.com/script/3hhs0XbR/
    Leverage is optional but the lower the better to limit liquidations
    """

    can_short = True

    # NOTE: as shipped, ROI is effectively disabled and stoploss is -1 (100%).
    # Signals reverse the position; there is NO protective stop. Add one before live use.
    minimal_roi = {
        "0": 100
    }

    stoploss = -1

    timeframe = '1h'

    plot_config = {
        'main_plot': {
        },
        'subplots': {
            "Volatility system": {
                "atr": {"color": "white"},
                "abs_close_change": {"color": "red"},
            }
        }
    }

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        resample_int = 60 * 3
        resampled = resample_to_interval(dataframe, resample_int)

        # Average True Range (ATR)
        resampled['atr'] = ta.ATR(resampled, timeperiod=14) * 2.0

        # Absolute close change
        resampled['close_change'] = resampled['close'].diff()
        resampled['abs_close_change'] = resampled['close_change'].abs()

        dataframe = resampled_merge(dataframe, resampled, fill_na=True)
        dataframe['atr'] = dataframe[f'resample_{resample_int}_atr']
        dataframe['close_change'] = dataframe[f'resample_{resample_int}_close_change']
        dataframe['abs_close_change'] = dataframe[f'resample_{resample_int}_abs_close_change']

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe['close_change'] * 1 > dataframe['atr'].shift(1)),
            'enter_long'] = 1

        dataframe.loc[
            (dataframe['close_change'] * -1 > dataframe['atr'].shift(1)),
            'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        use sell/buy signals as long/short indicators
        """
        dataframe.loc[
            dataframe['enter_long'] == 1,
            'exit_short'] = 1

        dataframe.loc[
            dataframe['enter_short'] == 1,
            'exit_long'] = 1

        return dataframe

    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                            proposed_stake: float, min_stake: Optional[float], max_stake: float,
                            leverage: float, entry_tag: Optional[str], side: str,
                            **kwargs) -> float:
        # 50% stake amount on initial entry
        return proposed_stake / 2

    position_adjustment_enable = True

    def adjust_trade_position(self, trade: Trade, current_time: datetime,
                              current_rate: float, current_profit: float,
                              min_stake: Optional[float], max_stake: float,
                              current_entry_rate: float, current_exit_rate: float,
                              current_entry_profit: float, current_exit_profit: float,
                              **kwargs) -> Optional[float]:
        dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)

        if len(dataframe) > 2:
            last_candle = dataframe.iloc[-1].squeeze()
            previous_candle = dataframe.iloc[-2].squeeze()
            signal_name = 'enter_long' if not trade.is_short else 'enter_short'
            prior_date = date_minus_candles(self.timeframe, 1, current_time)
            # Only enlarge position on new signal.
            if (
                last_candle[signal_name] == 1
                and previous_candle[signal_name] != 1
                and trade.nr_of_successful_entries < 2
                and trade.orders[-1].order_date_utc < prior_date
            ):
                return trade.stake_amount
        return None

    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, side: str,
                 **kwargs) -> float:
        """
        Customize leverage for each new trade. This method is only called in futures mode.
        :return: A leverage amount, which is between 1.0 and max_leverage.
        """
        return 2.0
