# GodStraNew Strategy
# Author: @Mablue (Masoud Azizi)
# github: https://github.com/mablue/
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew
# --- Do not remove these libs ---
from datetime import timedelta, datetime

from freqtrade import data
from freqtrade.persistence import Trade
from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter

from numpy.lib import math
from freqtrade.strategy.interface import IStrategy
import pandas
from pandas import DataFrame

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

# Add your lib to import here
# TODO: talib is fast but have not more indicators
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from functools import reduce
import numpy as np
from random import shuffle

#  TODO: this gene is removed 'MAVP' cuz or error on periods
all_god_genes = {
    'Overlap Studies': {
        'BBANDS-0',             # Bollinger Bands
        'BBANDS-1',             # Bollinger Bands
        'BBANDS-2',             # Bollinger Bands
        'DEMA',                 # Double Exponential Moving Average
        'EMA',                  # Exponential Moving Average
        'HT_TRENDLINE',         # Hilbert Transform - Instantaneous Trendline
        'KAMA',                 # Kaufman Adaptive Moving Average
        'MA',                   # Moving average
        'MAMA-0',               # MESA Adaptive Moving Average
        'MAMA-1',               # MESA Adaptive Moving Average
        # TODO: Fix this
        # 'MAVP',               # Moving average with variable period
        'MIDPOINT',             # MidPoint over period
        'MIDPRICE',             # Midpoint Price over period
        'SAR',                  # Parabolic SAR
        'SAREXT',               # Parabolic SAR - Extended
        'SMA',                  # Simple Moving Average
        'T3',                   # Triple Exponential Moving Average (T3)
        'TEMA',                 # Triple Exponential Moving Average
        'TRIMA',                # Triangular Moving Average
        'WMA',                  # Weighted Moving Average
    },
    'Momentum Indicators': {
        'ADX',                  # Average Directional Movement Index
        'ADXR',                 # Average Directional Movement Index Rating
        'APO',                  # Absolute Price Oscillator
        'AROON-0',              # Aroon
        'AROON-1',              # Aroon
        'AROONOSC',             # Aroon Oscillator
        'BOP',                  # Balance Of Power
        'CCI',                  # Commodity Channel Index
        'CMO',                  # Chande Momentum Oscillator
        'DX',                   # Directional Movement Index
        'MACD-0',               # Moving Average Convergence/Divergence
        'MACD-1',               # Moving Average Convergence/Divergence
        'MACD-2',               # Moving Average Convergence/Divergence
        'MACDEXT-0',            # MACD with controllable MA type
        'MACDEXT-1',            # MACD with controllable MA type
        'MACDEXT-2',            # MACD with controllable MA type
        'MACDFIX-0',            # Moving Average Convergence/Divergence Fix 12/26
        'MACDFIX-1',            # Moving Average Convergence/Divergence Fix 12/26
        'MACDFIX-2',            # Moving Average Convergence/Divergence Fix 12/26
        'MFI',                  # Money Flow Index
        'MINUS_DI',             # Minus Directional Indicator
        'MINUS_DM',             # Minus Directional Movement
        'MOM',                  # Momentum
        'PLUS_DI',              # Plus Directional Indicator
        'PLUS_DM',              # Plus Directional Movement
        'PPO',                  # Percentage Price Oscillator
        'ROC',                  # Rate of change : ((price/prevPrice)-1)*100
        # Rate of change Percentage: (price-prevPrice)/prevPrice
        'ROCP',
        'ROCR',                 # Rate of change ratio: (price/prevPrice)
        # Rate of change ratio 100 scale: (price/prevPrice)*100
        'ROCR100',
        'RSI',                  # Relative Strength Index
        'STOCH-0',              # Stochastic
        'STOCH-1',              # Stochastic
        'STOCHF-0',             # Stochastic Fast
        'STOCHF-1',             # Stochastic Fast
        'STOCHRSI-0',           # Stochastic Relative Strength Index
        'STOCHRSI-1',           # Stochastic Relative Strength Index
        # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA
        'TRIX',
        'ULTOSC',               # Ultimate Oscillator
        'WILLR',                # Williams' %R
    },
    'Volume Indicators': {
        'AD',                   # Chaikin A/D Line
        'ADOSC',                # Chaikin A/D Oscillator
        'OBV',                  # On Balance Volume
    },
    'Volatility Indicators': {
        'ATR',                  # Average True Range
        'NATR',                 # Normalized Average True Range
        'TRANGE',               # True Range
    },
    'Price Transform': {
        'AVGPRICE',             # Average Price
        'MEDPRICE',             # Median Price
        'TYPPRICE',             # Typical Price
        'WCLPRICE',             # Weighted Close Price
    },
    'Cycle Indicators': {
        'HT_DCPERIOD',          # Hilbert Transform - Dominant Cycle Period
        'HT_DCPHASE',           # Hilbert Transform - Dominant Cycle Phase
        'HT_PHASOR-0',          # Hilbert Transform - Phasor Components
        'HT_PHASOR-1',          # Hilbert Transform - Phasor Components
        'HT_SINE-0',            # Hilbert Transform - SineWave
        'HT_SINE-1',            # Hilbert Transform - SineWave
        'HT_TRENDMODE',         # Hilbert Transform - Trend vs Cycle Mode
    },
    'Pattern Recognition': {
        'CDL2CROWS',            # Two Crows
        'CDL3BLACKCROWS',       # Three Black Crows
        'CDL3INSIDE',           # Three Inside Up/Down
        'CDL3LINESTRIKE',       # Three-Line Strike
        'CDL3OUTSIDE',          # Three Outside Up/Down
        'CDL3STARSINSOUTH',     # Three Stars In The South
        'CDL3WHITESOLDIERS',    # Three Advancing White Soldiers
        'CDLABANDONEDBABY',     # Abandoned Baby
        'CDLADVANCEBLOCK',      # Advance Block
        'CDLBELTHOLD',          # Belt-hold
        'CDLBREAKAWAY',         # Breakaway
        'CDLCLOSINGMARUBOZU',   # Closing Marubozu
        'CDLCONCEALBABYSWALL',  # Concealing Baby Swallow
        'CDLCOUNTERATTACK',     # Counterattack
        'CDLDARKCLOUDCOVER',    # Dark Cloud Cover
        'CDLDOJI',              # Doji
        'CDLDOJISTAR',          # Doji Star
        'CDLDRAGONFLYDOJI',     # Dragonfly Doji
        'CDLENGULFING',         # Engulfing Pattern
        'CDLEVENINGDOJISTAR',   # Evening Doji Star
        'CDLEVENINGSTAR',       # Evening Star
        'CDLGAPSIDESIDEWHITE',  # Up/Down-gap side-by-side white lines
        'CDLGRAVESTONEDOJI',    # Gravestone Doji
        'CDLHAMMER',            # Hammer
        'CDLHANGINGMAN',        # Hanging Man
        'CDLHARAMI',            # Harami Pattern
        'CDLHARAMICROSS',       # Harami Cross Pattern
        'CDLHIGHWAVE',          # High-Wave Candle
        'CDLHIKKAKE',           # Hikkake Pattern
        'CDLHIKKAKEMOD',        # Modified Hikkake Pattern
        'CDLHOMINGPIGEON',      # Homing Pigeon
        'CDLIDENTICAL3CROWS',   # Identical Three Crows
        'CDLINNECK',            # In-Neck Pattern
        'CDLINVERTEDHAMMER',    # Inverted Hammer
        'CDLKICKING',           # Kicking
        'CDLKICKINGBYLENGTH',   # Kicking - bull/bear determined by the longer marubozu
        'CDLLADDERBOTTOM',      # Ladder Bottom
        'CDLLONGLEGGEDDOJI',    # Long Legged Doji
        'CDLLONGLINE',          # Long Line Candle
        'CDLMARUBOZU',          # Marubozu
        'CDLMATCHINGLOW',       # Matching Low
        'CDLMATHOLD',           # Mat Hold
        'CDLMORNINGDOJISTAR',   # Morning Doji Star
        'CDLMORNINGSTAR',       # Morning Star
        'CDLONNECK',            # On-Neck Pattern
        'CDLPIERCING',          # Piercing Pattern
        'CDLRICKSHAWMAN',       # Rickshaw Man
        'CDLRISEFALL3METHODS',  # Rising/Falling Three Methods
        'CDLSEPARATINGLINES',   # Separating Lines
        'CDLSHOOTINGSTAR',      # Shooting Star
        'CDLSHORTLINE',         # Short Line Candle
        'CDLSPINNINGTOP',       # Spinning Top
        'CDLSTALLEDPATTERN',    # Stalled Pattern
        'CDLSTICKSANDWICH',     # Stick Sandwich
        # Takuri (Dragonfly Doji with very long lower shadow)
        'CDLTAKURI',
        'CDLTASUKIGAP',         # Tasuki Gap
        'CDLTHRUSTING',         # Thrusting Pattern
        'CDLTRISTAR',           # Tristar Pattern
        'CDLUNIQUE3RIVER',      # Unique 3 River
        'CDLUPSIDEGAP2CROWS',   # Upside Gap Two Crows
        'CDLXSIDEGAP3METHODS',  # Upside/Downside Gap Three Methods

    },
    'Statistic Functions': {
        'BETA',                 # Beta
        'CORREL',               # Pearson's Correlation Coefficient (r)
        'LINEARREG',            # Linear Regression
        'LINEARREG_ANGLE',      # Linear Regression Angle
        'LINEARREG_INTERCEPT',  # Linear Regression Intercept
        'LINEARREG_SLOPE',      # Linear Regression Slope
        'STDDEV',               # Standard Deviation
        'TSF',                  # Time Series Forecast
        'VAR',                  # Variance
    }

}
god_genes = set()
########################### SETTINGS ##############################

# god_genes = {'SMA'}
god_genes |= all_god_genes['Overlap Studies']
god_genes |= all_god_genes['Momentum Indicators']
god_genes |= all_god_genes['Volume Indicators']
god_genes |= all_god_genes['Volatility Indicators']
god_genes |= all_god_genes['Price Transform']
god_genes |= all_god_genes['Cycle Indicators']
god_genes |= all_god_genes['Pattern Recognition']
god_genes |= all_god_genes['Statistic Functions']

#timeperiods = [5, 6, 12, 15, 50, 55, 100, 110]
timeperiods = [5, 10, 20, 50, 100]

operators = [
    "D",  # Disabled gene
    ">",  # Indicator, bigger than cross indicator
    "<",  # Indicator, smaller than cross indicator
    "=",  # Indicator, equal with cross indicator
    "C",  # Indicator, crossed the cross indicator
    "CA",  # Indicator, crossed above the cross indicator
    "CB",  # Indicator, crossed below the cross indicator
    ">R",  # Normalized indicator, bigger than real number
    "=R",  # Normalized indicator, equal with real number
    "<R",  # Normalized indicator, smaller than real number
    "/>R",  # Normalized indicator devided to cross indicator, bigger than real number
    "/=R",  # Normalized indicator devided to cross indicator, equal with real number
    "/<R",  # Normalized indicator devided to cross indicator, smaller than real number
    "UT",  # Indicator, is in UpTrend status
    "DT",  # Indicator, is in DownTrend status
    "OT",  # Indicator, is in Off trend status(RANGE)
    "CUT",  # Indicator, Entered to UpTrend status
    "CDT",  # Indicator, Entered to DownTrend status
    "COT"  # Indicator, Entered to Off trend status(RANGE)
]
# number of candles to check up,don,off trend.
TREND_CHECK_CANDLES = 8
DECIMALS = 2
########################### END SETTINGS ##########################
# DATAFRAME = DataFrame()

god_genes = list(god_genes)
# print('selected indicators for optimzatin: \n', god_genes)

god_genes_with_timeperiod = list()
for god_gene in god_genes:
    for timeperiod in timeperiods:
        # print(f'{god_gene}-{timeperiod}')
        god_genes_with_timeperiod.append(f'{god_gene}-{timeperiod}')

# Let give somethings to CatagoricalParam to Play with them
# When just one thing is inside catagorical lists
# TODO: its Not True Way :)
if len(god_genes) == 1:
    god_genes = god_genes*2
if len(timeperiods) == 1:
    timeperiods = timeperiods*2
if len(operators) == 1:
    operators = operators*2

# number of candles to check up,don,off trend.
TREND_CHECK_CANDLES = 8
DECIMALS = 2

buy_crossed_indicator0 = 'MINUS_DM-5'
buy_operator0 = "/<R"
buy_indicator0 = 'MA-20'

buy_crossed_indicator1 = 'DX-5'
buy_operator1 = ">"
buy_indicator1 = 'STOCH-1-10'

buy_crossed_indicator2 = 'LINEARREG-50'
buy_operator2 = "/<R"
buy_indicator2 = 'CDLDRAGONFLYDOJI-5'

# "buy_real_num0": 0.46,
# "buy_real_num1": 0.48,
# "buy_real_num2": 0.67



def normalize(df):
    df = (df-df.min())/(df.max()-df.min())
    return df


def gene_calculator(dataframe, indicator):
    # Cuz Timeperiods not effect calculating CDL patterns recognations
    if 'CDL' in indicator:
        splited_indicator = indicator.split('-')
        splited_indicator[1] = "0"
        new_indicator = "-".join(splited_indicator)
        # print(indicator, new_indicator)
        indicator = new_indicator

    gene = indicator.split("-")

    gene_name = gene[0]
    gene_len = len(gene)

    if indicator in dataframe.keys():
        # print(f"{indicator}, calculated befoure")
        # print(len(dataframe.keys()))
        return dataframe[indicator]
    else:
        result = None
        # For Pattern Recognations
        if gene_len == 1:
            # print('gene_len == 1\t', indicator)
            result = getattr(ta, gene_name)(
                dataframe
            )
            return normalize(result)
        elif gene_len == 2:
            # print('gene_len == 2\t', indicator)
            gene_timeperiod = int(gene[1])
            result = getattr(ta, gene_name)(
                dataframe,
                timeperiod=gene_timeperiod,
            )
            return normalize(result)
        # For
        elif gene_len == 3:
            # print('gene_len == 3\t', indicator)
            gene_timeperiod = int(gene[2])
            gene_index = int(gene[1])
            result = getattr(ta, gene_name)(
                dataframe,
                timeperiod=gene_timeperiod,
            ).iloc[:, gene_index]
            return normalize(result)
        # For trend operators(MA-5-SMA-4)
        elif gene_len == 4:
            # print('gene_len == 4\t', indicator)
            gene_timeperiod = int(gene[1])
            sharp_indicator = f'{gene_name}-{gene_timeperiod}'
            dataframe[sharp_indicator] = getattr(ta, gene_name)(
                dataframe,
                timeperiod=gene_timeperiod,
            )
            return normalize(ta.SMA(dataframe[sharp_indicator].fillna(0), TREND_CHECK_CANDLES))
        # For trend operators(STOCH-0-4-SMA-4)
        elif gene_len == 5:
            # print('gene_len == 5\t', indicator)
            gene_timeperiod = int(gene[2])
            gene_index = int(gene[1])
            sharp_indicator = f'{gene_name}-{gene_index}-{gene_timeperiod}'
            dataframe[sharp_indicator] = getattr(ta, gene_name)(
                dataframe,
                timeperiod=gene_timeperiod,
            ).iloc[:, gene_index]
            return normalize(ta.SMA(dataframe[sharp_indicator].fillna(0), TREND_CHECK_CANDLES))


def condition_generator(dataframe, operator, indicator, crossed_indicator, real_num):

    condition = (dataframe['volume'] > 10)

    # TODO : it ill callculated in populate indicators.

    dataframe[indicator] = gene_calculator(dataframe, indicator)
    dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator)

    indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}"
    if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]:
        dataframe[indicator_trend_sma] = gene_calculator(
            dataframe, indicator_trend_sma)

    if operator == ">":
        condition = (
            dataframe[indicator] > dataframe[crossed_indicator]
        )
    elif operator == "=":
        condition = (
            np.isclose(dataframe[indicator], dataframe[crossed_indicator])
        )
    elif operator == "<":
        condition = (
            dataframe[indicator] < dataframe[crossed_indicator]
        )
    elif operator == "C":
        condition = (
            (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) |
            (qtpylib.crossed_above(
                dataframe[indicator], dataframe[crossed_indicator]))
        )
    elif operator == "CA":
        condition = (
            qtpylib.crossed_above(
                dataframe[indicator], dataframe[crossed_indicator])
        )
    elif operator == "CB":
        condition = (
            qtpylib.crossed_below(
                dataframe[indicator], dataframe[crossed_indicator])
        )
    elif operator == ">R":
        condition = (
            dataframe[indicator] > real_num
        )
    elif operator == "=R":
        condition = (
            np.isclose(dataframe[indicator], real_num)
        )
    elif operator == "<R":
        condition = (
            dataframe[indicator] < real_num
        )
    elif operator == "/>R":
        condition = (
            dataframe[indicator].div(dataframe[crossed_indicator]) > real_num
        )
    elif operator == "/=R":
        condition = (
            np.isclose(dataframe[indicator].div(
                dataframe[crossed_indicator]), real_num)
        )
    elif operator == "/<R":
        condition = (
            dataframe[indicator].div(dataframe[crossed_indicator]) < real_num
        )
    elif operator == "UT":
        condition = (
            dataframe[indicator] > dataframe[indicator_trend_sma]
        )
    elif operator == "DT":
        condition = (
            dataframe[indicator] < dataframe[indicator_trend_sma]
        )
    elif operator == "OT":
        condition = (

            np.isclose(dataframe[indicator], dataframe[indicator_trend_sma])
        )
    elif operator == "CUT":
        condition = (
            (
                qtpylib.crossed_above(
                    dataframe[indicator],
                    dataframe[indicator_trend_sma]
                )
            ) &
            (
                dataframe[indicator] > dataframe[indicator_trend_sma]
            )
        )
    elif operator == "CDT":
        condition = (
            (
                qtpylib.crossed_below(
                    dataframe[indicator],
                    dataframe[indicator_trend_sma]
                )
            ) &
            (
                dataframe[indicator] < dataframe[indicator_trend_sma]
            )
        )
    elif operator == "COT":
        condition = (
            (
                (
                    qtpylib.crossed_below(
                        dataframe[indicator],
                        dataframe[indicator_trend_sma]
                    )
                ) |
                (
                    qtpylib.crossed_above(
                        dataframe[indicator],
                        dataframe[indicator_trend_sma]
                    )
                )
            ) &
            (
                np.isclose(
                    dataframe[indicator],
                    dataframe[indicator_trend_sma]
                )
            )
        )

    return condition, dataframe


class GodStraJD3_8(IStrategy):
    # #################### RESULTS PASTE PLACE ####################
    # ROI table:
    minimal_roi = {
        "0": 10,
        # "600": 0.12,
        # "1200": 0.08,
        # "2400": 0.06,
        # "3600": 0.04,
        # "7289": 0
    }

    # Stoploss:
    stoploss = -1
    # Buy hypers
    timeframe = '5m'

    # Trailing stoploss
    trailing_stop = False
    trailing_stop_positive = 0.15
    trailing_stop_positive_offset = 0.20
    trailing_only_offset_is_reached = True

    sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0, space='sell')
    sell_percent3 = DecimalParameter(0, 0.03, decimals=3, default=0, space='sell')
    sell_percent5 = DecimalParameter(0, 0.04, decimals=3, default=0, space='sell')

    # Sell Hyperoptable Parameters/Spaces.
    sell_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLSHOOTINGSTAR-150", space='sell')
    # sell_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="MAMA-1-100", space='sell')
    # sell_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLMATHOLD-6", space='sell')

    sell_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLUPSIDEGAP2CROWS-5", space='sell')
    # sell_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHARAMICROSS-150", space='sell')
    # sell_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDL2CROWS-5", space='sell')

    sell_operator0 = CategoricalParameter(operators, default="<R", space='sell')
    # sell_operator1 = CategoricalParameter(operators, default="D", space='sell')
    # sell_operator2 = CategoricalParameter(operators, default="/>R", space='sell')

    sell_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.09731, space='sell')
    # sell_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.81657, space='sell')
    # sell_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.87267, space='sell')

    plot_config = {
        # Main plot indicators (Moving averages, ...)
        'main_plot': {
            'bb_lowerband': {'color': 'red'},
            'bb_upperband': {'color': 'green'},
            'sma100': {'color': 'blue'},
            'sma10': {'color': 'yellow'},
             'min20': {'color': 'white'},
              'max20': {'color': 'white'},
            'min50': {'color': 'yellow'},
            'max50': {'color': 'yellow'},
            'sma20': {'color': 'cyan'}
        },
        'subplots': {
            # Subplots - each dict defines one additional plot
            "BB": {
                'bb_width': {'color': 'white'},
                'bb_min': {'color': 'red'},
            },
            "Ind0": {
                buy_crossed_indicator0: {'color': 'green'},
                buy_indicator0: {'color': 'red'}
            },
            "Cond1": {
                "cond1": {'color': 'yellow'}
            },
            "Pentes": {
                "bb_lower_pente": {'color': 'red'},
                "sma10_pente": {'color': 'yellow'},
                "sma20_pente": {'color': 'cyan'}
            },
            # "Ind2": {
            #     buy_indicator2: {'color': 'cyan'},
            #     buy_crossed_indicator2: {'color': 'blue'},
            # },
            # "Rsi": {
            #     'rsi': {'color': 'pink'},
            # },
            # "rolling": {
            #     'bb_rolling': {'color': '#87e470'},
            #     "bb_rolling_min": {'color': '#ac3e2a'}
            # },
            "percent": {
                "percent": {'color': 'green'},
                "percent3": {'color': 'blue'},
                "percent5": {'color': 'red'}
            }
        }
    }


    # #################### END OF RESULT PLACE ####################

    # TODO: Its not dry code!
    # Buy Hyperoptable Parameters/Spaces.
    # "buy_real_num0": 0.46,
    # "buy_real_num1": 0.48,
    # "buy_real_num2": 0.67
    #
    # buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.46, space='buy')
    # buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.48, space='buy')
    # buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.67, space='buy')

    profit_no_change = BooleanParameter(default=True, space="buy")
    profit_quick_lost = BooleanParameter(default=True, space="buy")
    profit_sma10 = BooleanParameter(default=True, space="buy")
    profit_sma20 = BooleanParameter(default=True, space="buy")
    profit_quick_gain = BooleanParameter(default=True, space="buy")
    profit_quick_gain_3 = BooleanParameter(default=True, space="buy")
    profit_old_sma10 = BooleanParameter(default=True, space="buy")
    profit_very_old_sma10 = BooleanParameter(default=True, space="buy")
    profit_over_rsi = BooleanParameter(default=True, space="buy")
    profit_short_loss = BooleanParameter(default=True, space="buy")

    # buy_cond1_num0 = DecimalParameter(0, 10, decimals=DECIMALS, default=1, space='buy')
    # buy_cond1_num1 = DecimalParameter(0, 10, decimals=DECIMALS, default=2, space='buy')
    # buy_bbwidth_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.035, space='buy')
    # buy_bbwidth_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.055, space='buy')

    protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection')
    protection_stop = IntParameter(1, 100, default=48, space='protection')
    protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection')
    lookback = IntParameter(1, 200, default=48, space='protection')
    trade_limit = IntParameter(1, 10, default=2, space='protection')
    protection_cooldown = IntParameter(1, 100, default=10, space='protection')
    lookback_stoploss = IntParameter(1, 200, default=48, space='protection')
    trade_limit_stoploss = IntParameter(1, 10, default=2, space='protection')

    def calc_profit(self, price: float, current: float) -> float:
        fee = 1.0007
        profit = ((current*fee) - (price*fee))

        return float(f"{profit:.8f}")

    def calc_percentage_lower(self, price: float, current: float) -> float:
        fee = 1.0007
        price = price*fee
        current = current*fee
        lowerpercent = ((price-current)/(price*fee))*100
        return float(f"{lowerpercent:.8f}")

    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": self.protection_cooldown.value
            },
            # {
            #     "method": "MaxDrawdown",
            #     "lookback_period_candles": self.lookback.value,
            #     "trade_limit": self.trade_limit.value,
            #     "stop_duration_candles": self.protection_stop.value,
            #     "max_allowed_drawdown": self.protection_max_allowed_dd.value,
            #     "only_per_pair": False
            # },
            # {
            #     "method": "StoplossGuard",
            #     "lookback_period_candles": self.lookback_stoploss.value,
            #     "trade_limit": self.trade_limit_stoploss.value,
            #     "stop_duration_candles": self.protection_stoploss_stop.value,
            #     "only_per_pair": False
            # },
        ]

    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) & (
                (last_candle['percent5'] <= -0.015)
                | (last_candle['percent10'] <= -0.015)
                | (last_candle['percent20'] <= -0.015)
            ) \
            & ((current_time - trade.open_date_utc).seconds >= 2400):
            return "quick_lost"

        if self.profit_quick_lost.value:
            if (current_profit >= 0) & (last_candle['percent3'] < -0.01) \
                    & ((current_time - trade.open_date_utc).seconds >= 2400):
                return "quick_lost_1H"

        if self.profit_no_change.value:
            if (current_profit > 0.015) & (last_candle['percent20'] < 0.0) & ((current_time - trade.open_date_utc).seconds >= 2400):
                return "no_change"

        #if (current_profit > 0.01) & (last_candle['rsi'] < 30):
        #    return "small_rsi"
        if self.profit_quick_gain_3.value:
            if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600):
                return "quick_gain_3"
        if self.profit_quick_gain.value:
            if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600)
                return "quick_gain"

        if self.profit_sma10.value:
            if (current_profit > 0.01) \
                    & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \
                        | (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 self.profit_sma20.value:
            if (current_profit > 0.005) & (last_candle['percent5'] < 0) & (last_candle['sma20_pente'] < -0.015)\
                    & ((current_time - trade.open_date_utc).seconds >= 3600) \
                    & ((previous_last_candle['sma20'] > last_candle['sma20'] * 1.005) &
                        ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))):
                # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
                return 'sma20'

        # if self.profit_old_sma10.value:
        #     if (current_profit > 0) \
        #             & ((current_time - trade.open_date_utc).days >= 3) \
        #             & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \
        #             & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
        #         # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #         return 'old_sma10'
        # if self.profit_very_old_sma10.value:
        #     if (current_profit > -0.01) \
        #             & ((current_time - trade.open_date_utc).days >= 6) \
        #             & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \
        #             & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
        #         # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #         return 'very_old_sma10'

        if self.profit_over_rsi.value:
            if (current_profit > 0) \
                    & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
                # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
                return 'over_rsi'

        if self.profit_short_loss.value:
            if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\
                    & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
                # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
                return 'short_lost'

        # if (current_profit > 0) \
        #         & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
        #     # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #     return 'over_rsi_2'

    def informative_pairs(self):
        return []

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

        dataframe['profit'] = 0
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe)
        dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10)
        dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20)
        dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50)
        dataframe['sma100'] = ta.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()

        # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)):
        #     dataframe["percent_ok"] = new dataframe()
        # else:
        #     dataframe["percent_ok"] = 0

        dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200)
        dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20)
        dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50)
        dataframe["volume10"] = dataframe["volume"].rolling(10).mean()

        dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200)
        dataframe['max20'] = ta.MAX(dataframe['close'], timeperiod=20)
        dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50)

        dataframe['max_min'] = dataframe['max'] / dataframe['min']
        # 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"]
        )
        dataframe["bb_lower_pente"] = 100 * (dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1)) / dataframe["bb_lowerband"]
        dataframe["sma10_pente"] = 100 * (dataframe["sma10"] - dataframe["sma10"].shift(1)) / dataframe["sma10"]
        dataframe["sma20_pente"] = 100 * (dataframe["sma20"] - dataframe["sma20"].shift(1)) / dataframe["sma20"]

        dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36)

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

        dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0)
        dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1)
        dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2)

        dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0)
        dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1)
        dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2)

        dataframe["HT_PHASOR-0-100"] = gene_calculator(dataframe, "HT_PHASOR-0-100")
        dataframe["CDLIDENTICAL3CROWS-100"] = gene_calculator(dataframe, "CDLIDENTICAL3CROWS-100")
        dataframe["DEMA-10"] = gene_calculator(dataframe, "DEMA-10")
        dataframe["CDLMORNINGDOJISTAR-20"] = gene_calculator(dataframe, "CDLMORNINGDOJISTAR-20")

        dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0])
        # dataframe["q_0.1"] = dataframe.rolling(50).quantile(.1)['close']
        # dataframe["q_0.25"] = dataframe.rolling(50).quantile(.25)['close']
        # dataframe["q_0.33"] = dataframe.rolling(50).quantile(.33)['close']

        dataframe["MACD-2-10"] = gene_calculator(dataframe, "MACD-2-10")
        dataframe["CDLIDENTICAL3CROWS-10"] = gene_calculator(dataframe, "CDLIDENTICAL3CROWS-10")
        dataframe["PPO-10"] = gene_calculator(dataframe, "PPO-10")
        dataframe["MINUS_DM-10"] = gene_calculator(dataframe, "MINUS_DM-10")
        dataframe["MOM-10"] = gene_calculator(dataframe, "MOM-10")
        dataframe["MACDEXT-0-100"] = gene_calculator(dataframe, "MACDEXT-0-100")

        # # EMA - Exponential Moving Average
        # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)

        percent_lower = False
        current_price = dataframe['close'].iloc[-1]

        dataframe['should_sell'] = False
        dataframe['should_buy'] = False

        # Get the previous trade
        trade = Trade.get_trades_proxy(is_open=False, pair=metadata['pair'])
        if trade:
            trade = trade[-1]
            lsp = trade.close_rate
            if lsp:
                percent_lower = self.calc_percentage_lower(price=lsp, current=current_price)
            # Found a bug? When force selling it doesnt close it
            else:
                lsp = trade.open_rate
                if lsp:
                    percent_lower = self.calc_percentage_lower(price=lsp, current=current_price)
        else:
            lsp = 0.00

        # Get the current Trade
        trade = Trade.get_trades_proxy(is_open=True, pair=metadata['pair'])
        if trade:
            trade = trade[-1]
            lbp = trade.open_rate
            open_trade = True
            profit = self.calc_profit(price=lbp, current=current_price)
            profit_percent = (profit/lbp)*100
        else:
            lbp = 0.00
            open_trade = False
            profit = False
            profit_percent = False
        # print("------------")
        # print("Last Sold For:", lsp)

        # if open_trade:
        #     print("Bought for: ", lbp)
        # print("Current Price: ", current_price)
        # if profit:
        #     print("Current Profit: ", profit, " ", float(f"{profit_percent:.8f}"), "%")
        # if percent_lower and not open_trade:
        #     print("Percent Lower: ", float(f"{percent_lower:.8f}"), "%")

        # Should we Sell?
        if profit_percent:
            if profit_percent > 1:
                dataframe['should_sell'] = True

        # Should we buy?
        if not open_trade:
            if (lsp == 0.00) & (lbp == 0.00):
                dataframe['should_buy'] = True
            # Is the percentage of what we sold for and the current price 2% lower
            if percent_lower > 2:
                dataframe['should_buy'] = True

        dataframe['last_sell_price'] = lsp
        dataframe['last_buy_price'] = lbp
        dataframe['current_price'] = current_price
        dataframe['profit_percent'] = profit_percent
        # print("Current Dataframe:")
        # print(dataframe.tail(1))

        return dataframe

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

        conditions = list()
        conditions2 = list()
        condition1, dataframe = condition_generator(
            dataframe,
            buy_operator0,
            buy_indicator0,
            buy_crossed_indicator0,
            0.67 #self.buy_real_num0.value
        )
        conditions.append(condition1)

        dataframe.loc[
            (
               reduce(lambda x, y: x & y, conditions)
               # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
               & (dataframe['close'] < dataframe['sma10'])
               & (dataframe['open'] < dataframe['sma10'])
               & (dataframe['low'] < dataframe['min20'])
               & (dataframe['bb_width'] > 0.05)
            ), ['buy', 'buy_tag']] = (1, 'buy_signal_condition')

        ####################################################################

        condition2, dataframe = condition_generator(dataframe, "/=R", "CMO-10", "HT_DCPERIOD-20", 0.37)
        conditions2.append(condition2)

        dataframe.loc[
            (reduce(lambda x, y: x & y, conditions2)
             # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
             & (dataframe['close'].shift(9) < dataframe['bb_lowerband'].shift(9))
             ), ['buy', 'buy_tag']] = (1, 'buy_bb_width_1')

        ####################################################################
        conditions = list()

        condition, dataframe = condition_generator(dataframe, "/<R", "HT_PHASOR-0-100", "CDLIDENTICAL3CROWS-100", 0.91)
        conditions.append(condition)

        condition, dataframe = condition_generator(dataframe, "<R", "DEMA-10", "CDLMORNINGDOJISTAR-20", 0.55)
        conditions.append(condition)

        if conditions:
            dataframe.loc[
                (reduce(lambda x, y: x & y, conditions)
                 # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
                 & (dataframe['sma10_pente'] > 0.02)
                 & (dataframe['sma10'].shift(1) < dataframe['sma10'])
                 & (dataframe['sma20'].shift(1) < dataframe['sma20'])
                 & (dataframe['sma50'].shift(1) < dataframe['sma50'])
                 & (dataframe['close'] < dataframe['bb_upperband'])
                 & (dataframe['open'] < dataframe['sma10'])
                 & (dataframe['percent50'] < 0.025)
                 ),
                ['buy', 'buy_tag']] = (1, 'buy_sma')
        # print(len(dataframe.keys()))


        ####################################################################
        # conditions3 = list()
        #
        # condition3, dataframe = condition_generator(dataframe, "D", "MACD-2-10", "CDLIDENTICAL3CROWS-10", 0.88)
        # conditions3.append(condition3)
        #
        # condition3, dataframe = condition_generator(dataframe, "/<R", "PPO-10", "MINUS_DM-10", 0.27)
        # conditions3.append(condition3)
        #
        # condition3, dataframe = condition_generator(dataframe, ">", "MOM-10", "MACDEXT-0-100", 0.87)
        # #conditions3.append(condition3)
        #
        # if conditions3:
        #     dataframe.loc[
        #         (reduce(lambda x, y: x & y, conditions3)
        #          # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
        #          & (dataframe['close'].shift(7) < dataframe['bb_lowerband'].shift(7))
        #          ),
        #         ['buy', 'buy_tag']] = (1, 'buy_minus_dm')
        # print(len(dataframe.keys()))

        # ###################################################################
        #
        # dataframe.loc[
        #      (dataframe['close'] > dataframe['bb_upperband'])
        #      & (dataframe['percent'] > 0.01)
        #      & (dataframe['percent20'] < 0.035)
        #      & (dataframe['cond1'] > 13)
        #      & (dataframe['bb_width'] > 0.02)
        #      & (dataframe['cond1'].shift(4) < 8)
        #      & (dataframe['bb_width'].shift(4) < 0.012)
        #      , ['buy', 'buy_tag']] = (1, 'buy_cond1')
        #
        # ####################################################################
        #
        # dataframe.loc[
        #      (dataframe['bb_lower_pente'].shift(4) < -0.50)
        #      & (dataframe['sma10_pente'] > -0.2)
        #      & (dataframe['sma10_pente'].shift(4) < -0.4)
        #      & (dataframe['percent5'] < 0.01)
        #      , ['buy', 'buy_tag']] = (1, 'buy_lower_pente')
        #
        # ####################################################################
        #
        # dataframe.loc[
        #      (dataframe['percent50'].shift(4) < -0.05)
        #      & (dataframe['sma10_pente'].shift(1) < 0)
        #      & (dataframe['sma10_pente'] >= 0)
        #      & (dataframe['open'] < dataframe['sma10'])
        #      , ['buy', 'buy_tag']] = (1, 'buy_sma50')
        #
        # pandas.set_option('display.max_rows', dataframe.shape[0] + 1)
        # pandas.set_option('display.max_columns', 30)
        # print(condition1)
        # print(dataframe["q_0.1"])

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # conditions = list()
        #
        # condition1, dataframe = condition_generator(
        #     dataframe,
        #     self.sell_operator0.value,
        #     self.sell_indicator0.value,
        #     self.sell_crossed_indicator0.value,
        #     self.sell_real_num0.value
        # )
        # conditions.append(condition1)
        #
        # # print(self.sell_percent.value, ' ',
        # #       self.sell_percent.value + self.sell_percent3.value, ' ',
        # #       self.sell_percent.value + self.sell_percent3.value + self.sell_percent5.value)
        # dataframe.loc[
        #     (
        #         (reduce(lambda x, y: x & y, conditions)) &
        #         (dataframe['percent'] < - self.sell_percent.value) &
        #         (dataframe['percent3'] < - (self.sell_percent3.value + self.sell_percent.value)) &
        #         (dataframe['percent5'] < - (self.sell_percent5.value + self.sell_percent3.value + self.sell_percent.value)) &
        #         (dataframe['close'] < dataframe['open'])
        #         # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02)
        #         # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025)
        #     ), 'sell'] = 1
        return dataframe

