# Zeus Strategy: First Generation of GodStra Strategy with maximum
# AVG/MID profit in USDT
# Author: @Mablue (Masoud Azizi)
# github: https://github.com/mablue/
# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta)
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus
# --- Do not remove these libs ---
from datetime import timedelta, datetime
from typing import Optional

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

from numpy.lib import math
from freqtrade.strategy.interface import IStrategy
import pandas
from pandas import DataFrame
import time
import logging
import calendar
from freqtrade.loggers import setup_logging
from freqtrade.strategy.strategy_helper import merge_informative_pair

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

# Add your lib to import here
import ta
from functools import reduce
import numpy as np
import talib.abstract as talib
from freqtrade.strategy.strategy_helper import merge_informative_pair
import freqtrade.vendor.qtpylib.indicators as qtpylib

from random import shuffle

logger = logging.getLogger(__name__)

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

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'


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(talib, gene_name)(
                dataframe
            )
            return normalize(result)
        elif gene_len == 2:
            # print('gene_len == 2\t', indicator)
            gene_timeperiod = int(gene[1])
            result = getattr(talib, 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(talib, 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(talib, gene_name)(
                dataframe,
                timeperiod=gene_timeperiod,
            )
            return normalize(talib.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(talib, gene_name)(
                dataframe,
                timeperiod=gene_timeperiod,
            ).iloc[:, gene_index]
            return normalize(talib.SMA(dataframe[sharp_indicator].fillna(0), TREND_CHECK_CANDLES))


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

    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].shift(decalage) > dataframe[crossed_indicator].shift(decalage))
    elif operator == "=":
        condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage)))
    elif operator == "<":
        condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage))
    elif operator == "C":
        condition = (
            (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) |
            (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage)))
        )
    elif operator == "CA":
        condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage)))
    elif operator == "CB":
        condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage)))
    elif operator == ">R":
        condition = (dataframe[indicator].shift(decalage) > real_num)
    elif operator == "=R":
        condition = (np.isclose(dataframe[indicator].shift(decalage), real_num))
    elif operator == "<R":
        condition = (dataframe[indicator].shift(decalage) < real_num)
    elif operator == "/>R":
        condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num)
    elif operator == "/=R":
        condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num))
    elif operator == "/<R":
        condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) < real_num)
    elif operator == "UT":
        condition = (dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage))
    elif operator == "DT":
        condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage))
    elif operator == "OT":
        condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)))
    elif operator == "CUT":
        condition = (
            (
                qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
            ) &
            (
                dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage)
            )
        )
    elif operator == "CDT":
        condition = (
            (
                qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
            ) &
            (
                dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)
            )
        )
    elif operator == "COT":
        condition = (
            (
                (
                    qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
                ) |
                (
                    qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
                )
            ) &
            (
                np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))
            )
        )
    return condition, dataframe

class Zeus_6(IStrategy):

    # *    1/43:     86 trades. 72/6/8 Wins/Draws/Losses. Avg profit  12.66%. Median profit  11.99%. Total profit  0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793
    # "max_open_trades": 10,
    # "stake_currency": "BTC",
    # "stake_amount": 0.01,
    # "tradable_balance_ratio": 0.99,
    # "timeframe": "4h",
    # "dry_run_wallet": 0.1,

    # Buy hyperspace params:
    buy_b_params = {
        "buy_b_cat": "<R",
        "buy_b_real": 0.0128,
    }

    # Sell hyperspace params:
    sell_b_params = {
        "sell_b_cat": "=R",
        "sell_b_real": 0.9455,
    }

    # Buy hyperspace params:
    buy_h_params = {
        "buy_h_cat": "<R",
        "buy_h_real": 0.0128,
    }

    # Sell hyperspace params:
    sell_h_params = {
        "sell_h_cat": "=R",
        "sell_h_real": 0.9455,
    }

    # ROI table:
    minimal_roi = {
        "0": 0.564,
        "567": 0.273,
        "2814": 0.12,
        "7675": 0
    }

    # Stoploss:
    stoploss = -0.256

    # sell_h_real = DecimalParameter(0.001, 0.999, decimals=4, default=0.59608, space='sell')
    # sell_h_cat = CategoricalParameter([">R", "=R", "<R"], default='>R', space='sell')

    # Buy hypers
    timeframe = '4h'

    market_overview = {'up': 0, 'down': 0}
    market_overview_pct5 = 0
    market_overview_pct1 = 0
    max_open_trades = 5
    max_amount = 40
    stop_buying = {}
    stop_buy_for_all = False

    plot_config = {
        "main_plot": {
            "min200": {
                "color": "#86c932"
            },
            "max50": {
                "color": "white"
            },
            "max200": {
                "color": "yellow"
            },
            "sma3_1d": {
                "color": "pink"
            },
            "sma5_1d": {
                "color": "blue"
            },
            "sma10_1d": {
                "color": "orange"
            },
            "close_1d": {
                "color": "#73e233",
            },
            "bb_lowerband": {
                "color": "#da59a6"},
            "bb_upperband": {
                "color": "#da59a6",
            },
            "sar": {
                "color": "#4f9f51",
            }
        },
        "subplots": {
            "Ind": {
                "trend_ichimoku_base": {
                    "color": "#dd1384"
                },
                "trend_kst_diff": {
                    "color": "#850678"
                }
            },
            "BB": {
                "bb_width": {
                    "color": "white"
                },
                "bb_lower_5": {
                    "color": "yellow"
                }
            },
            "Cond": {
                "cond1": {
                    "color": "yellow"
                }
            },
            "Rsi": {
                "rsi": {
                    "color": "pink"
                },
                "rsi_1d": {
                    "color": "yellow"
                }
            },
            "Percent": {
                "max_min": {
                    "color": "#74effc"
                },
                "pct_change_1_1d": {
                    "color": "green"
                },
                "pct_change_3_1d": {
                    "color": "orange"
                },
                "pct_change_5_1d": {
                    "color": "red"
                }
            }
        }
    }
    trades = list()

    buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.10, space='buy')
    buy_diff = DecimalParameter(0, 0.2, decimals=2, default=0.20, space='buy')

    buy_rsi = IntParameter(20, 80, default=42, space='buy')
    buy_rsi_max = IntParameter(60, 90, default=60, space='buy')

    buy_rsi_1h = IntParameter(20, 80, default=72, space='buy')
    buy_rsi_max_1h = IntParameter(60, 80, default=72, space='buy')

    buy_min_horizon = IntParameter(50, 200, default=72, space='buy')

    buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2,  default=-0.02, space='buy')
    buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2,  default=-0.02, space='buy')
    buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2,  default=-0.02, space='buy')

    buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy')
    buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy')
    buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy')

    buy_decalage_deb_0 = IntParameter(0, 3, default=5, space='buy')
    buy_decalage_deb_2 = IntParameter(0, 3, default=5, space='buy')
    buy_decalage_deb_3 = IntParameter(0, 3, default=5, space='buy')

    buy_real_num0 = DecimalParameter(0, 1, decimals=2,  default=0.67, space='buy')
    buy_real_num1 = DecimalParameter(0, 1, decimals=2,  default=0.67, space='buy')
    buy_real_num2 = DecimalParameter(0, 2, decimals=2,  default=0.67, space='buy')

    buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy')
    buy_decalage2 = IntParameter(buy_decalage_deb_2.value + 1, 8, default=5, space='buy')
    buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy')

    buy_1_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2,  default=0.7, space='buy')
    buy_2_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2,  default=0.7, space='buy')
    buy_3_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2,  default=0.7, space='buy')

    buy_b_real = DecimalParameter(0.001, 0.999, decimals=4, default=0.11908, space='buy')
    buy_b_cat = CategoricalParameter([">R", "=R", "<R"], default='<R', space='buy')
    buy_b_pct = DecimalParameter(0.001, 0.02, decimals=3, default=0.005, space='buy')
    buy_b_pct_1 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
    buy_b_pct_3 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
    buy_b_pct_5 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
    buy_b_bb_lowerband = DecimalParameter(1, 1.05, default=1, decimals=2, space='buy')
    buy_b_bb_width = DecimalParameter(0.01, 0.15, default=0.065, decimals=2, space='buy')

    decalage_h = IntParameter(0, 3, default=0, space='buy')
    decalage_b = IntParameter(0, 3, default=0, space='buy')

    buy_h_real = DecimalParameter(0.001, 0.999, decimals=4, default=0.11908, space='buy')
    buy_h_cat = CategoricalParameter([">R", "=R", "<R"], default='<R', space='buy')
    buy_h_pct = DecimalParameter(0.001, 0.02, decimals=3, default=0.005, space='buy')
    buy_h_pct_1 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
    buy_h_pct_3 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
    buy_h_pct_5 = DecimalParameter(-0.2, 0.2, decimals=2, default=0.005, space='buy')
    buy_h_bb_lowerband = DecimalParameter(1, 1.05, default=1, decimals=2, space='buy')
    buy_h_bb_width = DecimalParameter(0.01, 0.15, default=0.065, decimals=2, space='buy')

    buy_h_sma = CategoricalParameter(["sma3_1d", "sma5_1d", "sma10_1d"], default='sma10_1d', space='buy')
    buy_b_sma = CategoricalParameter(["sma3_1d", "sma5_1d", "sma10_1d"], default='sma10_1d', space='buy')
    buy_h_sma_close = CategoricalParameter(["sma3_1d", "sma5_1d", "sma10_1d"], default='sma3_1d', space='buy')
    buy_b_sma_close = CategoricalParameter(["sma3_1d", "sma5_1d", "sma10_1d"], default='sma3_1d', space='buy')

    profit_b_no_change = BooleanParameter(default=True, space="sell")
    profit_b_quick_lost = BooleanParameter(default=True, space="sell")
    profit_b_sma5 = BooleanParameter(default=True, space="sell")
    profit_b_sma10 = BooleanParameter(default=True, space="sell")
    profit_b_sma20 = BooleanParameter(default=True, space="sell")
    profit_b_quick_gain = BooleanParameter(default=True, space="sell")
    profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell")
    profit_b_old_sma10 = BooleanParameter(default=True, space="sell")
    profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell")
    profit_b_over_rsi = BooleanParameter(default=True, space="sell")
    profit_b_short_loss = BooleanParameter(default=True, space="sell")

    sell_b_percent = DecimalParameter(0, 0.02, decimals=3,  default=0.01, space='sell')
    sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3,  default=0.01, space='sell')
    sell_b_candels = IntParameter(0, 48, default=12, space='sell')

    sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell')
    sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3,  default=0.01, space='sell')

    sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3,  default=0.005, space='sell')
    sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4,  default=0.001, space='sell')

    sell_b_RSI = IntParameter(70, 98, default=88, space='sell')
    sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell')
    sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell')

    sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3,  default=0.01, space='sell')
    # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3,  default=0.01, space='sell')

    profit_h_no_change = BooleanParameter(default=True, space="sell")
    profit_h_quick_lost = BooleanParameter(default=True, space="sell")
    profit_h_sma5 = BooleanParameter(default=True, space="sell")
    profit_h_sma10 = BooleanParameter(default=True, space="sell")
    profit_h_sma20 = BooleanParameter(default=True, space="sell")
    profit_h_quick_gain = BooleanParameter(default=True, space="sell")
    profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell")
    profit_h_old_sma10 = BooleanParameter(default=True, space="sell")
    profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell")
    profit_h_over_rsi = BooleanParameter(default=True, space="sell")
    profit_h_short_loss = BooleanParameter(default=True, space="sell")

    sell_h_percent = DecimalParameter(0, 0.02, decimals=3,  default=0.01, space='sell')
    sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3,  default=0.01, space='sell')
    sell_h_candels = IntParameter(0, 48, default=12, space='sell')

    sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell')
    sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3,  default=0.01, space='sell')

    sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3,  default=0.005, space='sell')
    sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4,  default=0.001, space='sell')

    sell_h_RSI = IntParameter(70, 98, default=88, space='sell')
    sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell')
    sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell')

    sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3,  default=0.01, space='sell')
    # sell_h_expected_profit = DecimalParameter(0, 0.01, decimals=3,  default=0.01, space='sell')

    # 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()
    #
    #     if current_candle['close'] < current_candle['sma10_1d']:
    #          print("use more stake", pair, " ", proposed_stake * 2)
    #          return min(max_stake, proposed_stake * 2)
    #     else:
    #         if current_candle['close'] < current_candle['sma5_1d']:
    #             print("use more stake", pair, " ", proposed_stake * 1.5)
    #             return min(max_stake, proposed_stake * 1.5)
    #
    #     # Use default stake amount.
    #     return proposed_stake

    # def bot_loop_start(self, **kwargs) -> None:
    #     inf_tf = '5m'
    #     pairs = self.dp.current_whitelist()
    #     market_overview = {'up': 0, 'down': 0}
    #     sum_pct5 = 0
    #     sum_pct1 = 0
    #     self.max_open_trades = self.config['max_open_trades']
    #     self.max_amount = self.config['stake_amount']
    #     if self.dp:
    #         if self.dp.runmode.value in ('live', 'dry_run'):
    #             for pairname in pairs:
    #                 informative = self.dp.get_pair_dataframe(pair=pairname, timeframe=inf_tf)
    #                 informative['pct'] = informative['close'].pct_change(1)
    #                 informative['pct5'] = informative['close'].pct_change(5)
    #                 # print(informative['pct5'])
    #                 pct5 = (informative.tail(1).iloc[0]['pct5']) * 100
    #                 pct1 = (informative.tail(1).iloc[0]['pct']) * 100
    #                 if pairname == "COCOS/USDT":
    #                     print(pairname, round(pct1, 5), round(pct5, 5)) #, informative.tail(1).iloc[0])
    #
    #                 if pairname == "BTC/USDT":
    #                     current = informative.tail(1).iloc[0]['close']
    #                     # 50000 => 2 30000 => 20
    #                     if (current > 50000):
    #                         self.max_open_trades = 2
    #                         self.max_amount = self.config['stake_amount'] / 2
    #                     else:
    #                         if (current > 32000):
    #                             self.max_open_trades = 2 + int((50000 - current) / 1000)
    #                             self.max_amount = self.config['stake_amount'] / 2 \
    #                                             + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades']
    #                         else:
    #                             self.max_open_trades = self.config['max_open_trades']
    #                             self.max_amount = self.config['stake_amount']
    #
    #                     logger.info("pair '%s' prix %s  max open trade %s max amount %s.",
    #                                 pairname, current, self.max_open_trades, self.max_amount)
    #
    #                 if pct5 > 0:
    #                     market_overview['up'] += 1
    #                 elif pct5 < 0:
    #                     market_overview['down'] += 1
    #                 sum_pct5 += pct5
    #                 sum_pct1 += pct1
    #
    #             self.market_overview_pct1 = sum_pct1 / len(pairs)
    #             self.market_overview_pct5 = sum_pct5 / len(pairs)
    #
    #             logger.info("market_overview_pct1 %s %s %s", round(self.market_overview_pct1,5), round(self.market_overview_pct5, 5), market_overview)
    #             self.market_overview = market_overview

    # def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str,
    #                         current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool:
    #
    #     # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489,
    #     #  'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198,
    #     #  'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808,
    #     #  'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799,
    #     #  'info':
    #     #  {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667',
    #     #       'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000',
    #     #       'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000',
    #     #       'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000',
    #     #       'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000',
    #     #       'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570',
    #     #       'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484
    #
    #     allow_to_buy = True
    #     if self.dp:
    #         if self.dp.runmode.value in ('live', 'dry_run'):
    #             if len(Trade.get_open_trades()) >= self.max_open_trades:
    #                 logger.info("too much open trades for BTC current value")
    #                 allow_to_buy = False
    #
    #     return allow_to_buy

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

        expected_profit = 0.01
        #print(last_candle['buy_tag'])

        days = (current_time - trade.open_date_utc).days
        ######

        # if self.dp:
        #     if self.dp.runmode.value in ('live', 'dry_run'):
                # if (current_profit > - 0.04) & (self.market_overview_pct1 < - 1) & (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) < 0.05):
                #     return 'send_all'

                # if (current_profit > 0) & (last_candle['percent'] < 0) \
                #         (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) < 0.25):
                #     return 'all_down'

        if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0):
            if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)):
                return 'b_percent_quick'

            if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\
                    & (days < self.sell_b_too_old_day.value * 2)\
                    & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
                return "b_too_old_0.01"
            if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\
                & (days < self.sell_b_too_old_day.value * 3) \
                    & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
                return "b_too_old_0.02"
            if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \
                    & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
                return "b_too_old_0.03"

            if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005):
                return "b_quick_lost"

            if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \
                    & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \
                    & ((current_time - trade.open_date_utc).seconds >= 3600):
                return "b_no_change"

            if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \
                    & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value):
                return "b_quick_gain_param"

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

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

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

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

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

                if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \
                        (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
                    # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
                    return 'b_over_rsi_max'

            if self.profit_b_short_loss.value:
                if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\
                        & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
                    # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
                    return 'b_short_lost'
        else:
            if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)):
                return 'h_percent_quick'
            if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\
                    & (days < self.sell_h_too_old_day.value * 2)\
                    & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
                return "h_too_old_0.01"
            if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\
                & (days < self.sell_h_too_old_day.value * 3) \
                    & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
                return "h_too_old_0.02"
            if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \
                    & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0):
                return "h_too_old_0.03"

            if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005):
                return "h_quick_lost"

            if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \
                    & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \
                    & ((current_time - trade.open_date_utc).seconds >= 3600):
                return "h_no_change"

            if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \
                    & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value):
                return "h_quick_gain_param"

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

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

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

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

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

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

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

    def informative_pairs(self):
        # get access to all pairs available in whitelist.
        pairs = self.dp.current_whitelist()
        informative_pairs = [(pair, '1d') for pair in pairs]
        informative_pairs += [(pair, '4h') for pair in pairs]
        informative_pairs += [(pair, '1h') for pair in pairs]

        return informative_pairs

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Add all ta features

        dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line(
            dataframe['high'],
            dataframe['low'],
            window1=9,
            window2=26,
            visual=False,
            fillna=False
        )
        KST = ta.trend.KSTIndicator(
            close=dataframe['close'],
            roc1=10,
            roc2=15,
            roc3=20,
            roc4=30,
            window1=10,
            window2=10,
            window3=10,
            window4=15,
            nsig=9,
            fillna=False
        )

        dataframe['trend_kst_diff'] = KST.kst_diff()
        dataframe['rsi'] = talib.RSI(dataframe)
        dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5)
        dataframe['pct_change'] = dataframe['close'].pct_change(5)
        dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value)
        dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10)
        dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20)
        dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50)
        dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200)
        dataframe['min200_5'] = dataframe['min200'] * 1.005
        dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() 
        dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50)
        dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200)
        dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50']
        dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200']
        dataframe['min_max200_5'] = (dataframe['min200'] * (1 + dataframe['min_max200'] / 5))
        dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5)
        dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10)
        dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20)
        dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50)
        dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100)
        dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"]
        dataframe["percent5"] = dataframe["percent"].rolling(5).sum()
        dataframe["percent3"] = dataframe["percent"].rolling(3).sum()
        dataframe["percent10"] = dataframe["percent"].rolling(10).sum()
        dataframe["percent20"] = dataframe["percent"].rolling(20).sum()
        dataframe["percent50"] = dataframe["percent"].rolling(50).sum()
        dataframe["volume10"] = dataframe["volume"].rolling(10).mean()
        dataframe["volume10"] = dataframe["volume"].rolling(10).mean()
        # 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_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var()
        dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1)
        # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36)

        dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close']
        dataframe['min1.1'] = 1.01 * dataframe['min']
        dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean())
        dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var()
        dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var()

        dataframe['sar'] = talib.SAR(dataframe)
        # Normalization
        tib = dataframe['trend_ichimoku_base']
        dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min())
        tkd = dataframe['trend_kst_diff']
        dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min())

        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["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0])
        dataframe['atr'] = talib.ATR(dataframe, timeperiod=14)

        ################### INFORMATIVE 1D
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d")
        informative["rsi"] = talib.RSI(informative)
        informative["max3"] = talib.MAX(informative['close'], timeperiod=3)
        informative["min3"] = talib.MIN(informative['close'], timeperiod=3)
        informative['pct_change_1'] = informative['close'].pct_change(1)
        informative['pct_change_3'] = informative['close'].pct_change(3)
        informative['pct_change_5'] = informative['close'].pct_change(5)
        informative['sma3'] = talib.SMA(informative, timeperiod=3)
        informative['sma5'] = talib.SMA(informative, timeperiod=5)
        informative['sma10'] = talib.SMA(informative, timeperiod=10)
        informative['sar'] = talib.SAR(informative)

        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2)
        informative['bb_lowerband'] = bollinger['lower']
        informative['bb_middleband'] = bollinger['mid']
        informative['bb_upperband'] = bollinger['upper']
        informative["bb_percent"] = (
            (informative["close"] - informative["bb_lowerband"]) /
            (informative["bb_upperband"] - informative["bb_lowerband"])
        )
        informative["bb_width"] = (
            (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"]
        )

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

        ######################## INFORMATIVE 4h
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h")
        informative["rsi"] = talib.RSI(informative)
        informative['pct_change_1'] = informative['close'].pct_change(1)
        informative['pct_change_3'] = informative['close'].pct_change(3)
        informative['pct_change_5'] = informative['close'].pct_change(5)
        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True)

        ################### INFORMATIVE BTC 1H
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h")
        informative["rsi"] = talib.RSI(informative)
        informative["rsi_5"] = talib.RSI(informative, timeperiod=5)
        informative['pct_change_1'] = informative['close'].pct_change(1)
        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True)

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        ok = True
        #if self.dp:
        #    if self.dp.runmode.value in ('live', 'dry_run'):
        #        ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35)
        conditions = []
        IND = 'trend_ichimoku_base'
        REAL = self.buy_h_real.value
        OPR = self.buy_h_cat.value
        DFIND = dataframe[IND]
        # print(DFIND.mean())
        if OPR == ">R":
            conditions.append(DFIND > REAL)
        elif OPR == "=R":
            conditions.append(np.isclose(DFIND, REAL))
        elif OPR == "<R":
            conditions.append(DFIND < REAL)

        if conditions:
            dataframe.loc[
                (reduce(lambda x, y: x & y, conditions))
                #& (ok)
                & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
                & (dataframe['pct_change'] < - self.buy_h_pct.value)
                & (dataframe['close'] <= dataframe['min200'] * 1.002)
                & (dataframe['pct_change_1_1d'] > 0)
                & (dataframe['pct_change_3_1d'] > 0)
                & (dataframe['pct_change_5_1d'] > 0)  #self.buy_h_pct_5.value)
                #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value)
                & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value)
                & (dataframe['close'] <= dataframe['sma5_1d'])
                & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d'])
                & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2)
                ,
                ['buy', 'buy_tag']] = (1, 'buy_h')

        conditions = []
        IND = 'trend_ichimoku_base'
        REAL = self.buy_b_real.value
        OPR = self.buy_b_cat.value
        DFIND = dataframe[IND]
        # print(DFIND.mean())
        if OPR == ">R":
            conditions.append(DFIND > REAL)
        elif OPR == "=R":
            conditions.append(np.isclose(DFIND, REAL))
        elif OPR == "<R":
            conditions.append(DFIND < REAL)

        if conditions:
            dataframe.loc[
                (reduce(lambda x, y: x & y, conditions))
                #& (ok)
                & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
                & (dataframe['pct_change'] < - self.buy_b_pct.value)
                & (dataframe['close'] <= dataframe['min200'] * 1.002)
                & (dataframe['pct_change_1_1d'] > 0)
                & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value)
                & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value)
                #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value)
                & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value)
                & (dataframe['close'] <= dataframe['sma5_1d'])
                & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d'])
                & (dataframe['cond1'] <= 0.45)  # self.buy_real_num0.value / 2)
                ,
                ['buy', 'buy_tag']] = (1, 'buy_b')

        for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value):
            #if self.buy_0.value:
                conditions = list()
                condition1, dataframe = condition_generator(
                    dataframe,
                    buy_operator0,
                    buy_indicator0,
                    buy_crossed_indicator0,
                    self.buy_real_num0.value,
                    self.buy_decalage0.value
                )
                conditions.append(condition1)
                dataframe.loc[
                    (
                       reduce(lambda x, y: x & y, conditions)
                       #& (ok)
                       & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10)
                       & (dataframe['sma10'].shift(1) <= dataframe['sma10'])
                       & (dataframe['bb_width'] >= 0.05)
                       & (dataframe['close'] < dataframe['bb_middleband'])
                       & (dataframe['open'] < dataframe['sma10'])
                       & (dataframe['open'] < dataframe['sma20'])
                       & (dataframe['min50'].shift(decalage) == dataframe['min50'])
                       & (dataframe['min10'] <= dataframe['min50'] * 1.02)
                       & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value)
                       # & (dataframe['min20'] == dataframe['min50'])
                       & (dataframe['distance_min'] <= self.buy_0_distance.value)
                       & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005)
                       # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value)
                       & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value)
                       #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value)
                       #& (dataframe['percent_4h'] > 0)
                       #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value)
                    ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage))
        for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value):
            #if self.buy_2.value:
                dataframe.loc[
                    (
                       (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2)
                       #& (ok)
                       & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10)
                       & (dataframe['bb_width'] >= 0.05)
                       & (dataframe['close'] < dataframe['sma10'])
                       & (dataframe['open'] < dataframe['sma20'])
                       & (dataframe['open'] < dataframe['sma10'])
                       & (dataframe['min50'].shift(decalage) == dataframe['min50'])
                       & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value)
                       & (dataframe['distance_min'] <= self.buy_2_distance.value)
                       & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(
                        decalage) <= 0.005)
                       & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value)
                    ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage))
                # d = dataframe.tail(1).iloc[0]
                # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20'])
        for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value):
            #if self.buy_3.value:
                dataframe.loc[
                    (
                       (dataframe['cond1'].shift(decalage) <= 0.2)
                       #& (ok)
                       & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10)
                       # & (dataframe['sma10'].shift(1) <= dataframe['sma10'])
                       & (dataframe['bb_width'] >= 0.05)
                       & (dataframe['close'] < dataframe['sma10'])
                       & (dataframe['open'] < dataframe['sma20'])
                       & (dataframe['open'] < dataframe['sma10'])
                       & (dataframe['min50'].shift(decalage) == dataframe['min50'])
                       #& (dataframe['min10'] <= dataframe['min50'] * 1.02)
                       & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value)
                       & (dataframe['distance_min'] <= self.buy_3_distance.value)
                       & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(
                        decalage) <= 0.005)
                       # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value)
                       & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value)
                       #& (dataframe['percent_4h'] > 0)
                       #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value)
                    ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage))

        # dataframe.loc[
        #     (
        #        (dataframe['cond1'] <= 1)
        #        & (dataframe['close'] < dataframe['sma10'])
        #        & (dataframe['open'] < dataframe['sma20'])
        #        & (dataframe['open'] < dataframe['sma10'])
        #        & (dataframe['close'] <= dataframe['min'] * 1.005)
        #        & (dataframe['moy200_12'] == dataframe['min200'])
        #        & (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10)
        #        & (dataframe['percent20'] >= -0.005)
        #
        #     ), ['buy', 'buy_tag']] = (1, 'buy_near_m50')

        # dataframe.loc[
        #     (
        #        (dataframe['trend_ichimoku_base'] <= 0.08)
        #        & (dataframe['rsi'] < 42)
        #        & (dataframe['rsi_1h'] >= self.buy_rsi.value)
        #        & (dataframe['close'] < dataframe['sma10'])
        #        # & (dataframe['min50'].shift(2) == dataframe['min50'])
        #     ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku')
        # d = dataframe.tail(1)
        # print(metadata['pair'], d['percent50'].iloc[0], d['buy'].iloc[0], d['buy_tag'].iloc[0])

        dataframe.loc[
            (
                    (
                        (dataframe['trend_ichimoku_base'] <= self.buy_base.value)
                        | (dataframe['trend_kst_diff'] <= self.buy_diff.value)
                    )
                    & (dataframe['rsi_1h'] < self.buy_rsi_1h.value)
                    & (dataframe['rsi_1h'] >= self.buy_rsi_max_1h.value)
                    & (dataframe['rsi'] > self.buy_rsi.value)
                    & (dataframe['rsi'] <= self.buy_rsi_max.value)
                    #& (dataframe['close'] < dataframe['sma10'])
                    # & (dataframe['close'] < dataframe['min200'] * (1 + dataframe['min_max200'] / 5))
                    #& (dataframe['sma10'].shift(1) * 1.001 < dataframe['sma10'])
                    # & (dataframe['min50'].shift(2) == dataframe['min50'])
                    # & (dataframe['rsi_5_1h'] > 35)
                    & (dataframe['close'] <= dataframe['min50'] * (1 + dataframe['min_max50'] / 2))
            ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku_h')

        return dataframe

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

        return dataframe
