# source: https://raw.githubusercontent.com/mkaganm/Sentinel/efdbb2d60daa4c0fb0249f7c71343929d526ce10/user_data/strategies/SentinelStrategy.py
"""
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 Github_mkaganm_Sentinel__SentinelStrategy__20251207_210625(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

