# source: https://raw.githubusercontent.com/wesire/tradingbot/3885c0eab3926e798fa2249093a2db97b6940b49/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 numpy as np
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


class Github_wesire_tradingbot__btc_scalp_strategy__20260216_225514(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
    
    # 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 = 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 = config.REGIME_HTF_TIMEFRAME
    regime_ema_fast = config.REGIME_EMA_FAST
    regime_ema_slow = config.REGIME_EMA_SLOW
    regime_adx_threshold = config.REGIME_ADX_THRESHOLD
    regime_adx_period = config.REGIME_ADX_PERIOD
    
    # Entry Signals
    entry_ema_period = config.ENTRY_EMA_PERIOD
    entry_rsi_period = config.ENTRY_RSI_PERIOD
    entry_rsi_long_threshold = config.ENTRY_RSI_LONG_THRESHOLD
    entry_rsi_short_threshold = config.ENTRY_RSI_SHORT_THRESHOLD
    
    # Filters
    filter_atr_period = config.FILTER_ATR_PERIOD
    filter_atr_min_threshold = config.FILTER_ATR_MIN_THRESHOLD
    filter_volume_period = config.FILTER_VOLUME_PERIOD
    entry_volume_multiplier = config.ENTRY_VOLUME_MULTIPLIER
    
    # Position sizing
    position_adjustment_enable = True
    max_entry_position_adjustment = 0  # No DCA/averaging
    
    def __init__(self, config_dict: dict):
        super().__init__(config_dict)
        
        # 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=config.MAX_RISK_PER_TRADE,
            max_daily_drawdown=config.MAX_DAILY_DRAWDOWN,
            max_consecutive_losses=config.MAX_CONSECUTIVE_LOSSES,
            cooldown_minutes=config.COOLDOWN_MINUTES,
            stop_loss_atr_multiplier=config.STOP_LOSS_ATR_MULTIPLIER,
            stop_loss_max_percent=config.STOP_LOSS_MAX_PERCENT,
            take_profit_r_levels=config.TAKE_PROFIT_R_LEVELS,
            breakeven_after_first_tp=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
        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).
        """
        # 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 = 'long' if trade.is_short is False else 'short'
        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.
        """
        # Check if daily drawdown limit reached
        # Note: In production, you'd get actual equity from exchange
        # For backtesting, this is approximate
        
        # 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.
        """
        # Check if trading is allowed (daily drawdown, cooldown, etc.)
        # Note: In backtesting, equity tracking is approximate
        can_trade, reason = self.risk_engine.can_trade(equity=10000.0)
        
        if not can_trade:
            return False
        
        return True
    
    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
