# source: https://raw.githubusercontent.com/opythonic/mw-bot-test/fb8ac5e16e43bc7875badca86ef4d7a0b836bd6b/user_data/strategies/SimultaneousHedgingStrategy.py
# --- Do not remove these imports ---
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, CategoricalParameter
from pandas import DataFrame
# --------------------------------
# Add your lib to import here
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib

class Github_opythonic_mw_bot_test__SimultaneousHedgingStrategy__20250628_194249(IStrategy):
    """
    Strategy to open simultaneous long and short positions for hedging.
    This example attempts to always hold both a long and a short position.
    Includes Hyperopt spaces.
    """
    # Strategy interface version
    INTERFACE_VERSION = 3

    # Enable shorting
    can_short = True

    # THIS IS CRUCIAL for allowing simultaneous long and short on the SAME PAIR
    strategy_supports_edged_trading = True

    # --- Hyperopt Spaces ---

    # ROI table space
    roi_profit_target = DecimalParameter(0.005, 0.05, default=0.02, decimals=3, space="buy", optimize=True)

    # Stoploss space - Freqtrade expects this to be negative.
    # Naming this 'stoploss' (matching the class attribute) allows Freqtrade to auto-assign its .value.
    stoploss = DecimalParameter(-0.05, -0.005, default=-0.01, decimals=3, space="buy", optimize=True)

    # Trailing stoploss parameters
    # Naming these to match strategy attributes allows Freqtrade to auto-assign their .value.
    trailing_stop = CategoricalParameter([True, False], default=True, space="buy", optimize=True)
    trailing_stop_positive = DecimalParameter(0.005, 0.05, default=0.01, decimals=3, space="buy", optimize=True)
    trailing_stop_positive_offset = DecimalParameter(0.002, 0.03, default=0.005, decimals=3, space="buy", optimize=True)
    trailing_only_offset_is_reached = CategoricalParameter([True, False], default=True, space="buy", optimize=True)

    # Bollinger Bands parameters
    bb_window = IntParameter(10, 50, default=20, space="buy", optimize=True)
    bb_stds = DecimalParameter(1.5, 3.5, default=2.0, decimals=1, space="buy", optimize=True)

    # --- Class Attributes ---
    # minimal_roi will be populated in __init__
    # stoploss, trailing_stop etc. are now directly defined as Hyperopt parameters above.

    def __init__(self, config: dict):
        super().__init__(config)
        # Construct the minimal_roi dictionary using the value from the hyperopt parameter
        self.minimal_roi = {
            "0": self.roi_profit_target.value
        }

    # Optimal timeframe for the strategy.
    timeframe = '5m'

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = True

    # These values can be overridden in the config.
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False # Set to True if you don't want ROI exits to prevent re-entry

    # Number of candles the strategy requires before producing valid signals
    # This should be dynamic based on bb_window.value if possible, or set to a max sensible value.
    # For simplicity, we'll use a fixed value, but for robust hyperopt, this should be considered.
    @property
    def startup_candle_count(self) -> int:
        return self.bb_window.value # Or max(self.bb_window.value, any_other_lookback_period)


    # Optional order type mapping.
    order_types = {
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False # Set to True if supported and desired
    }

    # Optional order time in force.
    order_time_in_force = {
        'entry': 'gtc',
        'exit': 'gtc'
    }

    plot_config = {
        'main_plot': {
            'bb_upperband': {'color': 'blue'},
            'bb_middleband': {'color': 'orange'},
            'bb_lowerband': {'color': 'blue'},
        },
        'subplots': {} # No subplots needed for this simple BB strategy
    }

    # def informative_pairs(self):
    #     return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Adds Technical Analysis indicators to the given DataFrame
        """
        # Bollinger Bands
        bollinger = qtpylib.bollinger_bands(
            qtpylib.typical_price(dataframe),
            window=self.bb_window.value,
            stds=self.bb_stds.value
        )
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Always attempt to have a long and a short position.
        Freqtrade will manage not opening a new position if one of that side already exists.
        """

        # Set conditions to True to always attempt to enter if a position for that side is not already open.
        # Freqtrade handles the logic of not opening duplicate positions (e.g. if a long is open, it won't open another long).
        # With strategy_supports_edged_trading=True, it allows one long AND one short simultaneously for the same pair.

        # Long entry: Price closes below lower Bollinger Band (Reversal)
        dataframe.loc[
            (
                (dataframe['close'] < dataframe['bb_lowerband']) &
                (dataframe['volume'] > 0)
            ),
            ['enter_long', 'enter_tag']] = (1, 'bb_lower_reversal_long')

        # Short entry: Price closes above upper Bollinger Band (Reversal)
        dataframe.loc[
            (
                (dataframe['close'] > dataframe['bb_upperband']) &
                (dataframe['volume'] > 0)
            ),
            ['enter_short', 'enter_tag']] = (1, 'bb_upper_reversal_short')

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Exit conditions based on Bollinger Bands.
        These custom exits will work alongside ROI, stoploss, and trailing stoploss.
        """
        # Exit long conditions: Price touches or crosses middle band from below
        dataframe.loc[
            (
                (dataframe['close'] >= dataframe['bb_middleband']) &
                (dataframe['volume'] > 0)
            ),
            ['exit_long', 'exit_tag']] = (1, 'bb_middle_exit_long')

        # Exit short conditions: Price touches or crosses middle band from above
        dataframe.loc[
            (
                (dataframe['close'] <= dataframe['bb_middleband']) &
                (dataframe['volume'] > 0)
            ),
            ['exit_short', 'exit_tag']] = (1, 'bb_middle_exit_short')

        return dataframe
