# source: https://raw.githubusercontent.com/mupol313/hossain/fbb130e963268749c3c05cccea4f5e3b05054836/user_data/strategies/strats/Apollo11.py
from datetime import datetime
from datetime import timedelta
from functools import reduce
import freqtrade.vendor.qtpylib.indicators as qtpylib
import talib.abstract as ta
from freqtrade.persistence import Trade
from freqtrade.strategy import IStrategy
from pandas import DataFrame

def to_minutes(**timdelta_kwargs):
    return int(timedelta(**timdelta_kwargs).total_seconds() / 60)



class Github_mupol313_hossain__Apollo11__20240622_082213(IStrategy):
    INTERFACE_VERSION = 3
    timeframe = '15m'
    # Stoploss
    stoploss = -0.16
    startup_candle_count: int = 480
    trailing_stop = False
    use_custom_stoploss = True
    use_exit_signal = False
    # signal controls
    buy_signal_1 = True
    buy_signal_2 = True
    buy_signal_3 = True
    minimal_roi = {'0': 10}
    s1_ema_xs = 3
    s1_ema_sm = 5
    s1_ema_md = 10
    s1_ema_xl = 50
    s1_ema_xxl = 240
    # Signal 2
    s2_ema_input = 50
    s2_ema_offset_input = -1
    s2_bb_sma_length = 49
    s2_bb_std_dev_length = 64
    s2_bb_lower_offset = 3
    s2_fib_sma_len = 50
    s2_fib_atr_len = 14
    s2_fib_lower_value = 4.236
    s3_ema_long = 50
    s3_ema_short = 20
    s3_ma_fast = 10
    s3_ma_slow = 20

    @property
    def protections(self):
        return [{'method': 'CooldownPeriod', 'stop_duration': to_minutes(minutes=0)}, {'method': 'MaxDrawdown', 'lookback_period': to_minutes(hours=12), 'trade_limit': 20, 'stop_duration': to_minutes(hours=1), 'max_allowed_drawdown': 0.2}, {'method': 'StoplossGuard', 'lookback_period': to_minutes(hours=6), 'trade_limit': 4, 'stop_duration': to_minutes(minutes=30), 'only_per_pair': False}, {'method': 'LowProfitPairs', 'lookback_period': to_minutes(hours=1, minutes=30), 'trade_limit': 2, 'stop_duration': to_minutes(hours=15), 'required_profit': 0.02}, {'method': 'LowProfitPairs', 'lookback_period': to_minutes(hours=6), 'trade_limit': 4, 'stop_duration': to_minutes(minutes=30), 'required_profit': 0.01}]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Adding EMA's into the dataframe
        dataframe['s1_ema_xs'] = ta.EMA(dataframe, timeperiod=self.s1_ema_xs)
        dataframe['s1_ema_sm'] = ta.EMA(dataframe, timeperiod=self.s1_ema_sm)
        dataframe['s1_ema_md'] = ta.EMA(dataframe, timeperiod=self.s1_ema_md)
        dataframe['s1_ema_xl'] = ta.EMA(dataframe, timeperiod=self.s1_ema_xl)
        dataframe['s1_ema_xxl'] = ta.EMA(dataframe, timeperiod=self.s1_ema_xxl)
        s2_ema_value = ta.EMA(dataframe, timeperiod=self.s2_ema_input)
        s2_ema_xxl_value = ta.EMA(dataframe, timeperiod=200)
        dataframe['s2_ema'] = s2_ema_value - s2_ema_value * self.s2_ema_offset_input
        dataframe['s2_ema_xxl_off'] = s2_ema_xxl_value - s2_ema_xxl_value * self.s2_fib_lower_value
        dataframe['s2_ema_xxl'] = ta.EMA(dataframe, timeperiod=200)
        s2_bb_sma_value = ta.SMA(dataframe, timeperiod=self.s2_bb_sma_length)
        s2_bb_std_dev_value = ta.STDDEV(dataframe, self.s2_bb_std_dev_length)
        dataframe['s2_bb_std_dev_value'] = s2_bb_std_dev_value
        dataframe['s2_bb_lower_band'] = s2_bb_sma_value - s2_bb_std_dev_value * self.s2_bb_lower_offset
        s2_fib_atr_value = ta.ATR(dataframe, timeframe=self.s2_fib_atr_len)
        s2_fib_sma_value = ta.SMA(dataframe, timeperiod=self.s2_fib_sma_len)
        dataframe['s2_fib_lower_band'] = s2_fib_sma_value - s2_fib_atr_value * self.s2_fib_lower_value
        s3_bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=3)
        dataframe['s3_bb_lowerband'] = s3_bollinger['lower']
        dataframe['s3_ema_long'] = ta.EMA(dataframe, timeperiod=self.s3_ema_long)
        dataframe['s3_ema_short'] = ta.EMA(dataframe, timeperiod=self.s3_ema_short)
        dataframe['s3_fast_ma'] = ta.EMA(dataframe['volume'] * dataframe['close'], self.s3_ma_fast) / ta.EMA(dataframe['volume'], self.s3_ma_fast)
        dataframe['s3_slow_ma'] = ta.EMA(dataframe['volume'] * dataframe['close'], self.s3_ma_slow) / ta.EMA(dataframe['volume'], self.s3_ma_slow)
        # Volume weighted MACD
        dataframe['fastMA'] = ta.EMA(dataframe['volume'] * dataframe['close'], 12) / ta.EMA(dataframe['volume'], 12)
        dataframe['slowMA'] = ta.EMA(dataframe['volume'] * dataframe['close'], 26) / ta.EMA(dataframe['volume'], 26)
        dataframe['vwmacd'] = dataframe['fastMA'] - dataframe['slowMA']
        dataframe['signal'] = ta.EMA(dataframe['vwmacd'], 9)
        dataframe['hist'] = dataframe['vwmacd'] - dataframe['signal']
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # basic buy methods to keep the strategy simple
        if self.buy_signal_1:
            conditions = [dataframe['vwmacd'] < dataframe['signal'], dataframe['low'] < dataframe['s1_ema_xxl'], dataframe['close'] > dataframe['s1_ema_xxl'], qtpylib.crossed_above(dataframe['s1_ema_sm'], dataframe['s1_ema_md']), dataframe['s1_ema_xs'] < dataframe['s1_ema_xl'], dataframe['volume'] > 0]
            dataframe.loc[reduce(lambda x, y: x & y, conditions), ['enter_long', 'enter_tag']] = (1, 'buy_signal_1')
        if self.buy_signal_2:
            conditions = [qtpylib.crossed_above(dataframe['s2_fib_lower_band'], dataframe['s2_bb_lower_band']), dataframe['close'] < dataframe['s2_ema'], dataframe['volume'] > 0]
            dataframe.loc[reduce(lambda x, y: x & y, conditions), ['enter_long', 'enter_tag']] = (1, 'buy_signal_2')
        if self.buy_signal_3:
            conditions = [dataframe['low'] < dataframe['s3_bb_lowerband'], dataframe['high'] > dataframe['s3_slow_ma'], dataframe['high'] < dataframe['s3_ema_long'], dataframe['volume'] > 0]
            dataframe.loc[reduce(lambda x, y: x & y, conditions), ['enter_long', 'enter_tag']] = (1, 'buy_signal_3')
        if not all([self.buy_signal_1, self.buy_signal_2, self.buy_signal_3]):
            dataframe.loc[(), 'enter_long'] = 0
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # This is essentailly ignored as we're using strict ROI / Stoploss / TTP sale scenarios
        dataframe.loc[(), 'exit_long'] = 0
        return dataframe

    def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
        if current_profit > 0.2:
            return 0.04
        if current_profit > 0.1:
            return 0.03
        if current_profit > 0.06:
            return 0.02
        if current_profit > 0.03:
            return 0.01
        # Let's try to minimize the loss
        if current_profit <= -0.1:
            if trade.open_date_utc + timedelta(hours=60) < current_time:
                # After 60H since buy
                return current_profit / 1.75
        if current_profit <= -0.08:
            if trade.open_date_utc + timedelta(hours=120) < current_time:
                # After 120H since buy
                return current_profit / 1.7
        return -1