# source: https://raw.githubusercontent.com/wesire/tradingbot/ab4c8e2493255e8d45788466eec8dca226b7b794/bot/strategy/btc_scalp_strategy.py
"""
BTC/USDT Perpetual Futures Scalping Strategy for Freqtrade.
Implements regime filtering, multi-timeframe analysis, and dynamic entries.
"""
from typing import Optional
import pandas as pd
from pandas import DataFrame
from freqtrade.strategy import IStrategy, informative
import talib.abstract as ta

from bot.strategy.signal_filters import SignalFilters
from bot.strategy.risk_engine import RiskEngine
from bot.config.default_config import config as app_config


class Github_wesire_tradingbot__btc_scalp_strategy__20260218_014553(IStrategy):
    """
    BTC/USDT scalping strategy with regime filtering and dynamic risk management.

    Strategy Logic:
    1. Regime Filter (HTF): EMA crossover + ADX for trend direction/strength
    2. Entry Signals (LTF): Pullback/bounce entries with RSI confirmation
    3. Filters: Volatility (ATR) and volume spike detection
    4. Risk Management: Dynamic stops, partial TPs, breakeven logic
    """

    # Strategy metadata
    INTERFACE_VERSION = 3

    # Enable shorts (futures)
    can_short = True

    # Minimal ROI - handled by custom exit logic
    minimal_roi = {
        "0": 0.10,
        "30": 0.05,
        "60": 0.02,
        "120": 0.01
    }

    # Stoploss
    stoploss = -0.02  # Hard 2% stop as fallback

    # Trailing stop
    trailing_stop = False

    # Optimal timeframe
    timeframe = app_config.PRIMARY_TIMEFRAME

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

    # Use exit signals
    use_exit_signal = True
    exit_profit_only = False
    exit_profit_offset = 0.0

    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 250

    # Strategy parameters (can be optimized)
    # Regime Filter
    regime_htf_timeframe = app_config.REGIME_HTF_TIMEFRAME
    regime_ema_fast = app_config.REGIME_EMA_FAST
    regime_ema_slow = app_config.REGIME_EMA_SLOW
    regime_adx_threshold = app_config.REGIME_ADX_THRESHOLD
    regime_adx_period = app_config.REGIME_ADX_PERIOD

    # Entry Signals
    entry_ema_period = app_config.ENTRY_EMA_PERIOD
    entry_rsi_period = app_config.ENTRY_RSI_PERIOD
    entry_rsi_long_threshold = app_config.ENTRY_RSI_LONG_THRESHOLD
    entry_rsi_short_threshold = app_config.ENTRY_RSI_SHORT_THRESHOLD

    # Filters
    filter_atr_period = app_config.FILTER_ATR_PERIOD
    filter_atr_min_threshold = app_config.FILTER_ATR_MIN_THRESHOLD
    filter_volume_period = app_config.FILTER_VOLUME_PERIOD
    entry_volume_multiplier = app_config.ENTRY_VOLUME_MULTIPLIER

    # Position sizing
    position_adjustment_enable = True
    max_entry_position_adjustment = 0  # No DCA/averaging

    def __init__(self, config: dict, *args, **kwargs):
        # Freqtrade passes strategy config as keyword arg: config=...
        super().__init__(config)

        # Initialize signal filters and risk engine
        self.signal_filters = SignalFilters(
            atr_period=self.filter_atr_period,
            atr_min_threshold=self.filter_atr_min_threshold,
            volume_period=self.filter_volume_period,
            volume_multiplier=self.entry_volume_multiplier
        )

        self.risk_engine = RiskEngine(
            max_risk_per_trade=app_config.MAX_RISK_PER_TRADE,
            max_daily_drawdown=app_config.MAX_DAILY_DRAWDOWN,
            max_consecutive_losses=app_config.MAX_CONSECUTIVE_LOSSES,
            cooldown_minutes=app_config.COOLDOWN_MINUTES,
            stop_loss_atr_multiplier=app_config.STOP_LOSS_ATR_MULTIPLIER,
            stop_loss_max_percent=app_config.STOP_LOSS_MAX_PERCENT,
            take_profit_r_levels=app_config.TAKE_PROFIT_R_LEVELS,
            breakeven_after_first_tp=app_config.BREAKEVEN_AFTER_FIRST_TP
        )

    @informative('1h')
    def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Populate indicators for higher timeframe (regime detection).
        """
        # EMA for regime
        dataframe['ema_fast'] = ta.EMA(dataframe, timeperiod=self.regime_ema_fast)
        dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=self.regime_ema_slow)

        # ADX for trend strength
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=self.regime_adx_period)

        # Regime determination
        dataframe['regime_bullish'] = (
            (dataframe['ema_fast'] > dataframe['ema_slow']) &
            (dataframe['adx'] > self.regime_adx_threshold)
        )

        dataframe['regime_bearish'] = (
            (dataframe['ema_fast'] < dataframe['ema_slow']) &
            (dataframe['adx'] > self.regime_adx_threshold)
        )

        dataframe['regime_neutral'] = ~(
            dataframe['regime_bullish'] | dataframe['regime_bearish']
        )

        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Populate indicators for execution timeframe.
        """
        # Entry EMA
        dataframe['ema'] = ta.EMA(dataframe, timeperiod=self.entry_ema_period)

        # VWAP (approximation using cumulative)
        dataframe['vwap'] = (
            (dataframe['close'] * dataframe['volume']).cumsum() /
            dataframe['volume'].cumsum()
        )

        # RSI for entry triggers
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.entry_rsi_period)

        # Apply signal filters (ATR, volume)
        dataframe = self.signal_filters.apply_all_filters(
            dataframe,
            enable_mean_reversion=True
        )

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Populate buy/sell signals based on regime and entry conditions.
        """
        # Long Entry Conditions
        long_conditions = [
            # HTF regime is bullish
            (dataframe['regime_bullish_1h'] == True),

            # Price pulled back to EMA/VWAP zone
            (dataframe['close'] <= dataframe['ema'] * 1.01),  # Within 1% of EMA

            # RSI recovery
            (dataframe['rsi'] > self.entry_rsi_long_threshold),
            (dataframe['rsi'].shift(1) <= self.entry_rsi_long_threshold),  # Cross above

            # Filters passed
            (dataframe['filters_passed'] == True),

            # Volume condition
            (dataframe['volume'] > 0)
        ]

        # Short Entry Conditions
        short_conditions = [
            # HTF regime is bearish
            (dataframe['regime_bearish_1h'] == True),

            # Price bounced to resistance/EMA zone
            (dataframe['close'] >= dataframe['ema'] * 0.99),  # Within 1% of EMA

            # RSI rollover
            (dataframe['rsi'] < self.entry_rsi_short_threshold),
            (dataframe['rsi'].shift(1) >= self.entry_rsi_short_threshold),  # Cross below

            # Filters passed
            (dataframe['filters_passed'] == True),

            # Volume condition
            (dataframe['volume'] > 0)
        ]

        # Combine conditions
        dataframe['enter_long'] = 0
        dataframe['enter_short'] = 0

        if long_conditions:
            dataframe.loc[
                pd.concat(long_conditions, axis=1).all(axis=1),
                'enter_long'
            ] = 1

        if short_conditions:
            dataframe.loc[
                pd.concat(short_conditions, axis=1).all(axis=1),
                'enter_short'
            ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Populate exit signals (optional - mainly using custom exit logic).
        """
        dataframe['exit_long'] = 0
        dataframe['exit_short'] = 0

        # Exit long when regime flips bearish
        dataframe.loc[
            (dataframe['regime_bearish_1h'] == True),
            'exit_long'
        ] = 1

        # Exit short when regime flips bullish
        dataframe.loc[
            (dataframe['regime_bullish_1h'] == True),
            'exit_short'
        ] = 1

        return dataframe

    def custom_stoploss(
        self,
        pair: str,
        trade: 'Trade',
        current_time: 'datetime',
        current_rate: float,
        current_profit: float,
        **kwargs
    ) -> Optional[float]:
        """
        Custom stoploss logic using ATR.
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

        if len(dataframe) == 0:
            return None

        current_candle = dataframe.iloc[-1]

        if 'atr' not in current_candle or pd.isna(current_candle['atr']):
            return None

        # Calculate dynamic stop
        side = 'short' if trade.is_short else 'long'
        stop_loss_price = self.risk_engine.calculate_stop_loss(
            entry_price=trade.open_rate,
            atr=current_candle['atr'],
            side=side
        )

        # Convert to stop loss ratio
        if side == 'long':
            stop_loss_ratio = (stop_loss_price / trade.open_rate) - 1
        else:
            stop_loss_ratio = 1 - (stop_loss_price / trade.open_rate)

        return stop_loss_ratio

    def custom_exit(
        self,
        pair: str,
        trade: 'Trade',
        current_time: 'datetime',
        current_rate: float,
        current_profit: float,
        **kwargs
    ) -> Optional[str]:
        """
        Custom exit logic for partial take profits.
        This is a simplified version - full implementation would track partials.
        """
        # Exit at first TP level (simplified)
        if current_profit >= 0.01:  # 1% profit (1R approximate)
            return 'take_profit_1'

        return None

    def confirm_trade_entry(
        self,
        pair: str,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        current_time: 'datetime',
        entry_tag: Optional[str],
        side: str,
        **kwargs
    ) -> bool:
        """
        Confirm trade entry with risk engine checks.
        """
        can_trade, _reason = self.risk_engine.can_trade(equity=10000.0)
        return can_trade

    def leverage(
        self,
        pair: str,
        current_time: 'datetime',
        current_rate: float,
        proposed_leverage: float,
        max_leverage: float,
        entry_tag: Optional[str],
        side: str,
        **kwargs
    ) -> float:
        """
        Set leverage for futures trading.
        Conservative 3x leverage for scalping.
        """
        return 3.0