# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# isort: skip_file
# --- Do not remove these libs ---
import numpy as np  # noqa
import pandas as pd  # noqa
from pandas import DataFrame
import catboost
import pickle
from scipy import signal
import pandas_ta as pta

from freqtrade.strategy import IStrategy
import technical.indicators as ftt
# --------------------------------
# Add your lib to import here
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.strategy import   DecimalParameter, merge_informative_pair, timeframe_to_minutes, BooleanParameter
from functools import reduce

# This class is a sample. Feel free to customize it.
class Prediction_Strategy_v3_2__discord_StickyF_20220315(IStrategy):

    INTERFACE_VERSION = 2

    minimal_roi = {
        "360": 0.0,
        "240": 0.05,
        "0": 0.1
    }

    # Optimal stoploss designed for the strategy.
    # This attribute will be overridden if the config file contains "stoploss".
    stoploss = -0.30

    # Trailing stoploss
    trailing_stop = True
    trailing_only_offset_is_reached = True
    trailing_stop_positive = 0.30
    trailing_stop_positive_offset = 0.0  # Disabled / not configured

    # Optimal timeframe for the strategy.
    timeframe = '1h'

    # 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 = True

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

    # Optional order type mapping.
    order_types = {
        'buy': 'limit',
        'sell': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    # Optional order time in force.
    order_time_in_force = {
        'buy': 'gtc',
        'sell': 'gtc'
    }
    plot_config = {
        'main_plot': {
            # 'close_sha': {'color': 'blue'},
            # 'JMA10': {'color': 'red'},
            # 'sar': {'color': 'green'},
        },
        'subplots': {
            
            "pred": {
                'pred4': {'color': 'red'},
                'ma_pred4': {'color': 'orange'},
                # 'pred3': {'color': 'orange'},
                # 'pred2': {'color': 'yellow'},
                # 'pred1': {'color': 'green'},
                'pred0': {'color': 'blue'},
                'ma_pred0': {'color': 'green'},
            },
            # "target" :{
            #     'target': {'color': 'blue'},
            #     'real_target': {'color': 'green'},
                
            # }
            
        }
    }

    def informative_pairs(self):
        return []
        
    buy_pred4 = DecimalParameter(0.40, 0.90, decimals=2, default=0.6, space="buy")
    
    sell_pred4 = DecimalParameter(0.0, 0.50, decimals=2, default=0.2, space="sell")
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        verbose = True
        col_use = [
                    'volume', 'smadiff_3', 'smadiff_5', 'smadiff_8', 'smadiff_13', 'smadiff_21', 
                    'smadiff_34', 'smadiff_55', 'smadiff_89', 'smadiff_120', 'smadiff_240', 
                    'maxdiff_3', 'maxdiff_5', 'maxdiff_8', 'maxdiff_13', 'maxdiff_21', 'maxdiff_34', 
                    'maxdiff_55', 'maxdiff_89', 'maxdiff_120', 'maxdiff_240', 'std_3', 'std_5', 'std_8', 
                    'std_13', 'std_21', 'std_34', 'std_55', 'std_89', 'std_120', 'std_240', 'ma_3', 'ma_5', 
                    'ma_8', 'ma_13', 'ma_21', 'ma_34', 'ma_55', 'ma_89', 'ma_120', 'ma_240', 'z_score_120', 
                    'time_hourmin', 'time_dayofweek', 'time_hour', 'vwap_low', 'ewo', 'cmf', 'r_14', 'r_64', 
                    'r_96', 'ema_vwma_osc_32', 'ema_vwma_osc_64', 'ema_vwma_osc_96', 't3_avg', 'pivot', 
                    'res1', 'res2', 'res3', 'sup1', 'sup2', 'sup3', 'cti' ]


        with open('user_data/notebooks/model_v3.pkl', 'rb') as f:
            model = pickle.load(f)
        model = model[0]

        dataframe['open_sha'], dataframe['close_sha'], dataframe['low_sha'] = heikin_ashi(dataframe, smooth_inputs=True, smooth_outputs=False, length=10)

        # Starting create features
        #sma diff
        for i in [3,5,8,13,21,34,55,89,120,240]:
            dataframe[f"smadiff_{i}"] = (dataframe['close_sha'].rolling(i).mean() / dataframe['close'])
        #max diff
        for i in [3,5,8,13,21,34,55,89,120,240]:
            dataframe[f"maxdiff_{i}"] = (dataframe['close_sha'].rolling(i).max() / dataframe['close'])
        #min diff
        for i in [3,5,8,13,21,34,55,89,120,240]:
            dataframe[f"maxdiff_{i}"] = (dataframe['close_sha'].rolling(i).min() / dataframe['close'])
        #volatiliy
        for i in [3,5,8,13,21,34,55,89,120,240]:
            dataframe[f"std_{i}"] = dataframe['close_sha'].rolling(i).std()

        #Return
        for i in [3,5,8,13,21,34,55,89,120,240]:
            dataframe[f"ma_{i}"] = dataframe['close_sha'].pct_change(i).rolling(i).mean()

        dataframe['z_score_120'] = ((dataframe.ma_13 - dataframe.ma_13.rolling(21).mean() + 1e-9) 
                            / (dataframe.ma_13.rolling(21).std() + 1e-9))

        dataframe["datetime"] = pd.to_datetime(dataframe["date"], unit='ms').apply(lambda x: x.replace(tzinfo=None)) 
        dataframe['time_hourmin'] = dataframe.datetime.dt.hour * 60 + dataframe.datetime.dt.minute
        dataframe['time_dayofweek'] = dataframe.datetime.dt.dayofweek
        dataframe['time_hour'] = dataframe.datetime.dt.hour


        vwap_low, vwap, vwap_high = vmap_b(dataframe, 20, 1)
        dataframe['vwap_low'] = vwap_low / dataframe["close"]

        dataframe['ewo'] = ewo(dataframe, 50, 200)

        dataframe['cmf'] = chaikin_money_flow(dataframe, 20)

        dataframe['r_14'] = williams_r(dataframe, period=14)
        dataframe['r_64'] = williams_r(dataframe, period=64)
        dataframe['r_96'] = williams_r(dataframe, period=96)

        dataframe['ema_vwma_osc_32'] = ema_vwma_osc(dataframe, 32)
        dataframe['ema_vwma_osc_64'] = ema_vwma_osc(dataframe, 64)
        dataframe['ema_vwma_osc_96'] = ema_vwma_osc(dataframe, 96)

        dataframe['t3_avg'] = t3_average(dataframe)

        dataframe['pivot'], dataframe['res1'], dataframe['res2'], dataframe['res3'], dataframe['sup1'], dataframe['sup2'], dataframe['sup3'] = pivot_points(dataframe, mode='fibonacci')
        dataframe['pivot'] = dataframe['pivot'] /dataframe["close"]
        dataframe['res1'] = dataframe['res1'] /dataframe["close"]
        dataframe['res2'] = dataframe['res2'] /dataframe["close"]
        dataframe['res3'] = dataframe['res3'] /dataframe["close"]
        dataframe['sup1'] = dataframe['sup1'] /dataframe["close"]
        dataframe['sup2'] = dataframe['sup2'] /dataframe["close"]
        dataframe['sup3'] = dataframe['sup3'] /dataframe["close"]

        dataframe['cti'] = pta.cti(dataframe["close_sha"], length=20)
        
        dataframe['sar'] = ta.SAR(dataframe)
        
        dataframe['JMA10'] = pta.jma(dataframe['close'],10,50)

        #Model predictions
        preds = pd.DataFrame(model.predict_proba(dataframe[col_use]))
        preds.columns = [f"pred{i}" for i in range(5)]
        dataframe = dataframe.reset_index(drop=True)
        dataframe = pd.concat([dataframe, preds], axis=1)

        dataframe['ma_pred4'] = dataframe['pred4'].rolling(10).mean()
        dataframe['ma_pred0'] = dataframe['pred0'].rolling(10).mean()

        return dataframe

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

        conditions = []
        dataframe.loc[:, 'buy_tag'] = ''
        
        
        buy_signal=(

            (dataframe['pred4'] > dataframe['pred0']) & 
            (dataframe['pred4'].shift(1) < dataframe['pred0'].shift(1)) & 
            (dataframe['ma_pred4'] > dataframe['ma_pred0']) & 
            (dataframe['volume'] > 0)  # Make sure Volume is not 0
        )
        
        dataframe.loc[buy_signal, 'buy_tag'] += 'buy_signal'
        conditions.append(buy_signal)    
        
        dataframe.loc[
            reduce(lambda x, y: x | y, conditions),
            'buy'
        ] = 1
        return dataframe

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

        dataframe.loc[
            (
                (dataframe['pred4'] < self.sell_pred4.value) & 
                (dataframe['volume'] > 0)  # Make sure Volume is not 0
            ),
            'sell'] = 0
        return dataframe
    
def vmap_b(dataframe, window_size=20, num_of_std=1):
    df = dataframe.copy()
    df['vwap'] = qtpylib.rolling_vwap(df,window=window_size)
    rolling_std = df['vwap'].rolling(window=window_size).std()
    df['vwap_low'] = df['vwap'] - (rolling_std * num_of_std)
    df['vwap_high'] = df['vwap'] + (rolling_std * num_of_std)
    return df['vwap_low'], df['vwap'], df['vwap_high']


# Elliot Wave Oscillator
def ewo(dataframe, sma1_length=5, sma2_length=35):
    sma1 = ta.EMA(dataframe, timeperiod=sma1_length)
    sma2 = ta.EMA(dataframe, timeperiod=sma2_length)
    smadif = (sma1 - sma2) / dataframe['close'] * 100
    return smadif

# Chaikin Money Flow
def chaikin_money_flow(dataframe, n=20, fillna=False) -> pd.Series:
    """Chaikin Money Flow (CMF)
    It measures the amount of Money Flow Volume over a specific period.
    http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:chaikin_money_flow_cmf
    Args:
        dataframe(pandas.Dataframe): dataframe containing ohlcv
        n(int): n period.
        fillna(bool): if True, fill nan values.
    Returns:
        pandas.Series: New feature generated.
    """
    mfv = ((dataframe['close'] - dataframe['low']) - (dataframe['high'] - dataframe['close'])) / (dataframe['high'] - dataframe['low'])
    mfv = mfv.fillna(0.0)  # float division by zero
    mfv *= dataframe['volume']
    cmf = (mfv.rolling(n, min_periods=0).sum()
           / dataframe['volume'].rolling(n, min_periods=0).sum())
    if fillna:
        cmf = cmf.replace([np.inf, -np.inf], np.nan).fillna(0)
    return pd.Series(cmf, name='cmf')

# Williams %R
def williams_r(dataframe: pd.DataFrame, period: int = 14) -> pd.Series:
    """Williams %R, or just %R, is a technical analysis oscillator showing the current closing price in relation to the high and low
        of the past N days (for a given N). It was developed by a publisher and promoter of trading materials, Larry Williams.
        Its purpose is to tell whether a stock or commodity market is trading near the high or the low, or somewhere in between,
        of its recent trading range.
        The oscillator is on a negative scale, from −100 (lowest) up to 0 (highest).
    """

    highest_high = dataframe["high"].rolling(center=False, window=period).max()
    lowest_low = dataframe["low"].rolling(center=False, window=period).min()

    WR = pd.Series(
        (highest_high - dataframe["close"]) / (highest_high - lowest_low),
        name=f"{period} Williams %R",
        )

    return WR * -100

# Volume Weighted Moving Average
def vwma(dataframe: pd.DataFrame, length: int = 10):
    """Indicator: Volume Weighted Moving Average (VWMA)"""
    # Calculate Result
    pv = dataframe['close'] * dataframe['volume']
    vwma = pd.Series(ta.SMA(pv, timeperiod=length) / ta.SMA(dataframe['volume'], timeperiod=length))
    vwma = vwma.fillna(0, inplace=True)
    return vwma

# Exponential moving average of a volume weighted simple moving average
def ema_vwma_osc(dataframe, len_slow_ma):
    slow_ema = pd.Series(ta.EMA(vwma(dataframe, len_slow_ma), len_slow_ma))
    return ((slow_ema - slow_ema.shift(1)) / slow_ema.shift(1)) * 100

def t3_average(dataframe, length=5):
    """
    T3 Average by HPotter on Tradingview
    https://www.tradingview.com/script/qzoC9H1I-T3-Average/
    """
    df = dataframe.copy()

    df['xe1'] = ta.EMA(df['close'], timeperiod=length)
    df['xe1'].fillna(0, inplace=True)
    df['xe2'] = ta.EMA(df['xe1'], timeperiod=length)
    df['xe2'].fillna(0, inplace=True)
    df['xe3'] = ta.EMA(df['xe2'], timeperiod=length)
    df['xe3'].fillna(0, inplace=True)
    df['xe4'] = ta.EMA(df['xe3'], timeperiod=length)
    df['xe4'].fillna(0, inplace=True)
    df['xe5'] = ta.EMA(df['xe4'], timeperiod=length)
    df['xe5'].fillna(0, inplace=True)
    df['xe6'] = ta.EMA(df['xe5'], timeperiod=length)
    df['xe6'].fillna(0, inplace=True)
    b = 0.7
    c1 = -b * b * b
    c2 = 3 * b * b + 3 * b * b * b
    c3 = -6 * b * b - 3 * b - 3 * b * b * b
    c4 = 1 + 3 * b + b * b * b + 3 * b * b
    df['T3Average'] = c1 * df['xe6'] + c2 * df['xe5'] + c3 * df['xe4'] + c4 * df['xe3']

    return df['T3Average']

def pivot_points(dataframe: pd.DataFrame, mode = 'fibonacci') -> pd.Series:
    if mode == 'simple':
        hlc3_pivot = (dataframe['high'] + dataframe['low'] + dataframe['close']).shift(1) / 3
        res1 = hlc3_pivot * 2 - dataframe['low'].shift(1)
        sup1 = hlc3_pivot * 2 - dataframe['high'].shift(1)
        res2 = hlc3_pivot + (dataframe['high'] - dataframe['low']).shift()
        sup2 = hlc3_pivot - (dataframe['high'] - dataframe['low']).shift()
        res3 = hlc3_pivot * 2 + (dataframe['high'] - 2 * dataframe['low']).shift()
        sup3 = hlc3_pivot * 2 - (2 * dataframe['high'] - dataframe['low']).shift()
        return hlc3_pivot, res1, res2, res3, sup1, sup2, sup3
    elif mode == 'fibonacci':
        hlc3_pivot = (dataframe['high'] + dataframe['low'] + dataframe['close']).shift(1) / 3
        hl_range = (dataframe['high'] - dataframe['low']).shift(1)
        res1 = hlc3_pivot + 0.382 * hl_range
        sup1 = hlc3_pivot - 0.382 * hl_range
        res2 = hlc3_pivot + 0.618 * hl_range
        sup2 = hlc3_pivot - 0.618 * hl_range
        res3 = hlc3_pivot + 1 * hl_range
        sup3 = hlc3_pivot - 1 * hl_range
        return hlc3_pivot, res1, res2, res3, sup1, sup2, sup3
    elif mode == 'DeMark':
        demark_pivot_lt = (dataframe['low'] * 2 + dataframe['high'] + dataframe['close'])
        demark_pivot_eq = (dataframe['close'] * 2 + dataframe['low'] + dataframe['high'])
        demark_pivot_gt = (dataframe['high'] * 2 + dataframe['low'] + dataframe['close'])
        demark_pivot = np.where((dataframe['close'] < dataframe['open']), demark_pivot_lt, np.where((dataframe['close'] > dataframe['open']), demark_pivot_gt, demark_pivot_eq))
        dm_pivot = demark_pivot / 4
        dm_res = demark_pivot / 2 - dataframe['low']
        dm_sup = demark_pivot / 2 - dataframe['high']
        return dm_pivot, dm_res, dm_sup

def heikin_ashi(dataframe, smooth_inputs = False, smooth_outputs = False, length = 10):
    df = dataframe[['open','close','high','low']].copy().fillna(0)
    if smooth_inputs:
        df['open_s']  = ta.EMA(df['open'], timeframe = length)
        df['high_s']  = ta.EMA(df['high'], timeframe = length)
        df['low_s']   = ta.EMA(df['low'],  timeframe = length)
        df['close_s'] = ta.EMA(df['close'],timeframe = length)

        open_ha  = (df['open_s'].shift(1) + df['close_s'].shift(1)) / 2
        high_ha  = df.loc[:, ['high_s', 'open_s', 'close_s']].max(axis=1)
        low_ha   = df.loc[:, ['low_s', 'open_s', 'close_s']].min(axis=1)
        close_ha = (df['open_s'] + df['high_s'] + df['low_s'] + df['close_s'])/4
    else:
        open_ha  = (df['open'].shift(1) + df['close'].shift(1)) / 2
        high_ha  = df.loc[:, ['high', 'open', 'close']].max(axis=1)
        low_ha   = df.loc[:, ['low', 'open', 'close']].min(axis=1)
        close_ha = (df['open'] + df['high'] + df['low'] + df['close'])/4

    open_ha = open_ha.fillna(0)
    high_ha = high_ha.fillna(0)
    low_ha  = low_ha.fillna(0)
    close_ha = close_ha.fillna(0)

    if smooth_outputs:
        open_sha  = ta.EMA(open_ha, timeframe = length)
        high_sha  = ta.EMA(high_ha, timeframe = length)
        low_sha   = ta.EMA(low_ha, timeframe = length)
        close_sha = ta.EMA(close_ha, timeframe = length)

        return open_sha, close_sha, low_sha
    else:
        return open_ha, close_ha, low_ha
    
 
