# source: https://raw.githubusercontent.com/terrah27/freqtrade_strategies/35e0764375e63ca6f316e0a15437d4f8061bc245/rad_testing.py
# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame
import numpy as np
# --------------------------------

import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib

# need these to block pairs for x time
from freqtrade.persistence import Trade
from datetime import timedelta, datetime, timezone, time

class Github_terrah27_freqtrade_strategies__rad_testing__20210420_121649(IStrategy):
    """
    idea:
        Relative Average Distance: Calculate distance of price from its simple moving average.
        Measure mean reversion by applying RSI formula to price differences.
        Go long when RAD < 25 short when > 75

        Base on article from Sofien Kaabar:
        https://kaabar-sofien.medium.com/the-relative-average-distance-a-new-mean-reversion-trading-indicator-c21f8f1986e3
    """
    # Optimal timeframe for the strategy.
    timeframe = '30m'

    # Create custom dictionary
    custom_info = {}

    # ROI table:
    minimal_roi = {
        "0": 0.39724,
        "90": 0.07604,
        "258": 0.05495,
        "791": 0.1
    }

    # Stoploss:
    stoploss = -0.50

    # Trailing stop:
    trailing_stop = False
    process_only_new_candles = True
    use_custom_stoploss = True
    sell_profit_only = False

    startup_candle_count = 35

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

        if self.custom_info and pair in self.custom_info and trade:
            # using current_time directly (like below) will only work in backtesting.
            # so check "runmode" to make sure that it's only used in backtesting/hyperopt
            if self.dp and self.dp.runmode.value in ('backtest', 'hyperopt'):
                relative_sl = self.custom_info[pair].loc[current_time]['atr_ema']
 
            # in live / dry-run, it'll be really the current time
            else:
              # but we can just use the last entry from an already analyzed dataframe instead
                dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair,
                                                                       timeframe=self.timeframe)
                # WARNING
                # only use .iat[-1] in live mode, not in backtesting/hyperopt
                # otherwise you will look into the future
                # see: https://www.freqtrade.io/en/latest/strategy-customization/#common-mistakes-when-developing-strategies
                relative_sl = dataframe['atr_ema'].iat[-1]

            # set stoploss using atr based on current profit levels
            # tighten stop as profit increases to lock in gains
            if current_profit >= 0.07:
                return ((current_rate - relative_sl *1) / current_rate) - 1
            if current_profit >= 0.03:
                return ((current_rate - relative_sl *2) / current_rate) - 1
            if current_profit >= 0.01:
                return ((current_rate - relative_sl *3) / current_rate) - 1

            return ((current_rate - relative_sl *5) / current_rate) - 1

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

        # lock pairs if they lost in the last x days
        if self.config['runmode'].value in ('live', 'dry_run'):
        # fetch closed trades 
            trades = Trade.get_trades([Trade.pair == metadata['pair'],
                                    Trade.open_date > datetime.utcnow() - timedelta(days=1),
                                    Trade.is_open.is_(False),
                        ]).all()
            # Analyze the conditions you'd like to lock the pair ....
            sumprofit = sum(trade.close_profit for trade in trades)
            if sumprofit < 0:
                # Lock pair for
                self.lock_pair(metadata['pair'], until=datetime.now(timezone.utc) + timedelta(hours=6))

        # main trigger - get relative average distance
        dataframe['sma'] = ta.SMA(dataframe, timeperiod=8)
        dataframe['price_dif'] = dataframe['close'] - dataframe['sma']
        dataframe['rad'] = ta.RSI(dataframe['price_dif'], timeperiod=5)

        # atr for stoploss calculations
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=8)
        dataframe['atr_ema'] = ta.EMA(dataframe['atr'], timeperiod=5)

        # confirm signal with sma of slower version of rad?
        # using cross for entry gives less profits
        dataframe['sma_slow'] = ta.SMA(dataframe, timeperiod=13)
        dataframe['slow_dif'] = dataframe['close'] - dataframe['sma_slow']
        dataframe['slow_rad'] = ta.SMA(ta.RSI(dataframe['slow_dif'], timeperiod=5),timeperiod=8)

        #if self.dp.runmode.value in ('backtest', 'hyperopt'):
          # add indicator mapped to correct DatetimeIndex to custom_info
        self.custom_info[metadata['pair']] = dataframe[['date', 'atr','atr_ema']].copy().set_index('date')
        
        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the buy signal for the given dataframe
        :param dataframe: DataFrame
        :return: DataFrame with buy column
        """
        dataframe.loc[
            (
                # (                
                # (qtpylib.crossed_above(dataframe['rad'], dataframe['slow_rad'])) &
                # (dataframe['rad'].shift(1) < 30) )
                # |
                # (
                # (qtpylib.crossed_above(dataframe['rad'], dataframe['slow_rad'])) &
                # (dataframe['rad'].shift(2) < 30) 
                # )
                # |
                (
                (qtpylib.crossed_below(dataframe['rad'], 23)) &
                (dataframe['rad'].shift(1) > 23) &
                (dataframe['rad'].shift(2) > 23) &
                (dataframe['sma'] > dataframe['close'])
                )
                                
            ),
            'buy'] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the sell signal for the given dataframe
        :param dataframe: DataFrame
        :return: DataFrame with buy column
        """
        dataframe.loc[
            (

                (
                    (qtpylib.crossed_below(dataframe['rad'], dataframe['slow_rad'])) &
                    (dataframe['rad'].shift(1) > 60)
                )
                # |
                # (
                # (qtpylib.crossed_above(dataframe['rad'], 80)) &
                # (dataframe['rad'].shift(1) < 80)
                # )
                # | # trying closing out through midnight est? avoid overnight selloffs?
                # (
                #     (dataframe['date'].dt.time >= time(2,0)) &
                #     (dataframe['date'].dt.time <= time(5,0)) &
                #     (dataframe['close'] < dataframe['close'].shift(1)) &
                #     (dataframe['close'].shift(1) < dataframe['close'].shift(2))
                # )
             
            ),
            'sell'] = 1
        return dataframe

 