# 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 ScalperStrategy(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)))