# source: https://raw.githubusercontent.com/DerSalvador/freqtrade-helm-chart/f80d4d8b77c53435e9c0a9045636f1bfb2b8c539/chart/deployed_strategies/binance-michael-k8s-namespace/ichiV1_Marius.py
# --- Do not remove these libs ---
from sqlalchemy import true
from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame, Series
import copy
import logging
import pathlib
import rapidjson
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import pandas as pd  # noqa
pd.options.mode.chained_assignment = None  # default='warn'
import technical.indicators as ftt
from freqtrade.exchange import timeframe_to_prev_date
from functools import reduce
from datetime import datetime, timedelta, timezone
import numpy as np
from technical.util import resample_to_interval, resampled_merge
from freqtrade.strategy import informative
from freqtrade.strategy import stoploss_from_open
from freqtrade.strategy import BooleanParameter, timeframe_to_minutes, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter
from freqtrade.persistence import Trade
from typing import Dict
import numpy  # noqa
import math
import pandas_ta as pta
from typing import List
from skopt.space import Dimension, Integer
import time
from warnings import simplefilter
from technical.indicators import zema
logger = logging.getLogger(__name__)
simplefilter(action='ignore', category=pd.errors.PerformanceWarning)
##### SETINGS #####
# It hyperopt just one set of params for all entry and exit strategies if true.
DUALFIT = False
COUNT = 10
GAP = 3
### END SETINGS ###

def max_pump_detect_price_15m(dataframe, period=14, pause=288):
    df = dataframe.copy()
    df['size'] = df['high'] - df['low']
    cumulativeup = 0
    countup = 0
    cumulativedown = 0
    countdown = 0
    for i in range(period):
        cumulativeup = cumulativeup + df['volume'].shift(i) * df['size'].shift(i) * np.where(df['close'].shift(i) > df['open'].shift(i), 1, 0)
        cumulativedown = cumulativedown + df['volume'].shift(i) * df['size'].shift(i) * np.where(df['close'].shift(i) > df['open'].shift(i), 0, 1)
    flow_price = cumulativeup - cumulativedown
    flow_price_normalized = flow_price / (df['volume'].rolling(499).mean() * (df['high'] - df['low']).rolling(499).mean())
    max_flow_price = flow_price_normalized.rolling(pause).max()
    return max_flow_price

def flow_price_15m(dataframe, period=14, pause=288):
    df = dataframe.copy()
    df['size'] = df['high'] - df['low']
    cumulativeup = 0
    countup = 0
    cumulativedown = 0
    countdown = 0
    for i in range(period):
        cumulativeup = cumulativeup + df['volume'].shift(i) * df['size'].shift(i) * np.where(df['close'].shift(i) > df['open'].shift(i), 1, 0)
        cumulativedown = cumulativedown + df['volume'].shift(i) * df['size'].shift(i) * np.where(df['close'].shift(i) > df['open'].shift(i), 0, 1)
    flow_price = cumulativeup - cumulativedown
    flow_price_normalized = flow_price / (df['volume'].rolling(499).mean() * (df['high'] - df['low']).rolling(499).mean())
    return flow_price_normalized

def to_minutes(**timdelta_kwargs):
    return int(timedelta(**timdelta_kwargs).total_seconds() / 60)
#########################################################
#######################  ichiV1_Mod #####################
#########################################################
#############################################

class Github_DerSalvador_freqtrade_helm_chart__ichiV1_Marius__20260416_224245(IStrategy):
    INTERFACE_VERSION = 3

    class HyperOpt:

        @staticmethod
        def generate_roi_table(params: dict):
            """
            Generate the ROI table that will be used by Hyperopt
            This implementation generates the default legacy Freqtrade ROI tables.
            Change it if you need different number of steps in the generated
            ROI tables or other structure of the ROI tables.
            Please keep it aligned with parameters in the 'roi' optimization
            hyperspace defined by the roi_space method.
            """
            roi_table = {}
            roi_table[0] = 0.05
            roi_table[params['roi_t6']] = 0.04
            roi_table[params['roi_t5']] = 0.03
            roi_table[params['roi_t4']] = 0.02
            roi_table[params['roi_t3']] = 0.01
            roi_table[params['roi_t2']] = 0.0001
            roi_table[params['roi_t1']] = -10
            return roi_table

        @staticmethod
        def roi_space() -> List[Dimension]:
            """
            Values to search for each ROI steps
            Override it if you need some different ranges for the parameters in the
            'roi' optimization hyperspace.
            Please keep it aligned with the implementation of the
            generate_roi_table method.
            """
            return [Integer(240, 720, name='roi_t1'), Integer(120, 240, name='roi_t2'), Integer(90, 120, name='roi_t3'), Integer(60, 90, name='roi_t4'), Integer(30, 60, name='roi_t5'), Integer(1, 30, name='roi_t6')]
    DATESTAMP = 0
    SELLMA = 1
    # Buy hyperspace params:
    ##
    ##
    # "entry_fan_magnitude_shift_value": 3,
    # NOTE: Good value (Win% ~70%), alot of trades
    #"entry_min_fan_magnitude_gain": 1.008 # NOTE: Very save value (Win% ~90%), only the biggest moves 1.008,
    entry_params = {'max_slip': 0.668, 'antipump_threshold': 0.265, 'antipump_threshold_2': 0.133, 'entry_btc_safe_1d': -0.236, 'entry_btc_safe': -213, 'entry_threshold': 0.012, 'pump_limit': 1000, 'pump_pause_duration': 192, 'pump_period': 14, 'pump_recorver_price': 1.1, 'entry_trend_above_senkou_level': 1, 'entry_trend_bullish_level': 6, 'tesla_enabled': True, 'entry_min_fan_magnitude_gain': 1.0022}
    exit_params = {'ProfitLoss1': 0.005, 'ProfitLoss2': 0.021, 'ProfitMargin1': 0.018, 'ProfitMargin2': 0.051, 'pHSL': -0.08, 'exit_trend_indicator': 'trend_close_2h'}
    # minimum conditions to match in entry
    entry_minimum_conditions = IntParameter(1, 2, default=1, space='entry', optimize=False, load=True)
    position_adjustment_enable = True
    max_dca_orders = 2
    max_dca_multiplier = 1.25
    dca_stake_multiplier = 1.25

    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, **kwargs) -> float:
        if self.config['position_adjustment_enable'] == True and self.config['stake_amount'] == 'unlimited':
            return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] / self.max_dca_multiplier
        else:
            return proposed_stake

    def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: float, max_stake: float, **kwargs):
        if current_profit > -0.05:
            return None
        dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        previous_candle = dataframe.iloc[-2].squeeze()
        if last_candle['close'] < previous_candle['close']:
            return None
        filled_entrys = trade.select_filled_orders('entry')
        count_of_entrys = len(filled_entrys)
        if 0 < count_of_entrys <= self.max_dca_orders:
            try:
                stake_amount = filled_entrys[0].cost
                # This then calculates current safety order size
                stake_amount = stake_amount * self.dca_stake_multiplier
                return stake_amount
            except Exception as exception:
                return None
        return None
    # Pump protection
    pump_period = IntParameter(5, 24, default=entry_params['pump_period'], space='entry', optimize=False)
    pump_limit = IntParameter(100, 10000, default=entry_params['pump_limit'], space='entry', optimize=True)
    pump_recorver_price = DecimalParameter(1.0, 1.3, default=entry_params['pump_recorver_price'], space='entry', optimize=True)
    pump_pause_duration = IntParameter(6, 500, default=entry_params['pump_pause_duration'], space='entry', optimize=True)
    ##################################################################    
    ## Slippage params
    is_optimize_slip = False
    max_slip = DecimalParameter(0.33, 0.8, default=0.33, decimals=3, optimize=is_optimize_slip, load=True)
    entry_btc_safe = IntParameter(-300, 50, default=entry_params['entry_btc_safe'], optimize=True)
    entry_btc_safe_1d = DecimalParameter(-0.5, -0.015, default=entry_params['entry_btc_safe_1d'], optimize=True)
    antipump_threshold = DecimalParameter(0, 0.4, default=entry_params['antipump_threshold'], space='entry', optimize=True)
    antipump_threshold_2 = DecimalParameter(0, 0.4, default=entry_params['antipump_threshold_2'], space='entry', optimize=True)
    entry_min_fan_magnitude_gain = DecimalParameter(70, 90, default=entry_params['entry_min_fan_magnitude_gain'], space='entry', optimize=False, load=True)  # Multi Offset
    entry_threshold = DecimalParameter(0.003, 0.012, default=entry_params['entry_threshold'], optimize=True)
    #######################################################################
    # ROI table:
    minimal_roi = {'0': 0.215, '40': 0.032, '87': 0.016, '201': 0}
    # Stoploss:
    stoploss = -0.275  # value loaded from strategy
    # Trailing stop:
    trailing_stop = False
    #trailing_stop_positive = 0.001
    #trailing_stop_positive_offset = 0.016
    #trailing_only_offset_is_reached = True
    window_entry = IntParameter(60, 1000, default=500, space='entry', optimize=True)
    bandwidth_entry = IntParameter(2, 15, default=8, space='entry', optimize=True)
    mult_entry = DecimalParameter(0.5, 20.0, default=3, space='entry', optimize=True)
    # Optional order time in force.
    order_time_in_force = {'entry': 'gtc', 'exit': 'gtc'}
    use_custom_stoploss = True
    # Optimal timeframe for the strategy
    timeframe = '5m'
    informative_timeframe = '1h'
    inf_15m = '15m'  #use for pump detection
    timeframe_minutes = timeframe_to_minutes(timeframe)
    # storage dict for custom info
    custom_info = {}
    startup_candle_count: int = 499
    # startup_candle_count = 96
    process_only_new_candles = True
    # 50 // timeframe_minutes,
    # 85 // timeframe_minutes,
    timeperiods = [180 // timeframe_minutes, 360 // timeframe_minutes, 420 // timeframe_minutes, 560 // timeframe_minutes]
    use_exit_signal = False
    exit_profit_only = False
    ignore_roi_if_entry_signal = True
    # trailing stoploss hyperopt parameters
    pHSL = DecimalParameter(-0.15, -0.08, default=exit_params['pHSL'], decimals=3, space='exit', optimize=True)
    ProfitMargin1 = DecimalParameter(0.009, 0.019, default=exit_params['ProfitMargin1'], decimals=3, space='exit', optimize=True)
    ProfitLoss1 = DecimalParameter(0.005, 0.012, default=exit_params['ProfitLoss1'], decimals=3, space='exit', optimize=True)
    ProfitMargin2 = DecimalParameter(0.033, 0.099, default=exit_params['ProfitMargin2'], decimals=3, space='exit', optimize=True)
    ProfitLoss2 = DecimalParameter(0.01, 0.025, default=exit_params['ProfitLoss2'], decimals=3, space='exit', optimize=True)
    # fill area between senkou_a and senkou_b
    #optional
    #optional
    #optional
    # plot senkou_b, too. Not only the area to it.
    plot_config = {'main_plot': {'senkou_a': {'color': 'green', 'fill_to': 'senkou_b', 'fill_label': 'Ichimoku Cloud', 'fill_color': 'rgba(255,76,46,0.2)'}, 'senkou_b': {}}, 'subplots': {'fan_magnitude': {'fan_magnitude': {}}, 'fan_magnitude_gain': {'fan_magnitude_gain': {}}}}
    slippage_protection = {'retries': 3, 'max_slippage': -0.02}

    def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs):
        if (current_time - trade.open_date_utc).seconds / 60 > 1440:
            return 'unclog'

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
        HSL = self.pHSL.value
        if current_profit > self.ProfitMargin2.value:
            sl_profit = self.ProfitLoss2.value
        elif current_profit > self.ProfitMargin1.value:
            sl_profit = self.ProfitLoss1.value + (current_profit - self.ProfitMargin1.value) * (self.ProfitLoss2.value - self.ProfitLoss1.value) / (self.ProfitMargin2.value - self.ProfitMargin1.value)
        else:
            sl_profit = HSL
        return sl_profit

    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, exit_reason: str, current_time: datetime, **kwargs) -> bool:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1]
        current_profit = trade.calc_profit_ratio(rate)
        if 'tesla_' in trade.entry_tag and current_profit > 0.01:
            return True
        if trade.entry_tag == 'telsa_':
            if exit_reason in ['exit_signal'] or exit_reason in ['roi'] or exit_reason in ['trailing_stop_loss']:
                return False
        if last_candle is not None:
            if exit_reason in ['exit_signal']:
                if last_candle['hma_50'] > last_candle['ema_100'] and last_candle['rsi'] < 45:  #*1.2
                    return False
        if last_candle is not None:
            if exit_reason in ['exit_signal']:
                if last_candle['hma_50'] * 1.149 > last_candle['ema_100'] and last_candle['close'] < last_candle['ema_100'] * 0.951:  #*1.2
                    return False
        # slippage
        try:
            state = self.slippage_protection['__pair_retries']
        except KeyError:
            state = self.slippage_protection['__pair_retries'] = {}
        candle = dataframe.iloc[-1].squeeze()
        slippage = rate / candle['close'] - 1
        if slippage < self.slippage_protection['max_slippage']:
            pair_retries = state.get(pair, 0)
            if pair_retries < self.slippage_protection['retries']:
                state[pair] = pair_retries + 1
                return False
        state[pair] = 0
        return True
    age_filter = 30

    @informative('1d')
    def populate_indicators_1d(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['age_filter_ok'] = dataframe['volume'].rolling(window=self.age_filter, min_periods=self.age_filter).min() > 0
        return dataframe

    def informative_pairs(self):
        # get access to all pairs available in whitelist.
        pairs = self.dp.current_whitelist()
        # Assign tf to each pair so they can be downloaded and cached for strategy.
        informative_pairs = [(pair, '1h') for pair in pairs]
        informative_pairs = [(pair, '15m') for pair in pairs]
        informative_pairs.extend([(pair, self.informative_timeframe) for pair in pairs])
        informative_pairs += [('BTC/USDT', '1m')]
        informative_pairs += [('BTC/USDT', '5m')]
        informative_pairs += [('BTC/USDT', '1d')]
        return informative_pairs

    def get_informative_indicators(self, metadata: dict):
        dataframe = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.informative_timeframe)
        return dataframe

    def informative_1h_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        tik = time.perf_counter()
        assert self.dp, 'DataProvider is required for multiple timeframes.'
        # Get the informative pair
        informative_1h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.informative_timeframe)
        # RSI
        informative_1h['rsi'] = ta.RSI(informative_1h, timeperiod=14)
        #Weekly average close price  
        informative_1h['weekly_close_avg'] = informative_1h['close'].rolling(168).mean()
        return informative_1h
    #######################################################################
    # Informative indicator for pump detection 

    def informative_15m_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        assert self.dp, 'DataProvider is required for multiple timeframes.'
        # Get the informative pair
        informative_15m = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_15m)
        informative_15m['max_flow_price'] = max_pump_detect_price_15m(informative_15m, period=self.pump_period.value, pause=self.pump_pause_duration.value)
        informative_15m['flow_price'] = flow_price_15m(informative_15m, period=self.pump_period.value, pause=self.pump_pause_duration.value)
        return informative_15m
    #######################################################################

    def top_percent_change(self, dataframe: DataFrame, length: int) -> float:
        """
        Percentage change of the current close from the range maximum Open price

        :param dataframe: DataFrame The original OHLC dataframe
        :param length: int The length to look back
        """
        df = dataframe.copy()
        if length == 0:
            return (df['open'] - df['close']) / df['close']
        else:
            return (df['open'].rolling(length).max() - df['close']) / df['close']

    def normal_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        ### BTC protection
        if self.config['stake_currency'] in ['USDT', 'BUSD', 'USDC', 'DAI', 'TUSD', 'PAX', 'USD', 'EUR', 'GBP']:
            btc_info_pair = f"BTC/{self.config['stake_currency']}"
        else:
            btc_info_pair = 'BTC/USDT'
        btc_df = self.dp.get_pair_dataframe(pair=btc_info_pair, timeframe=self.timeframe)
        dataframe['btc_rsi'] = normalize(ta.RSI(btc_df, timeperiod=14), 0, 100)
        ### BTC protection
        dataframe['btc_5m'] = self.dp.get_pair_dataframe('BTC/USDT', timeframe='5m')['close']
        btc_1d = self.dp.get_pair_dataframe('BTC/USDT', timeframe='1d')[['date', 'close']].rename(columns={'close': 'btc'}).shift(1)
        dataframe = merge_informative_pair(dataframe, btc_1d, '5m', '1d', ffill=True)
        # BTC info
        informative = self.dp.get_pair_dataframe(pair='BTC/USDT', timeframe='5m')
        informative_past = informative.copy().shift(1)
        # BTC 5m dump protection
        informative_past_source = (informative_past['open'] + informative_past['close'] + informative_past['high'] + informative_past['low']) / 4  # Get BTC price
        informative_threshold = informative_past_source * self.entry_threshold.value  # BTC dump n% in 5 min
        informative_past_delta = informative_past['close'].shift(1) - informative_past['close']  # should be positive if dump
        informative_diff = informative_threshold - informative_past_delta  # Need be larger than 0
        dataframe['btc_threshold'] = informative_threshold
        dataframe['btc_diff'] = informative_diff
        # BTC 1d dump protection
        informative_past_1d = informative.copy().shift(288)
        informative_past_source_1d = (informative_past_1d['open'] + informative_past_1d['close'] + informative_past_1d['high'] + informative_past_1d['low']) / 4
        dataframe['btc_5m'] = informative_past_source
        dataframe['btc_1d'] = informative_past_source_1d
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        # strategy ClucMay72018
        dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=50)
        dataframe['volume_mean_slow'] = dataframe['volume'].rolling(window=30).mean()
        dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50)
        dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100)
        dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        dataframe['ema55'] = ta.EMA(dataframe, timeperiod=55)
        # Pump strength
        dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50)
        dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)
        dataframe['zema_30'] = ftt.zema(dataframe, period=30)
        dataframe['zema_200'] = ftt.zema(dataframe, period=200)
        dataframe['pump_strength'] = (dataframe['zema_30'] - dataframe['zema_200']) / dataframe['zema_30']
        dataframe['pump_strength_2'] = (dataframe['ema_50'] - dataframe['ema_200']) / dataframe['ema_50']
        heikinashi = qtpylib.heikinashi(dataframe)
        heikinashi['volume'] = dataframe['volume']
        dataframe['open'] = heikinashi['open']
        #dataframe['close'] = heikinashi['close']
        dataframe['high'] = heikinashi['high']
        dataframe['low'] = heikinashi['low']
        dataframe['trend_close_5m'] = dataframe['close']
        dataframe['trend_close_15m'] = ta.EMA(dataframe['close'], timeperiod=3)
        dataframe['trend_close_30m'] = ta.EMA(dataframe['close'], timeperiod=6)
        dataframe['trend_close_1h'] = ta.EMA(dataframe['close'], timeperiod=12)
        dataframe['trend_close_2h'] = ta.EMA(dataframe['close'], timeperiod=24)
        dataframe['trend_close_4h'] = ta.EMA(dataframe['close'], timeperiod=48)
        dataframe['trend_close_6h'] = ta.EMA(dataframe['close'], timeperiod=72)
        dataframe['trend_close_8h'] = ta.EMA(dataframe['close'], timeperiod=96)
        dataframe['trend_open_5m'] = dataframe['open']
        dataframe['trend_open_15m'] = ta.EMA(dataframe['open'], timeperiod=3)
        dataframe['trend_open_30m'] = ta.EMA(dataframe['open'], timeperiod=6)
        dataframe['trend_open_1h'] = ta.EMA(dataframe['open'], timeperiod=12)
        dataframe['trend_open_2h'] = ta.EMA(dataframe['open'], timeperiod=24)
        dataframe['trend_open_4h'] = ta.EMA(dataframe['open'], timeperiod=48)
        dataframe['trend_open_6h'] = ta.EMA(dataframe['open'], timeperiod=72)
        dataframe['trend_open_8h'] = ta.EMA(dataframe['open'], timeperiod=96)
        dataframe['fan_magnitude'] = dataframe['trend_close_1h'] / dataframe['trend_close_8h']
        dataframe['fan_magnitude_gain'] = dataframe['fan_magnitude'] / dataframe['fan_magnitude'].shift(1)
        ichimoku = ftt.ichimoku(dataframe, conversion_line_period=20, base_line_periods=60, laggin_span=120, displacement=30)
        dataframe['chikou_span'] = ichimoku['chikou_span']
        dataframe['tenkan_sen'] = ichimoku['tenkan_sen']
        dataframe['kijun_sen'] = ichimoku['kijun_sen']
        dataframe['senkou_a'] = ichimoku['senkou_span_a']
        dataframe['senkou_b'] = ichimoku['senkou_span_b']
        dataframe['leading_senkou_span_a'] = ichimoku['leading_senkou_span_a']
        dataframe['leading_senkou_span_b'] = ichimoku['leading_senkou_span_b']
        dataframe['cloud_green'] = ichimoku['cloud_green']
        dataframe['cloud_red'] = ichimoku['cloud_red']
        dataframe['atr'] = ta.ATR(dataframe)
        # MFI
        dataframe['mfi'] = ta.MFI(dataframe)
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # The indicators for the 1h informative timeframe
        informative_1h = self.informative_1h_indicators(dataframe, metadata)
        dataframe = merge_informative_pair(dataframe, informative_1h, self.timeframe, self.informative_timeframe, ffill=True)
        # The indicators for the normal (5m) timeframe
        dataframe = self.normal_tf_indicators(dataframe, metadata)
        #Import 15m indicators    
        informative_15m = self.informative_15m_indicators(dataframe, metadata)
        dataframe = merge_informative_pair(dataframe, informative_15m, self.timeframe, self.inf_15m, ffill=True)
        drop_columns = [s + '_' + self.inf_15m for s in ['date']]
        dataframe.drop(columns=dataframe.columns.intersection(drop_columns), inplace=True)
        #Pump protection
        dataframe['weekly_close_avg_offset'] = self.pump_recorver_price.value * dataframe['weekly_close_avg_1h']
        dataframe['price_test'] = dataframe['close'] > dataframe['weekly_close_avg_offset']
        dataframe['pump_price_test'] = dataframe['max_flow_price_15m'] > self.pump_limit.value
        # Check if a pump uccured during pump_pause_duration and coin didn't recovered its pre pump value
        dataframe['pump_dump_alert'] = dataframe['price_test'] & dataframe['pump_price_test']
        dataframe['entry_ok'] = np.where(dataframe['pump_dump_alert'], False, True)
        return dataframe
    ## Confirm Entry

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, **kwargs) -> bool:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        max_slip = self.max_slip.value
        if len(dataframe) < 1:
            return False
        dataframe = dataframe.iloc[-1].squeeze()
        if rate > dataframe['close']:
            slippage = (rate / dataframe['close'] - 1) * 100
            if slippage < max_slip:
                return True
            else:
                return False
        return True

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        dataframe.loc[:, 'entry_tag'] = ''
        is_protection = (pct_change(dataframe['btc_1d'], dataframe['btc_5m']).fillna(0) > self.entry_btc_safe_1d.value) & (dataframe['pump_strength_2'] < self.antipump_threshold_2.value) & dataframe['entry_ok'] & (dataframe['volume'] > 0)
        if self.entry_params['tesla_enabled'] >= True:
            tesla = (dataframe['rsi'] > dataframe['rsi_1h']) & (dataframe['trend_close_8h'] > dataframe['trend_close_6h']) & (dataframe['trend_close_15m'] > dataframe['trend_close_30m']) & (dataframe['trend_open_5m'] > dataframe['trend_open_15m']) & (dataframe['trend_close_1h'] > dataframe['ema55']) & (dataframe['ema21'] > dataframe['trend_close_4h']) & (dataframe['trend_open_1h'] > dataframe['trend_open_2h']) & (dataframe['mfi'] < 70) & (dataframe['fan_magnitude_gain'] >= self.entry_min_fan_magnitude_gain.value) & (dataframe['fan_magnitude'] > 0.99)
            conditions.append(tesla)
            dataframe.loc[tesla, 'entry_tag'] += 'tesla_'
        if conditions:
            dataframe.loc[is_protection & reduce(lambda x, y: x & y, conditions), 'entry'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        conditions.append(qtpylib.crossed_below(dataframe['trend_close_5m'], dataframe[self.exit_params['exit_trend_indicator']]))
        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'exit'] = 1
        return dataframe

    def rollingNormalize(self, dataframe, name):
        df = dataframe.copy()
        df[name + '_nmin'] = df[name].rolling(window=1440 // self.timeframe_minutes).min()
        df[name + '_nmax'] = df[name].rolling(window=1440 // self.timeframe_minutes).max()
        return np.where(df[name + '_nmin'] == df[name + '_nmax'], 0, 2.0 * (df[name] - df[name + '_nmin']) / (df[name + '_nmax'] - df[name + '_nmin']) - 1.0)

def normalize(data, min_value, max_value):
    return (data - min_value) / (max_value - min_value)

def pct_change(a, b):
    return (b - a) / a
# smoothed Heiken Ashi

def HA(dataframe, smoothing=None):
    df = dataframe.copy()
    df['HA_Close'] = (df['open'] + df['high'] + df['low'] + df['close']) / 4
    df.reset_index(inplace=True)
    ha_open = [(df['open'][0] + df['close'][0]) / 2]
    [ha_open.append((ha_open[i] + df['HA_Close'].values[i]) / 2) for i in range(0, len(df) - 1)]
    df['HA_Open'] = ha_open
    df.set_index('index', inplace=True)
    df['HA_High'] = df[['HA_Open', 'HA_Close', 'high']].max(axis=1)
    df['HA_Low'] = df[['HA_Open', 'HA_Close', 'low']].min(axis=1)
    if smoothing is not None:
        sml = abs(int(smoothing))
        if sml > 0:
            df['Smooth_HA_O'] = ta.EMA(df['HA_Open'], sml)
            df['Smooth_HA_C'] = ta.EMA(df['HA_Close'], sml)
            df['Smooth_HA_H'] = ta.EMA(df['HA_High'], sml)
            df['Smooth_HA_L'] = ta.EMA(df['HA_Low'], sml)
    return df

def pump_warning(dataframe, perc=15):
    df = dataframe.copy()
    df['change'] = df['high'] - df['low']
    df['test1'] = df['close'] > df['open']
    df['test2'] = df['change'] / df['low'] > perc / 100
    df['result'] = (df['test1'] & df['test2']).astype('int')
    return df['result']