# 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

from numpy.lib import math
from freqtrade.strategy.interface import IStrategy
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]
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 = 1
########################### 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:
        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


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_3(IStrategy):
    # #################### RESULTS PASTE PLACE ####################
    # ROI table:
    minimal_roi = {
        "0": 1,
        # "600": 0.12,
        # "1200": 0.08,
        # "2400": 0.06,
        # "3600": 0.04,
        # "7289": 0
    }

    # Stoploss:
    stoploss = -1
    # Buy hypers
    timeframe = '4h'

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


    plot_config = {
        # Main plot indicators (Moving averages, ...)
        'main_plot': {
            'bb_lowerband': {'color': 'red'},
            'bb_upperband': {'color': 'green'},
            'sma100': {'color': 'blue'},
            'sma10': {'color': 'yellow'},
            'min': {'color': 'white'},
            'max': {'color': 'white'},
            'sma20': {'color': 'cyan'}
        },
        'subplots': {
            # Subplots - each dict defines one additional plot
            "BB": {
                'bb_width': {'color': 'white'},
                'bb_min': {'color': 'red'},
            },
            "Ind": {
                'CDLTASUKIGAP-5': {'color': 'white'},
                'CDLSPINNINGTOP-6': {'color': 'blue'},
                'CDL3WHITESOLDIERS-110': {'color': 'red'}
            },
            "Ind2": {
                'CDLSHOOTINGSTAR-110': {'color': 'white'},
                'MACDFIX-2-12': {'color': 'blue'},
                'DEMA-110': {'color': 'red'}
            },

            "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_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy')
    buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy')
    buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy')

    buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy')
    buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy')
    buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy')

    buy_operator0 = CategoricalParameter(operators, default="/<R", space='buy')
    buy_operator1 = CategoricalParameter(operators, default="<R", space='buy')
    buy_operator2 = CategoricalParameter(operators, default="CB", space='buy')

    buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS,  default=0.89009, space='buy')
    buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.56953, space='buy')
    buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.38365, space='buy')

    # 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')

    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 10
            },
            {
                "method": "MaxDrawdown",
                "lookback_period_candles": 48,
                "trade_limit": 2,
                "stop_duration_candles": 10,
                "max_allowed_drawdown": 0.04,
                "only_per_pair": False
            },
            # {
            #     "method": "StoplossGuard",
            #     "lookback_period_candles": 24,
            #     "trade_limit": 4,
            #     "stop_duration_candles": 2,
            #     "only_per_pair": False
            # },
            # {
            #     "method": "LowProfitPairs",
            #     "lookback_period_candles": 6,
            #     "trade_limit": 2,
            #     "stop_duration_candles": 60,
            #     "required_profit": 0.02
            # },
            # {
            #     "method": "LowProfitPairs",
            #     "lookback_period_candles": 24,
            #     "trade_limit": 4,
            #     "stop_duration_candles": 2,
            #     "required_profit": 0.01
            # }
        ]

    # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
    #                         proposed_stake: float, min_stake: float, max_stake: float,
    #                         **kwargs) -> float:
    #
    #     dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
    #     current_candle = dataframe.iloc[-1].squeeze()
    #
    #     # print("proposed_stake=", proposed_stake, " max_stake=", max_stake)
    #     if current_candle['bb_width'] > 0.065:
    #         # print("use more stake", pair, " ", proposed_stake * 2)
    #         return min(max_stake, proposed_stake * 2)
    #
    #     if current_candle['bb_width'] > 0.045:
    #         # print("use more stake", pair, " ", proposed_stake * 1.5)
    #         return min(max_stake, proposed_stake * 1.5)
    #
    #     # if current_candle['bb_width'] < 0.020:
    #     #     print("use less stake", pair, " ", proposed_stake / 2)
    #     #     return min(max_stake, proposed_stake / 2)
    #     # if self.config['stake_amount'] == 'unlimited':
    #     #     # Use entire available wallet during favorable conditions when in compounding mode.
    #     #     return max_stake
    #     # else:
    #     #     # Compound profits during favorable conditions instead of using a static stake.
    #     #     return self.wallets.get_total_stake_amount() / self.config['max_open_trades']
    #
    #     # Use default stake amount.
    #     return proposed_stake

    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()

        count = 0
        for coin, balance in self.wallets.get_all_balances().items():
            count = count + 1
            # print(coin, " ", balance)

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

        # if (current_profit < -0.05) \
        #         & ((current_time - trade.open_date_utc).days >= 3):
        #     print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #     return 'stop_loss_profit'

        # if (current_profit > 0.02) \
        #         & (last_candle['percent'] < 0.01) \
        #         & ((current_time - trade.open_date_utc).seconds >= 3600):
        #     print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #     return 'lost_half_profit'

        # ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \
        if (current_profit > 0) \
                & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.01)) \
                & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): #(current_profit / 4))):
            # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
            return 'over_bb_band_sma10_desc'

        #if (current_profit > 0) \
        #        & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \
        #        & (previous_last_candle['sma20'] > last_candle['sma20']) \
        #        & (last_candle['percent'] < 0):
        #    print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #    return 'over_bb_band_sma20_desc'

        if (current_profit > 0) & (last_candle['rsi'] > 88):
            print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
            return 'over_rsi'

        #if 0.015 < current_profit < 0.03:
        #    if (last_candle['percent3'] < -0.005 ):
        #        # self.lock_pair(pair, until=current_time + timedelta(hours=3))
        #        print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #        return 'profit_percent3'
        # if (current_profit > 0) \
        #         & (last_candle['percent'] < -0.02):
        #     # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #     return 'stop_percent_loss'

        # description trade
        # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00)
        # print(last_candle)
        # if 0.05 < current_profit < 1:
        #     if (
        #             (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) &
        #             (current_time - trade.open_date_utc).seconds >= 3600 * 3
        #     # ) | (
        #     #         (current_time - trade.open_date_utc).seconds >= 3600 * 6
        #     ):
        #         # self.lock_pair(pair, until=current_time + timedelta(hours=3))
        #
        #         print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #         return 'profit_3h_sma10_desc'
        #
        # if (0 < current_profit < 0.1) \
        #         & (previous_last_candle['sma20'] > last_candle['sma20']) \
        #         & ((current_time - trade.open_date_utc).seconds >= 3600 * 5):
        #     print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
        #     return 'profit_5h_sma20_desc'

        # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \
        #         & ((current_time - trade.open_date_utc).seconds >= 3600 * 6):
        #     self.lock_pair(pair, until=current_time + timedelta(hours=10))
        #     print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate,
        #           "count=", count, "max=", self.config['max_open_trades'])
        #     return 'stop_short_loss'

    def informative_pairs(self):
        return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # MACD
        # macd = ta.MACD(dataframe)
        # dataframe['macd'] = macd['macd']
        # dataframe['macdsignal'] = macd['macdsignal']
        # dataframe['macdhist'] = macd['macdhist']

        # # # Plus Directional Indicator / Movement
        # dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
        # dataframe['plus_di'] = ta.PLUS_DI(dataframe)
        #
        # # Minus Directional Indicator / Movement
        # dataframe['adx'] = ta.ADX(dataframe)
        # dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
        # dataframe['minus_di'] = ta.MINUS_DI(dataframe)

        # dataframe['min'] = ta.MIN(dataframe)
        # dataframe['max'] = ta.MAX(dataframe)

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

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

        # # EMA - Exponential Moving Average
        # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
        # dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
        # dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
        # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
        # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)

        dataframe['ADD-20'] = gene_calculator(dataframe, 'ADD-20')
        dataframe['ASIN-6'] = gene_calculator(dataframe, 'ASIN-6')
        dataframe['CDLEVENINGSTAR-50'] = gene_calculator(dataframe, 'CDLEVENINGSTAR-50')

        dataframe['SMA-100'] = gene_calculator(dataframe, 'SMA-100')
        dataframe['WILLR-50'] = gene_calculator(dataframe, 'WILLR-50')
        dataframe['CDLHANGINGMAN-20'] = gene_calculator(dataframe, 'CDLHANGINGMAN-20')


        # # SMA - Simple Moving Average
        # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3)
        # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5)
        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['sma200'] = ta.SMA(dataframe, timeperiod=200)
        # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95
        # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98
        # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500)
        # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9
        # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95
        # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2
        dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"]
        dataframe["percent5"] = dataframe["percent"].rolling(5).sum()
        dataframe["percent3"] = dataframe["percent"].rolling(3).sum()
        dataframe["percent20"] = dataframe["percent"].rolling(20).sum()
        dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200)
        dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20)

        dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200)
        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_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"]
        )

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

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

        conditions = list()

        # TODO: Its not dry code!
        buy_indicator = self.buy_indicator0.value
        buy_crossed_indicator = self.buy_crossed_indicator0.value
        buy_operator = self.buy_operator0.value
        buy_real_num = self.buy_real_num0.value
        condition, dataframe = condition_generator(
            dataframe,
            buy_operator,
            buy_indicator,
            buy_crossed_indicator,
            buy_real_num
        )
        conditions.append(condition)
        # backup
        buy_indicator = self.buy_indicator1.value
        buy_crossed_indicator = self.buy_crossed_indicator1.value
        buy_operator = self.buy_operator1.value
        buy_real_num = self.buy_real_num1.value

        condition, dataframe = condition_generator(
            dataframe,
            buy_operator,
            buy_indicator,
            buy_crossed_indicator,
            buy_real_num
        )
        conditions.append(condition)

        buy_indicator = self.buy_indicator2.value
        buy_crossed_indicator = self.buy_crossed_indicator2.value
        buy_operator = self.buy_operator2.value
        buy_real_num = self.buy_real_num2.value
        condition, dataframe = condition_generator(
            dataframe,
            buy_operator,
            buy_indicator,
            buy_crossed_indicator,
            buy_real_num
        )
        conditions.append(condition)

        if conditions:
            dataframe.loc[
                (
                    (dataframe['close'] < dataframe['bb_lowerband'])
                    & (dataframe['bb_width'] >= 0.12)
                    & (dataframe['volume'] * dataframe['close'] / 1000 > 100)
                ) |
                (reduce(lambda x, y: x & y, conditions)),
                'buy']=1
        # print(len(dataframe.keys()))

        return dataframe

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

        conditions = list()
        # TODO: Its not dry code!
        sell_indicator = self.sell_indicator0.value
        sell_crossed_indicator = self.sell_crossed_indicator0.value
        sell_operator = self.sell_operator0.value
        sell_real_num = self.sell_real_num0.value
        condition, dataframe = condition_generator(
            dataframe,
            sell_operator,
            sell_indicator,
            sell_crossed_indicator,
            sell_real_num
        )
        conditions.append(condition)

        sell_indicator = self.sell_indicator1.value
        sell_crossed_indicator = self.sell_crossed_indicator1.value
        sell_operator = self.sell_operator1.value
        sell_real_num = self.sell_real_num1.value
        condition, dataframe = condition_generator(
            dataframe,
            sell_operator,
            sell_indicator,
            sell_crossed_indicator,
            sell_real_num
        )
        conditions.append(condition)

        sell_indicator = self.sell_indicator2.value
        sell_crossed_indicator = self.sell_crossed_indicator2.value
        sell_operator = self.sell_operator2.value
        sell_real_num = self.sell_real_num2.value
        condition, dataframe = condition_generator(
            dataframe,
            sell_operator,
            sell_indicator,
            sell_crossed_indicator,
            sell_real_num
        )
        conditions.append(condition)

        #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1

        # count = 0
        # for coin, balance in self.wallets.get_all_balances().items():
        #     count = count + 1
        #     # print(coin, " ", balance)
        # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \
        #         & ((current_time - trade.open_date_utc).seconds >= 3600 * 6):
        #     self.lock_pair(pair, until=current_time + timedelta(hours=10))
        #     print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate,
        #           "count=", count, "max=", self.config['max_open_trades'])
        #     return 'stop_short_loss'

        # print("sell pair=", self.wallets.get_all_balances()[metadata['pair']])

        if conditions:
            dataframe.loc[
                (
                        reduce(lambda x, y: x & y, conditions) &
                        (dataframe['volume'] * dataframe['close'] / 1000 >= 100)
                        # (dataframe['open'] < dataframe['sma10'])
                ),
            'sell']=1
        return dataframe


class StrategyHelperLocal:
    """
        simple helper class to predefine a couple of patterns for our
        strategy
    """

    @staticmethod
    def two_green_candles(dataframe):
        """
            evaluates if we are having 7 green candles in a row
        :param self:
        :param dataframe:
        :return:
        """
        return (
                (dataframe['open'] < dataframe['close']) &
                (dataframe['open'].shift(1) < dataframe['close'].shift(1))
                # (dataframe['open'].shift(2) < dataframe['close'].shift(2))
        )

    @staticmethod
    def no_more_three_green_candles(dataframe):
        """
            evaluates if we are having not more than 3 green candles in a row
        :param self:
        :param dataframe:
        :return:
        """
        return not (
                (dataframe['open'] < dataframe['close']) &
                (dataframe['open'].shift(1) < dataframe['close'].shift(1)) &
                (dataframe['open'].shift(2) < dataframe['close'].shift(2)) &
                (dataframe['open'].shift(3) < dataframe['close'].shift(3))
        )

    @staticmethod
    def seven_green_candles(dataframe):
        """
            evaluates if we are having 7 green candles in a row
        :param self:
        :param dataframe:
        :return:
        """
        return (
                (dataframe['open'] < dataframe['close']) &
                (dataframe['open'].shift(1) < dataframe['close'].shift(1)) &
                (dataframe['open'].shift(2) < dataframe['close'].shift(2)) &
                (dataframe['open'].shift(3) < dataframe['close'].shift(3)) &
                (dataframe['open'].shift(4) < dataframe['close'].shift(4)) &
                (dataframe['open'].shift(5) < dataframe['close'].shift(5)) &
                (dataframe['open'].shift(6) < dataframe['close'].shift(6)) &
                (dataframe['open'].shift(7) < dataframe['close'].shift(7))
        )

    @staticmethod
    def eight_green_candles(dataframe):
        """
            evaluates if we are having 8 green candles in a row
        :param self:
        :param dataframe:
        :return:
        """
        return (
                (dataframe['open'] < dataframe['close']) &
                (dataframe['open'].shift(1) < dataframe['close'].shift(1)) &
                (dataframe['open'].shift(2) < dataframe['close'].shift(2)) &
                (dataframe['open'].shift(3) < dataframe['close'].shift(3)) &
                (dataframe['open'].shift(4) < dataframe['close'].shift(4)) &
                (dataframe['open'].shift(5) < dataframe['close'].shift(5)) &
                (dataframe['open'].shift(6) < dataframe['close'].shift(6)) &
                (dataframe['open'].shift(7) < dataframe['close'].shift(7)) &
                (dataframe['open'].shift(8) < dataframe['close'].shift(8))
        )

    @staticmethod
    def two_red_candles(dataframe, shift=0):
        """
            evaluates if we are having 8 red candles in a row
        :param self:
        :param dataframe:
        :param shift: shift the pattern by n
        :return:
        """
        return (
                (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) &
                (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift))
                # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) &
                # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) &
                # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) &
                # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) &
                # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift))
        )

    @staticmethod
    def four_red_candles(dataframe, shift=0):
        """
            evaluates if we are having 8 red candles in a row
        :param self:
        :param dataframe:
        :param shift: shift the pattern by n
        :return:
        """
        return (
                (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) &
                (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) &
                (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) &
                (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) &
                (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift))
                # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) &
                # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) &
                # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) &
                # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift))
        )

    @staticmethod
    def two_green_one_red_candle(dataframe):
        """
            evaluates if we are having a red candle and 4 previous green
        :param self:
        :param dataframe:
        :return:
        """
        return (
                (dataframe['open'] < dataframe['close']) &
                (dataframe['open'].shift(1) < dataframe['close'].shift(1)) &
                (dataframe['open'].shift(2) > dataframe['close'].shift(2))
        )

    @staticmethod
    def four_green_one_red_candle(dataframe):
        """
            evaluates if we are having a red candle and 4 previous green
        :param self:
        :param dataframe:
        :return:
        """
        return (
                (dataframe['open'] > dataframe['close']) &
                (dataframe['open'].shift(1) < dataframe['close'].shift(1)) &
                (dataframe['open'].shift(2) < dataframe['close'].shift(2)) &
                (dataframe['open'].shift(3) < dataframe['close'].shift(3)) &
                (dataframe['open'].shift(4) < dataframe['close'].shift(4))
        )

    @staticmethod
    def four_red_one_green_candle(dataframe):
        """
            evaluates if we are having a green candle and 4 previous red
        :param self:
        :param dataframe:
        :return:
        """
        return (
                (dataframe['open'] < dataframe['close']) &
                (dataframe['open'].shift(1) > dataframe['close'].shift(1)) &
                (dataframe['open'].shift(2) > dataframe['close'].shift(2)) &
                (dataframe['open'].shift(3) > dataframe['close'].shift(3)) &
                (dataframe['open'].shift(4) > dataframe['close'].shift(4))
        )
