# source: https://raw.githubusercontent.com/1amcord/freqtrade_settings/b078a9d66f5e88f6ad18d35066241aaf91c31032/strategies/BBandRsi.py
# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from freqtrade.persistence import Trade
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from functools import reduce
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,
                                IStrategy, IntParameter, DecimalParameter, merge_informative_pair)

import logging
logger = logging.getLogger(__name__)

# --------------------------------


class github_1amcord_freqtrade_settings__BBandRsi__20220814_101522(IStrategy):

    # Minimal ROI designed for the strategy.
    # adjust based on market conditions. We would recommend to keep it low for quick turn arounds
    # This attribute will be overridden if the config file contains "minimal_roi"
    minimal_roi = {
        "0": 10
    }

    stoploss = -0.325
    # stoploss = -10

    trailing_stop = True
    trailing_stop_positive = 0.224
    trailing_stop_positive_offset = 0.319
    trailing_only_offset_is_reached = True
    use_custom_stoploss = False

    # Adjust position amount
    position_adjustment_enable = True
    max_entry_position_adjustment = 2

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = False

    # These values can be overridden in the "ask_strategy" section in the config.
    use_sell_signal = True
    sell_profit_only = False
    ignore_roi_if_buy_signal = False

    dca_stake_decrease_percent = DecimalParameter(0.5, 1, decimals=2, default=0.89, space="buy")
    dca_stake_adj = DecimalParameter(0.33, 0.95, decimals=2, default=0.63, space="buy")
    
    # dca_num_buys can't be hyperoptimized. Therefore this is a fixed value 
    dca_num_buys = 2

    @property
    def plot_config(self):
        return {
            # Main plot indicators (Moving averages, ...)
            'main_plot': {
                # 'fastd': {'color': 'lightblue'},
                # 'fastk': {'color': 'green'},
                # 'smma3': {'color': 'yellow'},
                # 'smma21': {'color': 'red'},
                'sar_4h': {'color': 'blue'},
                'smma12_4h': {'color': 'lightgreen'},
                # 'sar': {'color': 'lightblue'},
            },
            'subplots': {
                # Subplots - each dict defines one additional plot
                "RSI": {
                    'rsi_4h': {'color': 'red'},
                }
            }
        }

    # Optimal stoploss designed for the strategy
    stoploss = -0.99

    # Optimal timeframe for the strategy
    timeframe = '15m'

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)

        # Bollinger bands
        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']

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

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

        inf_tf = '4h'
        # Get the informative pair
        informative = self.dp.get_pair_dataframe(
            pair=metadata['pair'], timeframe=inf_tf)
        # # Get the 21 day SMA for the 4h timeframe
        # informative['ema21'] = ta.EMA(informative, timeperiod=21)

        informative['avg_price_last_day'] = (
            (informative['high'] + informative['low'])/2)

        # Parabolic SAR
        informative['sar'] = ta.SAR(informative)
        # EMA6
        # informative['ema6'] = ta.EMA(informative, timeperiod=6)

        # smma12_4h
        informative['smma12'] = ta.SMA(ta.SMA(informative, timeperiod=12), 12)

        informative['rsi'] = ta.RSI(informative, timeperiod=14)

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

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                # (dataframe['rsi_4h'] < 33) &
                (dataframe['smma12_4h'] < dataframe['avg_price']) &
                (dataframe['rsi'] < 30) &
                (dataframe['close'] < dataframe['bb_lowerband'])

            ),
            '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 populated with indicators
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with buy column
        """
        conditions = []
        # Stay away from ema21_4h to avoid sells when crossing below ema21_4h
        # conditions.append((((dataframe['ema21_4h']*0.97) > dataframe['close']) |
        #                    ((dataframe['ema21_4h']*1.03) < dataframe['close'])))

        # # No falling prices
        # conditions.append((
        #     dataframe['sar_4h'] < dataframe['close']))

        # conditions.append((qtpylib.crossed_below(dataframe['sar_4h'], dataframe['ema6_4h'])))

        # conditions.append((dataframe['ema21_4h'] < dataframe['close']))
        conditions.append((dataframe['smma12_4h'] < dataframe['avg_price']))

        # conditions.append((dataframe['ema6_4h'] < dataframe['close']))
        # conditions.append((dataframe['rsi_4h'] < self.buy_rsi.value))
        conditions.append((dataframe['volume'] > 0))
        conditions.append((qtpylib.crossed_below(
            dataframe['sar_4h'], dataframe['avg_price'])))

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

        return dataframe

    def custom_stake_amount(self, pair: str, current_rate: float,
                            proposed_stake: float, min_stake: float, max_stake: float, **kwargs) -> float:

        # We need to leave most of the funds for possible further DCA orders
        # This also applies to fixed stakes
        new_proposed_stake = (proposed_stake*self.dca_stake_adj.value)
        logger.debug("Proposed Stake: %s", new_proposed_stake)

        return new_proposed_stake
        # return proposed_stake

    def adjust_trade_position(self, trade: Trade, current_time: 'datetime',
                              current_rate: float, current_profit: float, min_stake: float,
                              max_stake: float, **kwargs):
        """
        Custom trade adjustment logic, returning the stake amount that a trade should be increased.
        This means extra buy orders with additional fees.

        :param trade: trade object.
        :param current_time: datetime object, containing the current datetime
        :param current_rate: Current buy rate.
        :param current_profit: Current profit (as ratio), calculated based on current_rate.
        :param min_stake: Minimal stake size allowed by exchange.
        :param max_stake: Balance available for trading.
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return float: Stake amount to adjust your trade
        """
        # Obtain pair dataframe (just to show how to access it)
        dataframe, _ = self.dp.get_analyzed_dataframe(
            trade.pair, self.timeframe)
        # Only buy when not actively falling price.
        last_candle = dataframe.iloc[-1].squeeze()

        count_of_buys = trade.nr_of_successful_buys
        open_order_price = trade.open_rate

        # if ((count_of_buys == 1) &
        if ((last_candle['close'] > (open_order_price * self.dca_stake_decrease_percent.value))):
            return None
        if (count_of_buys > self.dca_num_buys):
            return None

        filled_buys = trade.select_filled_orders('buy')
        num_filled_buys = len(filled_buys)
        # logger.info("Date: %s - Filled buys: %s - min stake: %s max stake: %s",
        #             current_time, num_filled_buys, min_stake, max_stake)
        # Allow up to 3 additional increasingly smaller buys (4 in total)
        # Initial buy is 1x
        # Hope you have a deep wallet!
        try:
            # This returns first order stake size
            stake_amount = filled_buys[0].cost
            new_stake_amount = ((stake_amount/self.dca_stake_adj.value)
                                * ((1-self.dca_stake_adj.value)/self.dca_num_buys))

            # This then calculates current safety order size
            # stake_amount = stake_amount/(self.max_entry_position_adjustment+1)
            logger.debug('\nLast candle date: %s \n\
                            - count of buys: %s \n\
                            - max stake: %s \n\
                            - Stake Amount: %s \n\
                            - New Stake Amount: %s \n',
                         last_candle['date'],
                         count_of_buys,
                         max_stake,
                         stake_amount,
                         new_stake_amount)
            return (new_stake_amount)
        except Exception as exception:
            return None

        return None
