# Heracles Strategy: Strongest Son of GodStra
# ( With just 1 Genome! its a bacteria :D )
# Author: @Mablue (Masoud Azizi)
# github: https://github.com/mablue/
# IMPORTANT:Add to your pairlists inside config.json (Under StaticPairList):
#   {
#       "method": "AgeFilter",
#       "min_days_listed": 100
#   },
# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta)
#
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces roi buy --strategy Heracles
# ######################################################################
# --- Do not remove these libs ---
from freqtrade.strategy.parameters import  IntParameter, DecimalParameter
from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame
# --------------------------------
# Add your lib to import here
# import talib.abstract as ta
import pandas as pd
import talib.abstract as talib
import ta
from ta.utils import dropna
import freqtrade.vendor.qtpylib.indicators as qtpylib
from functools import reduce
import numpy as np
from freqtrade.strategy.strategy_helper import merge_informative_pair


class Heracles_2(IStrategy):
    ########################################## RESULT PASTE PLACE ##########################################
    # 10/100:     25 trades. 18/4/3 Wins/Draws/Losses. Avg profit   5.92%. Median profit   6.33%. Total profit  0.04888306 BTC (  48.88Σ%). Avg duration 4 days, 6:24:00 min. Objective: -11.42103

    # Buy hyperspace params:
    buy_params = {
        "buy_crossed_indicator_shift": 9,
        "buy_div_max": 0.75,
        "buy_div_min": 0.16,
        "buy_indicator_shift": 15,
    }

    # Sell hyperspace params:
    sell_params = {
    }

    # ROI table:
    minimal_roi = {
        "0": 0.598,
        "644": 0.166,
        "3269": 0.115,
        "7289": 0
    }

    # Stoploss:
    stoploss = -0.256

    # Optimal timeframe use it in your config
    timeframe = '4h'

    # Trailing stoploss
    trailing_stop = True
    trailing_stop_positive = 0.001
    trailing_stop_positive_offset = 0.015
    trailing_only_offset_is_reached = True

    plot_config = {
  "main_plot": {
    "max50": {
      "color": "#dd1384",
      "type": "line"
    },
    "min50": {
      "color": "#850678",
      "type": "line"
    },
    "min3_1d": {
      "color": "#30c310",
      "type": "line"
    },
    "max3_1d": {
      "color": "#e64062",
      "type": "line"
    },
    "min200": {
      "color": "#86c932",
      "type": "line"
    }
  },
  "subplots": {
    "Vol": {
      "volume10": {
        "color": "#7b53f5",
        "type": "line"
      }
    },
    "Percent": {
      "max_min": {
        "color": "#74effc",
        "type": "line"
      }
    }
  }
}

    ########################################## END RESULT PASTE PLACE ######################################

    # buy params
    buy_div_min = DecimalParameter(0, 1, default=0.16, decimals=2, space='buy')
    buy_div_max = DecimalParameter(0, 1, default=0.75, decimals=2, space='buy')
    buy_indicator_shift = IntParameter(0, 20, default=16, space='buy')
    buy_crossed_indicator_shift = IntParameter(0, 20, default=9, space='buy')

    #buy_rsi_min = IntParameter(10, 40, default=10, space='buy')
    #buy_rsi_max = IntParameter(50, 98, default=90, space='buy')

    buy_max_min = DecimalParameter(0, 0.05, default=0.02, decimals=2, space='buy')
    buy_bb_lowerband = DecimalParameter(1, 1.05, default=1, decimals=2, space='buy')
    buy_bb_width = DecimalParameter(0.01, 0.15, default=0.065, decimals=2, space='buy')

    def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
                    current_profit: float, **kwargs):
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        previous_last_candle = dataframe.iloc[-2].squeeze()
        previous_5_candle = dataframe.iloc[-5].squeeze()

        if (current_profit > 0.01) \
                & ((previous_5_candle['sma5'] > last_candle['sma5']) \
                    | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \
                & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
            # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
            return 'sma10'

        if (current_profit > 0.01) \
                & ((previous_5_candle['sma10'] > last_candle['sma10']) \
                    | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \
                & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
            # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
            return 'sma10'

        if (current_profit > 0.005) \
                & (previous_last_candle['sma10'] > last_candle['sma10']) \
                & ((current_time - trade.open_date_utc).seconds >= 3600) \
                & ((previous_last_candle['sma20'] > last_candle['sma20']) &
                    ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))):
            # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
            return 'sma20'


    def informative_pairs(self):
        # 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, "5m") for pair in pairs]
        informative_pairs = [(pair, '1d') for pair in pairs]
        # informative_pairs += [(pair, '1w') for pair in pairs]

        # Optionally Add additional "static" pairs
        # informative_pairs = [("BTC/USDT", "1w"), ("BTC/USDT", "1d"), ("BTC/USDT", "5m")]

        return informative_pairs

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

        dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband(
            dataframe['high'],
            dataframe['low'],
            dataframe['close'],
            window=20,
            window_atr=10,
            fillna=False,
            original_version=True
        )

        dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband(
            dataframe['high'],
            dataframe['low'],
            dataframe['close'],
            window=10,
            offset=0,
            fillna=False
        )
        dataframe['rsi'] = talib.RSI(dataframe)
        dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5)
        dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10)
        dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20)
        dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50)
        dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100)
        dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"]
        dataframe["percent5"] = dataframe["percent"].rolling(5).sum()
        dataframe["percent3"] = dataframe["percent"].rolling(3).sum()
        dataframe["percent10"] = dataframe["percent"].rolling(10).sum()
        dataframe["percent20"] = dataframe["percent"].rolling(20).sum()
        dataframe["percent50"] = dataframe["percent"].rolling(50).sum()

        dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10)
        dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20)
        dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50)
        dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200)

        dataframe["volume10"] = dataframe["volume"].rolling(10).mean()

        dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50)
        dataframe['max_min'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50']
        dataframe["volume10"] = dataframe["volume"].rolling(10).mean()
        dataframe['volume_cond'] = (dataframe["volume10"] * dataframe['close'] / 1000 >= 19)

        # 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']
        dataframe["bb_percent"] = (
            (dataframe["close"] - dataframe["bb_lowerband"]) /
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"])
        )
        dataframe["bb_width"] = (
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"]
        )
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d")
        informative["rsi"] = talib.RSI(informative)
        informative["max3"] = talib.MAX(informative['close'], timeperiod=3)
        informative["min3"] = talib.MIN(informative['close'], timeperiod=3)

        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True)

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Buy strategy Hyperopt will build and use.
        """
        conditions = []

        IND = 'volatility_dcp'
        CRS = 'volatility_kcw'
        DFIND = dataframe[IND]
        DFCRS = dataframe[CRS]

        d = DFIND.shift(self.buy_indicator_shift.value).div(
            DFCRS.shift(self.buy_crossed_indicator_shift.value))

        # print(d.min(), "\t", d.max())
        conditions.append(
            d.between(self.buy_div_min.value, self.buy_div_max.value))

        if conditions:
            dataframe.loc[
                (
                 (dataframe['volume10'] * dataframe['close'] / 1000 >= 19)
                 & (reduce(lambda x, y: x & y, conditions))
                 & (dataframe['close_1d'] <= dataframe['open_1d'])
                 & (dataframe['max_min'] >= self.buy_max_min.value)
                 & (dataframe['close'] <= dataframe['min200'] * 1.005)
                 & (dataframe['close'].shift(1) <= dataframe['min200'].shift(1) * 1.005)
                 & (dataframe['min200'].shift(1) == dataframe['min200'])
                 & (dataframe['min50'] <= dataframe['min3_1d'] * 1.005)
                 & (dataframe['close'].shift(1) < dataframe['bb_lowerband'] * self.buy_bb_lowerband.value)
                 & (dataframe['bb_width'].shift(1) >= self.buy_bb_width.value)
                 ),
                'buy']=1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Sell strategy Hyperopt will build and use.
        """
        # dataframe.loc[
        #     (dataframe['close'] >= dataframe['max50'] * 0.998)
        # , 'sell'] = 1
        return dataframe
