# source: https://raw.githubusercontent.com/1amcord/freqtrade_settings/b078a9d66f5e88f6ad18d35066241aaf91c31032/strategies/RSIDivergence.py
# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from typing import Dict, List
from functools import reduce
from pandas import DataFrame
# --------------------------------
from datetime import datetime
from datetime import timedelta

import pandas as pd
import talib.abstract as ta
import numpy as np
import freqtrade.vendor.qtpylib.indicators as qtpylib
import datetime
from technical.util import resample_to_interval, resampled_merge
from technical.indicators import atr
from freqtrade.strategy import DecimalParameter, IntParameter, BooleanParameter, merge_informative_pair, stoploss_from_open
from technical.util import resample_to_interval, resampled_merge
from freqtrade.persistence import Trade

import logging
logger = logging.getLogger(__name__)


rangeUpper = 60
rangeLower = 5

use_sell_signal = True
sell_profit_only = False
ignore_roi_if_buy_signal = False

# Elliot Wave Oscillator


def EWO(dataframe, sma1_length=5, sma2_length=35):
    df = dataframe.copy()
    sma1 = ta.SMA(df, timeperiod=sma1_length)
    sma2 = ta.SMA(df, timeperiod=sma2_length)
    smadif = (sma1 - sma2) / df['close'] * 100
    return smadif


def valuewhen(dataframe, condition, source, occurrence):
    copy = dataframe.copy()
    copy['colFromIndex'] = copy.index
    copy = copy.sort_values(
        by=[condition, 'colFromIndex'], ascending=False).reset_index(drop=True)
    copy['valuewhen'] = np.where(
        copy[condition] > 0, copy[source].shift(-occurrence), copy[source])
    copy['barrsince'] = copy['colFromIndex'] - \
        copy['colFromIndex'].shift(-occurrence)
    copy.loc[
        (
            (rangeLower <= copy['barrsince']) &
            (copy['barrsince'] <= rangeUpper)
        ), "in_range"] = 1
    copy['in_range'] = copy['in_range'].fillna(0)
    copy = copy.sort_values(by=['colFromIndex'],
                            ascending=True).reset_index(drop=True)
    return copy['valuewhen'], copy['in_range']


class github_1amcord_freqtrade_settings__RSIDivergence__20220814_101522(IStrategy):
    INTERFACE_VERSION = 2

    # Optimal timeframe for the strategy
    timeframe = '15m'
    inf_tf = '4h'

    # Buy hyperspace params:
    buy_params = {
        'use_bull': True,
        'use_hidden_bull': False,
        'use_rsi_smma_buy': True,
        "ewo_high": 5.835,
        "low_rsi_buy": 40,
        "high_rsi_buy": 40,
        "high_smma_rsi_buy": 35,
        "low_adx_buy": 30,
        "high_adx_buy": 30,
        "low_stoch_buy": 20,
        "high_stoch_buy": 80,
        "low_osc_buy": 80,
        "high_osc_buy": 80,
        "ewo_rol": 7,
    }
    # Sell hyperspace params:
    sell_params = {
        'low_rsi_sell': 65,
        'low_ewo_sell': 0.5
    }

    # ROI table:
    minimal_roi = {
        "0": 10,
    }

    # Stoploss:
    stoploss = -0.24

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.03
    trailing_stop_positive_offset = 0.04
    trailing_only_offset_is_reached = False
    use_custom_stoploss = False

    use_bull = BooleanParameter(
        default=buy_params['use_bull'], space='buy', optimize=True)
    use_hidden_bull = BooleanParameter(
        default=buy_params['use_hidden_bull'], space='buy', optimize=True)
    use_rsi_smma_buy = BooleanParameter(
        default=buy_params['use_rsi_smma_buy'], space='buy', optimize=True)
    # Protection
    fast_ewo = 5
    slow_ewo = 35
    ewo_high = DecimalParameter(
        0, 7.0, default=buy_params['ewo_high'], space='buy', optimize=False)
    low_ewo_sell = DecimalParameter(
        -0.5, 3, decimals=1, default=sell_params['low_ewo_sell'], space='sell', optimize=True)
    ewo_rol = IntParameter(
        0, 10, default=buy_params['ewo_rol'], space='buy', optimize=False)
    low_rsi_buy = IntParameter(
        0, 100, default=buy_params['low_rsi_buy'], space='buy', optimize=False)
    low_rsi_sell = IntParameter(
        40, 100, default=sell_params['low_rsi_sell'], space='sell', optimize=True)
    high_rsi_buy = IntParameter(
        15, 60, default=buy_params['high_rsi_buy'], space='buy', optimize=True)
    high_smma_rsi_buy = IntParameter(
        15, 60, default=buy_params['high_smma_rsi_buy'], space='buy', optimize=True)
    low_adx_buy = IntParameter(
        0, 100, default=buy_params['low_adx_buy'], space='buy', optimize=False)
    high_adx_buy = IntParameter(
        0, 100, default=buy_params['high_adx_buy'], space='buy', optimize=False)
    low_stoch_buy = IntParameter(
        0, 100, default=buy_params['low_stoch_buy'], space='buy', optimize=False)
    high_stoch_buy = IntParameter(
        0, 100, default=buy_params['high_stoch_buy'], space='buy', optimize=False)
    low_osc_buy = IntParameter(
        0, 100, default=buy_params['low_osc_buy'], space='buy', optimize=False)
    high_osc_buy = IntParameter(
        0, 100, default=buy_params['high_osc_buy'], space='buy', optimize=False)

    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 50

    osc = 'RSI'
    src = 'close'
    rsiLen = 14
    lbL = 40
    lbR = 5

    @property
    def plot_config(self):
        return {
            # Main plot indicators (Moving averages, ...)
            'main_plot': {
                f'sar_{self.inf_tf}': {'color': 'lightgreen'},
                # 'resample_360_sar': {'color': 'lightblue'},
                # 'fastd': {'color': 'lightblue'},
                # f'sl_10_{self.atr_len.value}_{self.atr_multiplier_profit_10.value}': {'color': '#ffd700'},
                # f'sl_5_{self.atr_len.value}_{self.atr_multiplier_profit_5.value}': {'color': '#fcf75e'},
                # f'sl_0_{self.atr_len.value}_{self.atr_multiplier_profit.value}': {'color': '#fdfd96'},
                # f'sl_below_0_{self.atr_len.value}_{self.atr_multiplier_no_profit.value}': {'color': '#c3b091'},
                # 'sar_4h': {'color': 'blue'},
            },
            'subplots': {
                # Subplots - each dict defines one additional plot
                "RSI": {
                    'RSI': {'color': 'red'},
                    'SMMA': {'color': 'yellow'}
                }
            }
        }

    def informative_pairs(self):
        """
        Define additional, informative pair/interval combinations to be cached from the exchange.
        These pair/interval combinations are non-tradeable, unless they are part
        of the whitelist as well.
        For more information, please consult the documentation
        :return: List of tuples in the format (pair, interval)
            Sample: return [("ETH/USDT", "5m"),
                            ("BTC/USDT", "15m"),
                            ]
        """
        # get access to all pairs available in whitelist.
        pairs = self.dp.current_whitelist()
        # Assign tf to each pair so they can be downloaded and cached for strategy.
        informative_pairs = [(pair, self.inf_tf)
                             for pair in pairs]
        return informative_pairs

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        # Informative dataframe
        informative = self.dp.get_pair_dataframe(
            pair=metadata['pair'], timeframe=self.inf_tf)

        informative['sar'] = ta.SAR(informative)

        dataframe = merge_informative_pair(
            dataframe, informative, self.timeframe, self.inf_tf, ffill=True)

        # inf_long = resample_to_interval(dataframe, 360)
        # inf_long['sar'] = ta.SAR(inf_long)

        # dataframe = resampled_merge(dataframe, inf_long, fill_na=True)

        # for val in self.atr_len.range:
        #     dataframe[f'atr_{val}'] = ta.ATR(
        #         dataframe, period=self.atr_len.value)

        # dataframe['RSI'] = ta.RSI(dataframe[self.src], self.rsiLen)
        # dataframe['RSI'] = dataframe['RSI'].fillna(0)
        dataframe['RSI'] = ta.RSI(dataframe, timeperiod=14)
        stoch = ta.STOCH(dataframe, fastk_period=10, slowk_period=3,
                         slowk_matype=0, slowd_period=3, slowd_matype=0)
        dataframe['slowk'] = stoch['slowk']
        dataframe['slowd'] = stoch['slowd']
        dataframe['osc'] = dataframe[self.osc]

        # SMMA
        dataframe['SMMA'] = ta.SMA(
            ta.RSI(dataframe, timeperiod=5).rolling(3).mean(), period=14
        )

        # plFound = na(pivotlow(osc, lbL, lbR)) ? false : true
        dataframe['min'] = dataframe['osc'].rolling(self.lbL).min()
        dataframe['prevMin'] = np.where(dataframe['min'] > dataframe['min'].shift(
        ), dataframe['min'].shift(), dataframe['min'])
        dataframe.loc[
            (
                (dataframe['osc'].shift(1) == dataframe['prevMin'].shift(1)) &
                (dataframe['osc'] != dataframe['prevMin'])
            ), 'plFound'] = 1

        # phFound = na(pivothigh(osc, lbL, lbR)) ? false : true
        dataframe['max'] = dataframe['osc'].rolling(self.lbL).max()
        dataframe['prevMax'] = np.where(dataframe['max'] < dataframe['max'].shift(
        ), dataframe['max'].shift(), dataframe['max'])
        dataframe.loc[
            (
                (dataframe['osc'].shift(1) == dataframe['prevMax'].shift(1)) &
                (dataframe['osc'] != dataframe['prevMax'])
            ), 'phFound'] = 1

        # ------------------------------------------------------------------------------
        # Regular Bullish
        # Osc: Higher Low
        # oscHL = osc[lbR] > valuewhen(plFound, osc[lbR], 1) and _inRange(plFound[1])
        dataframe['valuewhen_plFound_osc'], dataframe['inrange_plFound_osc'] = valuewhen(
            dataframe, 'plFound', 'osc', 1)
        dataframe.loc[
            (
                (dataframe['osc'] > dataframe['valuewhen_plFound_osc']) &
                (dataframe['inrange_plFound_osc'] == 1)
            ), 'oscHL'] = 1

        # Price: Lower Low
        # priceLL = low[lbR] < valuewhen(plFound, low[lbR], 1)
        dataframe['valuewhen_plFound_low'], dataframe['inrange_plFound_low'] = valuewhen(
            dataframe, 'plFound', 'low', 1)
        dataframe.loc[
            (dataframe['low'] < dataframe['valuewhen_plFound_low']), 'priceLL'] = 1
        # bullCond = plotBull and priceLL and oscHL and plFound
        dataframe.loc[
            (
                (dataframe['priceLL'] == 1) &
                (dataframe['oscHL'] == 1) &
                (dataframe['plFound'] == 1)
            ), 'bullCond'] = 1

        # plot(
        #      plFound ? osc[lbR] : na,
        #      offset=-lbR,
        #      title="Regular Bullish",
        #      linewidth=2,
        #      color=(bullCond ? bullColor : noneColor)
        #      )

        # plotshape(
        #      bullCond ? osc[lbR] : na,
        #      offset=-lbR,
        #      title="Regular Bullish Label",
        #      text=" Bull ",
        #      style=shape.labelup,
        #      location=location.absolute,
        #      color=bullColor,
        #      textcolor=textColor
        #      )

        # //------------------------------------------------------------------------------
        # // Hidden Bullish
        # // Osc: Lower Low
        #
        # oscLL = osc[lbR] < valuewhen(plFound, osc[lbR], 1) and _inRange(plFound[1])
        dataframe['valuewhen_plFound_osc'], dataframe['inrange_plFound_osc'] = valuewhen(
            dataframe, 'plFound', 'osc', 1)
        dataframe.loc[
            (
                (dataframe['osc'] < dataframe['valuewhen_plFound_osc']) &
                (dataframe['inrange_plFound_osc'] == 1)
            ), 'oscLL'] = 1
        #
        # // Price: Higher Low
        #
        # priceHL = low[lbR] > valuewhen(plFound, low[lbR], 1)
        dataframe['valuewhen_plFound_low'], dataframe['inrange_plFound_low'] = valuewhen(
            dataframe, 'plFound', 'low', 1)
        dataframe.loc[
            (dataframe['low'] > dataframe['valuewhen_plFound_low']), 'priceHL'] = 1
        # hiddenBullCond = plotHiddenBull and priceHL and oscLL and plFound
        dataframe.loc[
            (
                (dataframe['priceHL'] == 1) &
                (dataframe['oscLL'] == 1) &
                (dataframe['plFound'] == 1)
            ), 'hiddenBullCond'] = 1
        #
        # plot(
        #      plFound ? osc[lbR] : na,
        #      offset=-lbR,
        #      title="Hidden Bullish",
        #      linewidth=2,
        #      color=(hiddenBullCond ? hiddenBullColor : noneColor)
        #      )
        #
        # plotshape(
        #      hiddenBullCond ? osc[lbR] : na,
        #      offset=-lbR,
        #      title="Hidden Bullish Label",
        #      text=" H Bull ",
        #      style=shape.labelup,
        #      location=location.absolute,
        #      color=bullColor,
        #      textcolor=textColor
        #      )
        #
        # //------------------------------------------------------------------------------
        # // Regular Bearish
        # // Osc: Lower High
        #
        # oscLH = osc[lbR] < valuewhen(phFound, osc[lbR], 1) and _inRange(phFound[1])
        dataframe['valuewhen_phFound_osc'], dataframe['inrange_phFound_osc'] = valuewhen(
            dataframe, 'phFound', 'osc', 1)
        dataframe.loc[
            (
                (dataframe['osc'] < dataframe['valuewhen_phFound_osc']) &
                (dataframe['inrange_phFound_osc'] == 1)
            ), 'oscLH'] = 1
        #
        # // Price: Higher High
        #
        # priceHH = high[lbR] > valuewhen(phFound, high[lbR], 1)
        dataframe['valuewhen_phFound_high'], dataframe['inrange_phFound_high'] = valuewhen(
            dataframe, 'phFound', 'high', 1)
        dataframe.loc[
            (dataframe['high'] > dataframe['valuewhen_phFound_high']), 'priceHH'] = 1
        #
        # bearCond = plotBear and priceHH and oscLH and phFound
        dataframe.loc[
            (
                (dataframe['priceHH'] == 1) &
                (dataframe['oscLH'] == 1) &
                (dataframe['phFound'] == 1)
            ), 'bearCond'] = 1
        #
        # plot(
        #      phFound ? osc[lbR] : na,
        #      offset=-lbR,
        #      title="Regular Bearish",
        #      linewidth=2,
        #      color=(bearCond ? bearColor : noneColor)
        #      )
        #
        # plotshape(
        #      bearCond ? osc[lbR] : na,
        #      offset=-lbR,
        #      title="Regular Bearish Label",
        #      text=" Bear ",
        #      style=shape.labeldown,
        #      location=location.absolute,
        #      color=bearColor,
        #      textcolor=textColor
        #      )
        #
        # //------------------------------------------------------------------------------
        # // Hidden Bearish
        # // Osc: Higher High
        #
        # oscHH = osc[lbR] > valuewhen(phFound, osc[lbR], 1) and _inRange(phFound[1])
        dataframe['valuewhen_phFound_osc'], dataframe['inrange_phFound_osc'] = valuewhen(
            dataframe, 'phFound', 'osc', 1)
        dataframe.loc[
            (
                (dataframe['osc'] > dataframe['valuewhen_phFound_osc']) &
                (dataframe['inrange_phFound_osc'] == 1)
            ), 'oscHH'] = 1
        #
        # // Price: Lower High
        #
        # priceLH = high[lbR] < valuewhen(phFound, high[lbR], 1)
        dataframe['valuewhen_phFound_high'], dataframe['inrange_phFound_high'] = valuewhen(
            dataframe, 'phFound', 'high', 1)
        dataframe.loc[
            (dataframe['high'] < dataframe['valuewhen_phFound_high']), 'priceLH'] = 1
        #
        # hiddenBearCond = plotHiddenBear and priceLH and oscHH and phFound
        dataframe.loc[
            (
                (dataframe['priceLH'] == 1) &
                (dataframe['oscHH'] == 1) &
                (dataframe['phFound'] == 1)
            ), 'hiddenBearCond'] = 1
        #
        # plot(
        #      phFound ? osc[lbR] : na,
        #      offset=-lbR,
        #      title="Hidden Bearish",
        #      linewidth=2,
        #      color=(hiddenBearCond ? hiddenBearColor : noneColor)
        #      )
        #
        # plotshape(
        #      hiddenBearCond ? osc[lbR] : na,
        #      offset=-lbR,
        #      title="Hidden Bearish Label",
        #      text=" H Bear ",
        #      style=shape.labeldown,
        #      location=location.absolute,
        #      color=bearColor,
        #      textcolor=textColor
        #  )"""

        # Elliot
        dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo)

        # dataframe['ADX'] = ta.ADX(dataframe, timeperiod=14)

        # Average price
        # dataframe['avg_price'] = ((dataframe['high'] + dataframe['low'])/2)

        # dataframe['sar'] = ta.SAR(dataframe)

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []

        if self.use_bull.value:
            conditions.append(
                (
                    (dataframe['bullCond'] > 0) &
                    # (dataframe[f'sar_{self.inf_tf}'] < dataframe['close']) &
                    (dataframe['EWO'] > dataframe['EWO'].shift(1).rolling(self.ewo_rol.value).max()) &
                    # (dataframe['resample_360_sar'] < dataframe['close']) &
                    # (dataframe['valuewhen_plFound_osc'] > self.low_osc_buy.value) &
                    # (dataframe['valuewhen_plFound_osc'] < self.high_osc_buy.value) &
                    # (dataframe['EWO'] > self.ewo_high.value) &
                    (dataframe['RSI'] < self.high_rsi_buy.value) &
                    # (dataframe['RSI'] > self.low_rsi_buy.value) &
                    # (dataframe['ADX'] > self.low_adx_buy.value) &
                    # (dataframe['ADX'] < self.high_adx_buy.value) &
                    # (dataframe['slowk'] < self.high_stoch_buy.value) &
                    # (dataframe['slowk'] > self.low_stoch_buy.value) &
                    (dataframe['volume'] > 0)
                )
            )

        if self.use_hidden_bull.value:
            conditions.append(
                (
                    (dataframe['hiddenBullCond'] > 0) &
                    # (dataframe[f'sar_{self.inf_tf}'] < dataframe['close']) &
                    (dataframe['EWO'] > dataframe['EWO'].shift(1).rolling(self.ewo_rol.value).max()) &
                    # (dataframe['resample_360_sar'] < dataframe['close']) &
                    # (dataframe['valuewhen_plFound_osc'] > self.low_osc_buy.value) &
                    # (dataframe['valuewhen_plFound_osc'] < self.high_osc_buy.value) &
                    (dataframe['RSI'] < self.high_rsi_buy.value) &
                    # (dataframe['RSI'] > self.low_rsi_buy.value) &
                    # (dataframe['slowk'] < self.high_stoch_buy.value) &
                    # (dataframe['slowk'] > self.low_stoch_buy.value) &
                    # (dataframe['ADX'] > self.low_adx_buy.value) &
                    # (dataframe['ADX'] < self.high_adx_buy.value) &
                    (dataframe['volume'] > 0)
                )
            )

        if self.use_rsi_smma_buy.value:
            conditions.append(
                (
                    # (dataframe[f'sar_{self.inf_tf}'] < dataframe['close']) &
                    (dataframe['EWO'] > dataframe['EWO'].shift(1).rolling(self.ewo_rol.value).max()) &
                    (dataframe['RSI'] < self.high_smma_rsi_buy.value) &
                    (qtpylib.crossed_above(dataframe['RSI'], dataframe['SMMA'])) &
                    (dataframe['volume'] > 0)
                )
            )

        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x | y, conditions),
                'buy'
            ] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []

        conditions.append(
            (dataframe['EWO'] > self.low_ewo_sell.value) &
            (dataframe['EWO'] < dataframe['EWO'].shift(1)) &
            # (dataframe['RSI'] > self.low_rsi_sell.value) &
            # (qtpylib.crossed_below(dataframe['RSI'], dataframe['SMMA'])) &
            (dataframe['volume'] > 0)
        )

        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'sell'] = 1

        # dataframe.to_csv('user_data/csvs/%s_%s.csv' %
        #                  (self.__class__.__name__, metadata["pair"].replace("/", "_")))

        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:
        sl_new = 1

        if (current_profit > 0.2):
            sl_new = 0.05
        elif (current_profit > 0.1):
            sl_new = 0.03
        elif (current_profit > 0.06):
            sl_new = 0.02
        elif (current_profit > 0.03):
            sl_new = 0.01

        return sl_new
