# source: https://raw.githubusercontent.com/wynnforthework/quant-strategies-knowledge/523670ea407d3e361fdf8adcd2c74819d2275e83/freqtrade-strategies/ScalperStrategy.py
# Filename: ScalpingStrategy.py
# Author: Grok 4
# Description: A scalping strategy for Freqtrade on Binance futures, with hyperopt support and dynamic leverage.
# Supports 1m timeframe, RSI and EMA crossover for entries/exits.
# Dynamic leverage: Adjusts based on volatility (ATR) - higher volatility, lower leverage to reduce risk.

from freqtrade.strategy import IStrategy, informative
from freqtrade.strategy.parameters import IntParameter, DecimalParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib

class Github_wynnforthework_quant_strategies_knowledge__ScalperStrategy__20251007_112722(IStrategy):
    """
    高频剥头皮策略
    - 时间周期: 1m
    - 信号: EMA + VWAP + 成交量激增
    - 止盈止损: 小幅快速止盈，极紧止损
    """
    # Strategy metadata
    INTERFACE_VERSION = 3
    timeframe = '1m'  # High frequency: 1 minute candles
    minimal_roi = {"0": 0.002}  # Minimal ROI: 0.2% to exit
    stoploss = -0.005  # Fixed stoploss: -0.5%
    trailing_stop = False  # Optional: Can enable for better exits
    can_short: bool = True

    # Hyperopt parameters - These will be optimized
    rsi_period = IntParameter(5, 20, default=14, space='buy')
    rsi_buy_threshold = IntParameter(20, 40, default=30, space='buy')
    rsi_sell_threshold = IntParameter(60, 80, default=70, space='sell')
    ema_fast_period = IntParameter(3, 10, default=5, space='buy')
    ema_slow_period = IntParameter(10, 30, default=15, space='sell')
    max_leverage = DecimalParameter(5.0, 20.0, default=10.0, space='buy')  # Max leverage for dynamic adjustment

    # Remove duplicate custom_leverage method (keeping only the leverage method below)

    def informative_pairs(self):
        # No additional informative pairs needed
        return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Calculate indicators
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.rsi_period.value)
        dataframe['ema_fast'] = ta.EMA(dataframe, timeperiod=self.ema_fast_period.value)
        dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=self.ema_slow_period.value)
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)  # For dynamic leverage
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Long entry: RSI oversold in an uptrend (EMA bullish) - wait for RSI bounce
        dataframe.loc[
            (dataframe['rsi'] < self.rsi_buy_threshold.value) &
            (dataframe['ema_fast'] > dataframe['ema_slow']),  # Uptrend confirmed
            'enter_long'] = 1

        # Short entry: RSI overbought in a downtrend (EMA bearish) - wait for RSI drop
        dataframe.loc[
            (dataframe['rsi'] > self.rsi_sell_threshold.value) &
            (dataframe['ema_fast'] < dataframe['ema_slow']),  # Downtrend confirmed
            'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Long exit: RSI overbought (or ROI/stoploss handles)
        dataframe.loc[
            (dataframe['rsi'] > self.rsi_sell_threshold.value),
            'exit_long'] = 1

        # Short exit: RSI oversold
        dataframe.loc[
            (dataframe['rsi'] < self.rsi_buy_threshold.value),
            'exit_short'] = 1

        return dataframe

    # 动态杠杆：根据波动率(ATR)和止损幅度自适应调整
    def leverage(self, pair, current_time, current_rate, proposed_leverage, max_leverage, entry_tag, side, **kwargs) -> float:
        """
        Dynamically adjust leverage based on volatility (ATR) and stoploss.
        Higher volatility -> lower leverage to reduce risk.
        """
        try:
            # Get the latest dataframe for ATR-based adjustment
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            if len(dataframe) > 0 and 'atr' in dataframe.columns:
                atr = dataframe['atr'].iloc[-1]
                if atr > 0:
                    # Normalize ATR as percentage
                    normalized_atr = atr / current_rate
                    # Reduce leverage when volatility is high
                    volatility_factor = max(0.5, 1 - min(normalized_atr * 50, 0.5))
                else:
                    volatility_factor = 1.0
            else:
                volatility_factor = 1.0
            
            # Also consider stoploss for leverage calculation
            sl = abs(float(self.stoploss)) if getattr(self, 'stoploss', None) is not None else 0.005
            sl_factor = 0.05 / sl if sl > 0 else 1.0
            
            # Combine both factors
            base_leverage = min(self.max_leverage.value, sl_factor) * volatility_factor
            final_leverage = max(1.0, min(float(base_leverage), float(max_leverage)))
            
            return final_leverage
        except Exception:
            # Fallback to safe leverage
            return max(1.0, min(5.0, float(max_leverage)))


