from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame
# --------------------------------

import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np  # noqa


def activate(x):
    return np.tanh(x)  # tanh

params = {
    '0-0-0-w': -0.53814,
    '0-0-bias': -0.96407,
    '1-0-0-w': -0.49249,
    '10-0-0-w': 0.08845,
    '11-0-0-w': -0.14317,
    '12-0-0-w': 0.00923,
    '13-0-0-w': 0.30464,
    '14-0-0-w': -0.35835,
    '15-0-0-w': -0.49712,
    '16-0-0-w': 0.76135,
    '17-0-0-w': -0.75257,
    '18-0-0-w': -0.04622,
    '19-0-0-w': 0.10012,
    '2-0-0-w': -0.23534,
    '20-0-0-w': -0.04553,
    '21-0-0-w': -0.35334,
    '22-0-0-w': 0.17952,
    '23-0-0-w': 0.44446,
    '24-0-0-w': -0.15875,
    '25-0-0-w': 0.97565,
    '26-0-0-w': -0.89948,
    '27-0-0-w': 0.61777,
    '28-0-0-w': -0.60204,
    '29-0-0-w': -0.85229,
    '3-0-0-w': 0.47262,
    '30-0-0-w': -0.52791,
    '31-0-0-w': 0.98494,
    '4-0-0-w': -0.54942,
    '5-0-0-w': 0.40523,
    '6-0-0-w': 0.4723,
    '7-0-0-w': 0.63297,
    '8-0-0-w': 0.07159,
    '9-0-0-w': -0.86791,
    'adx-bias': -0.48719,
    'ao-bias': -0.87518,
    'aroonosc-bias': -0.56096,
    'bb_percent-bias': -0.98703,
    'bb_width-bias': -0.73742,
    'cci-bias': 0.47039,
    'end-0-w': -0.81658,
    'end-bias': 0.74656,
    'fastd-bias': -0.2793,
    'fisher_rsi_norm-bias': -0.36065,
    'kc_percent-bias': 0.76707,
    'kc_width-bias': 0.5489,
    'macd-bias': 0.55448,
    'macdhist-bias': -0.83133,
    'macdsignal-bias': 0.30828,
    'mfi-bias': -0.13097,
    'roc-bias': -0.78885,
    'rsi-bias': 0.9856,
    'sar-bias': 0.43812,
    'sma10-bias': -0.39019,
    'sma100-bias': 0.03558,
    'sma21-bias': 0.07457,
    'sma3-bias': 0.93633,
    'sma5-bias': -0.93329,
    'sma50-bias': -0.60637,
    'tema10-bias': -0.45946,
    'tema100-bias': 0.1662,
    'tema21-bias': 0.68466,
    'tema3-bias': 0.25368,
    'tema5-bias': -0.88818,
    'tema50-bias': 0.3019,
    'uo-bias': 0.71019,
    'wbb_percent-bias': -0.55964,
    'wbb_width-bias': 0.23523,

    's-0-0-0-w': 0.85409,
    's-0-0-bias': -0.04613,
    's-1-0-0-w': -0.14997,
    's-10-0-0-w': -0.67008,
    's-11-0-0-w': -0.40221,
    's-12-0-0-w': 0.64553,
    's-13-0-0-w': 0.22838,
    's-14-0-0-w': 0.99977,
    's-15-0-0-w': 0.89363,
    's-16-0-0-w': -0.88212,
    's-17-0-0-w': -0.71813,
    's-18-0-0-w': 0.41602,
    's-19-0-0-w': -0.48389,
    's-2-0-0-w': 0.09649,
    's-20-0-0-w': 0.64273,
    's-21-0-0-w': -0.31671,
    's-22-0-0-w': 0.9663,
    's-23-0-0-w': 0.00229,
    's-24-0-0-w': 0.96244,
    's-25-0-0-w': -0.24513,
    's-26-0-0-w': 0.52312,
    's-27-0-0-w': 0.44742,
    's-28-0-0-w': -0.03916,
    's-29-0-0-w': 0.88882,
    's-3-0-0-w': -0.32112,
    's-30-0-0-w': -0.70886,
    's-31-0-0-w': -0.42672,
    's-4-0-0-w': -0.55265,
    's-5-0-0-w': 0.56105,
    's-6-0-0-w': 0.47436,
    's-7-0-0-w': 0.58136,
    's-8-0-0-w': -0.48308,
    's-9-0-0-w': -0.16024,
    's-adx-bias': -0.4091,
    's-ao-bias': 0.76889,
    's-aroonosc-bias': 0.16228,
    's-bb_percent-bias': 0.19407,
    's-bb_width-bias': 0.11795,
    's-cci-bias': 0.8379,
    's-end-0-w': -0.14648,
    's-end-bias': -0.85697,
    's-fastd-bias': -0.00581,
    's-fisher_rsi_norm-bias': -0.05253,
    's-kc_percent-bias': -0.3562,
    's-kc_width-bias': 0.67451,
    's-macd-bias': -0.17742,
    's-macdhist-bias': -0.58328,
    's-macdsignal-bias': -0.79847,
    's-mfi-bias': -0.48236,
    's-roc-bias': -0.5914,
    's-rsi-bias': -0.9618,
    's-sar-bias': 0.57033,
    's-sma10-bias': 0.14349,
    's-sma100-bias': 0.02401,
    's-sma21-bias': 0.78191,
    's-sma3-bias': 0.72279,
    's-sma5-bias': -0.19383,
    's-sma50-bias': 0.63697,
    's-tema10-bias': 0.96837,
    's-tema100-bias': 0.77171,
    's-tema21-bias': 0.67279,
    's-tema3-bias': -0.24583,
    's-tema5-bias': -0.08997,
    's-tema50-bias': 0.65532,
    's-uo-bias': 0.67701,
    's-wbb_percent-bias': -0.658,
    's-wbb_width-bias': -0.71056
}

network_shape = [1]

class Genetic(IStrategy):
    # ROI table:
    minimal_roi = {
        "0": 0.21029,
        "11": 0.05876,
        "57": 0.02191,
        "281": 0
    }

    # Stoploss:
    stoploss = -1

    # Optimal ticker interval for the strategy
    ticker_interval = '2h'

    # Trailing stop:
    trailing_only_offset_is_reached = False
    trailing_stop = True
    trailing_stop_positive = 0.01019
    trailing_stop_positive_offset = 0.01164

    # run "populate_indicators" only for new candle
    process_only_new_candles = True

    # Experimental settings (configuration will overide these if set)
    use_sell_signal = True
    sell_profit_only = True
    ignore_roi_if_buy_signal = True

    startup_candle_count = 100

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Adds several different TA indicators to the given DataFrame

        Performance Note: For the best performance be frugal on the number of indicators
        you are using. Let uncomment only the indicator you are using in your strategies
        or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
        """

        # Momentum Indicators
        # ------------------------------------

        # ADX
        dataframe['adx'] = ta.ADX(dataframe) / 100

        # # Plus Directional Indicator / Movement
        # dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
        # dataframe['plus_di'] = ta.PLUS_DI(dataframe)

        # # Minus Directional Indicator / Movement
        # dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
        # dataframe['minus_di'] = ta.MINUS_DI(dataframe)

        # # Aroon, Aroon Oscillator
        # aroon = ta.AROON(dataframe)
        # dataframe['aroonup'] = aroon['aroonup']
        # dataframe['aroondown'] = aroon['aroondown']
        dataframe['aroonosc'] = ta.AROONOSC(dataframe) / 100

        # # Awesome Oscillator
        dataframe['ao'] = ((qtpylib.awesome_oscillator(dataframe) > 0).astype(int) - 0.5) * 2

        # # Keltner Channel
        keltner = qtpylib.keltner_channel(dataframe)
        dataframe["kc_upperband"] = keltner["upper"]
        dataframe["kc_lowerband"] = keltner["lower"]
        dataframe["kc_middleband"] = keltner["mid"]
        dataframe["kc_percent"] = (
                (dataframe["close"] - dataframe["kc_lowerband"]) /
                (dataframe["kc_upperband"] - dataframe["kc_lowerband"])
        )
        dataframe["kc_width"] = (
                (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"]
        )

        # # Ultimate Oscillator
        dataframe['uo'] = ta.ULTOSC(dataframe) / 100

        # # Commodity Channel Index: values [Oversold:-100, Overbought:100]
        dataframe['cci'] = ta.CCI(dataframe) / 200

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe) / 100

        # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy)
        rsi = 0.1 * (dataframe['rsi'] * 100 - 50)
        fisher_rsi = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1)

        # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy)
        dataframe['fisher_rsi_norm'] = 50 * (fisher_rsi + 1) / 100

        # # Stochastic Slow
        # stoch = ta.STOCH(dataframe)
        # dataframe['slowd'] = stoch['slowd']
        # dataframe['slowk'] = stoch['slowk']

        # Stochastic Fast
        stoch_fast = ta.STOCHF(dataframe)
        dataframe['fastd'] = stoch_fast['fastd'] / 100

        # # Stochastic RSI
        # stoch_rsi = ta.STOCHRSI(dataframe)
        # dataframe['fastd_rsi'] = stoch_rsi['fastd']
        # dataframe['fastk_rsi'] = stoch_rsi['fastk']

        # MACD
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']

        # MFI
        dataframe['mfi'] = ta.MFI(dataframe) / 100

        # # ROC
        dataframe['roc'] = ta.ROC(dataframe) / 100

        # Overlap Studies
        # ------------------------------------

        # 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"]
        )

        # Bollinger Bands - Weighted (EMA based instead of SMA)
        weighted_bollinger = qtpylib.weighted_bollinger_bands(
            qtpylib.typical_price(dataframe), window=20, stds=2
        )
        dataframe["wbb_upperband"] = weighted_bollinger["upper"]
        dataframe["wbb_lowerband"] = weighted_bollinger["lower"]
        dataframe["wbb_middleband"] = weighted_bollinger["mid"]
        dataframe["wbb_percent"] = (
                (dataframe["close"] - dataframe["wbb_lowerband"]) /
                (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"])
        )
        dataframe["wbb_width"] = (
                (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"]
        )

        # # SMA - Simple Moving Average
        dataframe['sma3'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=3) - 1
        dataframe['sma5'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=5) - 1
        dataframe['sma10'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=10) - 1
        dataframe['sma21'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=21) - 1
        dataframe['sma50'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=50) - 1
        dataframe['sma100'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=100) - 1

        # Parabolic SAR
        dataframe['sar'] = dataframe['close'] / ta.SAR(dataframe) - 1

        # TEMA - Triple Exponential Moving Average
        dataframe['tema3'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=3) - 1
        dataframe['tema5'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=5) - 1
        dataframe['tema10'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=10) - 1
        dataframe['tema21'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=21) - 1
        dataframe['tema50'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=50) - 1
        dataframe['tema100'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=100) - 1

        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
        """
        indicators = ['aroonosc', 'ao', 'uo', 'cci', 'rsi', 'fisher_rsi_norm', 'sar',
                      'sma3', 'sma5', 'sma10', 'sma21', 'sma50', 'sma100',
                      'tema3', 'tema5', 'tema10', 'tema21', 'tema50', 'tema100',
                      'fastd', 'adx', 'bb_percent', 'bb_width', 'macd', 'macdsignal', 'macdhist', 'mfi',
                      'wbb_percent', 'wbb_width', 'roc', 'kc_percent', 'kc_width']
        inputs = []
        for indicator in indicators:
            inputs.append(dataframe[indicator] + params[indicator + '-bias'])

        for index, layer_size in enumerate(network_shape):
            outputs = []
            for n in range(layer_size):
                weight = 0
                for i, input in enumerate(inputs):
                    weight += params['{}-{}-{}-w'.format(i, index, n)] * input
                weight += params['{}-{}-bias'.format(index, n)]
                outputs.append(activate(weight))
            inputs = outputs

        weight = 0
        for i, input in enumerate(inputs):
            weight += params['end-{}-w'.format(i)] * input
        weight += params['end-bias']

        dataframe.loc[activate(weight) > 0, 'buy'] = 1

        # Check that the candle had volume
        dataframe.loc[dataframe['volume'] <= 0, 'buy'] = 0

        return dataframe

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

        return dataframe

