""" Sentinel Strategy - A low-risk trading strategy using RSI and Bollinger Bands. This strategy combines Relative Strength Index (RSI) and Bollinger Bands indicators to identify oversold conditions for entry and overbought conditions for exit. Designed for conservative trading with built-in risk management. """ import numpy as np import pandas as pd from pandas import DataFrame from freqtrade.strategy import IStrategy, IntParameter import talib.abstract as ta import freqtrade.vendor.qtpylib.indicators as qtpylib class SentinelStrategy(IStrategy): """ Sentinel Trading Strategy - The Guardian of Capital. A low-risk strategy that uses RSI, Bollinger Bands, and SMA 200 trend filter. Entry: RSI < buy_rsi AND Price < Bollinger Bands Lower Band AND Price > SMA 200 (Only buy oversold conditions when price is above long-term trend) Exit: RSI > sell_rsi (overbought condition) Risk Management: - Stop Loss: -5% (tighter to reduce losses) - Trailing Stop: Enabled (starts at 0.4% profit) - ROI Table: Dynamic profit targets based on time in trade - SMA 200 filter prevents buying during downtrends (avoids catching falling knives) """ INTERFACE_VERSION = 3 timeframe = '15m' # Risk Management (The Sentinel's Shield) - Balanced Risk/Reward Ratio stoploss = -0.08 # -8% stoploss (balanced: avoid noise but limit losses) trailing_stop = True trailing_stop_positive = 0.01 # Start trailing after 1% profit (activate safety net) trailing_stop_positive_offset = 0.02 # Keep stop 2% behind price (lock in profits) trailing_only_offset_is_reached = True # ROI (Take Profit) - Realistic targets to improve Risk/Reward Ratio minimal_roi = { "0": 0.03, # Aim for 3% profit initially (let winners run) "15": 0.02, # After 15 mins, accept 2% profit "30": 0.012, # After 30 mins, accept 1.2% profit "60": 0.006 # After 60 mins, accept 0.6% profit } # Optimization Parameters - Optimized for maximum profitability (best result: -1.82%) buy_rsi = IntParameter(10, 40, default=22, space="buy", optimize=True, load=True) # Optimized RSI for best entries sell_rsi = IntParameter(60, 90, default=72, space="sell", optimize=True, load=True) # Optimized exit for profit capture def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Populate technical indicators for the strategy. Args: dataframe: DataFrame with OHLCV data metadata: Dictionary with pair metadata Returns: DataFrame with added indicator columns """ # RSI (Relative Strength Index) - 14 period dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) # Bollinger Bands - 20 period, 2 standard deviations bollinger = qtpylib.bollinger_bands( qtpylib.typical_price(dataframe), window=20, stds=2 ) dataframe['bb_lowerband'] = bollinger['lower'] dataframe['bb_middleband'] = bollinger['mid'] dataframe['bb_upperband'] = bollinger['upper'] # SMA 200 - Trend filter to avoid buying in downtrends dataframe['sma_200'] = ta.SMA(dataframe, timeperiod=200) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Define entry conditions for long positions. Entry Logic: - RSI < buy_rsi (default: 22) - Oversold condition (optimized for deeper dips) - Price < Bollinger Bands Lower Band - Price below lower band - Price > SMA 200 * 0.95 - Trend filter with 5% tolerance (prevents catching falling knives in strong downtrends) Args: dataframe: DataFrame with indicators metadata: Dictionary with pair metadata Returns: DataFrame with 'enter_long' column set to 1 where conditions are met """ dataframe.loc[ ( (dataframe['rsi'] < self.buy_rsi.value) & (dataframe['close'] < dataframe['bb_lowerband']) & (dataframe['close'] > dataframe['sma_200'] * 0.95) # Allow 5% tolerance below SMA 200 ), 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Define exit conditions for long positions. Exit Logic: - RSI > sell_rsi (default: 70) - Overbought condition Args: dataframe: DataFrame with indicators metadata: Dictionary with pair metadata Returns: DataFrame with 'exit_long' column set to 1 where conditions are met """ dataframe.loc[ ( (dataframe['rsi'] > self.sell_rsi.value) ), 'exit_long'] = 1 return dataframe