# source: https://raw.githubusercontent.com/DerSalvador/freqtrade-helm-chart/a669dc11b640b0eb63aa8f8b51e9f181fd7ee43c/chart/deployed_strategies/binance-futures-k8s-namespace/NFI4Frog.py
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np
import talib.abstract as ta
from finta import TA as fta
from typing import Dict, List, Optional, Tuple
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy import merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter
from pandas import DataFrame
from functools import reduce
from freqtrade.exchange import timeframe_to_minutes
from freqtrade.persistence import Trade
from datetime import datetime, timedelta
from cachetools import TTLCache
from skopt.space import Dimension
###########################################################################################################
##                NostalgiaForInfinityV4 by iterativ                                                     ##
##                                                                                                       ##
##    Strategy for Freqtrade https://github.com/freqtrade/freqtrade                                      ##
##                                                                                                       ##
###########################################################################################################
##               GENERAL RECOMMENDATIONS                                                                 ##
##                                                                                                       ##
##   For optimal performance, suggested to use between 4 and 6 open trades, with unlimited stake.        ##
##   A pairlist with 40 to 80 pairs. Volume pairlist works well.                                         ##
##   Prefer stable coin (USDT, BUSDT etc) pairs, instead of BTC or ETH pairs.                            ##
##   Highly recommended to blacklist leveraged tokens (*BULL, *BEAR, *UP, *DOWN etc).                    ##
##   Ensure that you don't override any variables in you config.json. Especially                         ##
##   the timeframe (must be 5m).                                                                         ##
##     use_exit_signal must set to true (or not set at all).                                             ##
##     exit_profit_only must set to false (or not set at all).                                           ##
##     ignore_roi_if_entry_signal must set to true (or not set at all).                                    ##
##                                                                                                       ##
###########################################################################################################
##               DONATIONS                                                                               ##
##                                                                                                       ##
##   Absolutely not required. However, will be accepted as a token of appreciation.                      ##
##                                                                                                       ##
##   BTC: bc1qvflsvddkmxh7eqhc4jyu5z5k6xcw3ay8jl49sk                                                     ##
##   ETH (ERC20): 0x83D3cFb8001BDC5d2211cBeBB8cB3461E5f7Ec91                                             ##
##   BEP20/BSC (ETH, BNB, ...): 0x86A0B21a20b39d16424B7c8003E4A7e12d78ABEe                               ##
##                                                                                                       ##
###########################################################################################################

class Github_DerSalvador_freqtrade_helm_chart__NFI4Frog__20260115_122204(IStrategy):
    INTERFACE_VERSION = 3
    # # ROI table:
    minimal_roi = {'0': 10}
    stoploss = -1.0
    # Trailing stoploss (not used)
    trailing_stop = False
    trailing_only_offset_is_reached = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.03
    use_custom_stoploss = False
    # Optimal timeframe for the strategy.
    timeframe = '5m'
    inf_1h = '1h'
    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = True
    custom_trade_info = {}
    # These values can be overridden in the "ask_strategy" section in the config.
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = True
    use_dynamic_roi = False
    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 400
    # Optional order type mapping.
    order_types = {'entry': 'limit', 'exit': 'limit', 'trailing_stop_loss': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False}
    #############################################################
    #############
    # Enable/Disable conditions
    entry_params = {'entry_condition_1_enable': True, 'entry_condition_2_enable': True, 'entry_condition_3_enable': True, 'entry_condition_4_enable': True, 'entry_condition_5_enable': True, 'entry_condition_6_enable': True, 'entry_condition_7_enable': True, 'entry_condition_8_enable': True, 'entry_condition_9_enable': True, 'entry_condition_10_enable': True, 'entry_condition_11_enable': True, 'entry_condition_12_enable': True, 'entry_condition_13_enable': True, 'entry_condition_14_enable': True, 'entry_condition_15_enable': True, 'entry_condition_16_enable': True, 'entry_condition_17_enable': True}
    #############
    # Enable/Disable conditions
    #############
    exit_params = {'exit_condition_1_enable': True, 'exit_condition_2_enable': True, 'exit_condition_3_enable': True, 'exit_condition_4_enable': True, 'exit_condition_5_enable': True, 'exit_condition_6_enable': True, 'exit_condition_7_enable': True, 'exit_condition_8_enable': True}
    #############################################################
    entry_condition_1_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_2_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_3_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_4_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_5_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_6_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_7_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_8_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_9_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_10_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_11_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_12_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_13_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_14_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_15_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_16_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    entry_condition_17_enable = CategoricalParameter([True, False], default=True, space='entry', optimize=True, load=True)
    # Normal dips
    entry_dip_threshold_1 = DecimalParameter(0.001, 0.05, default=0.02, space='entry', decimals=3, optimize=True, load=True)
    entry_dip_threshold_2 = DecimalParameter(0.01, 0.2, default=0.14, space='entry', decimals=3, optimize=True, load=True)
    entry_dip_threshold_3 = DecimalParameter(0.05, 0.4, default=0.32, space='entry', decimals=3, optimize=True, load=True)
    entry_dip_threshold_4 = DecimalParameter(0.2, 0.5, default=0.5, space='entry', decimals=3, optimize=True, load=True)
    # Strict dips
    entry_dip_threshold_5 = DecimalParameter(0.001, 0.05, default=0.015, space='entry', decimals=3, optimize=True, load=True)
    entry_dip_threshold_6 = DecimalParameter(0.01, 0.2, default=0.06, space='entry', decimals=3, optimize=True, load=True)
    entry_dip_threshold_7 = DecimalParameter(0.05, 0.4, default=0.24, space='entry', decimals=3, optimize=True, load=True)
    entry_dip_threshold_8 = DecimalParameter(0.2, 0.5, default=0.4, space='entry', decimals=3, optimize=True, load=True)
    # 12 hours
    entry_pump_pull_threshold_1 = DecimalParameter(1.5, 3.0, default=1.75, space='entry', decimals=2, optimize=True, load=True)
    entry_pump_threshold_1 = DecimalParameter(0.4, 1.0, default=0.46, space='entry', decimals=3, optimize=True, load=True)
    # 36 hours
    entry_pump_pull_threshold_2 = DecimalParameter(1.5, 3.0, default=1.75, space='entry', decimals=2, optimize=True, load=True)
    entry_pump_threshold_2 = DecimalParameter(0.4, 1.0, default=0.56, space='entry', decimals=3, optimize=True, load=True)
    # 48 hours
    entry_pump_pull_threshold_3 = DecimalParameter(1.5, 3.0, default=1.75, space='entry', decimals=2, optimize=True, load=True)
    entry_pump_threshold_3 = DecimalParameter(0.4, 1.0, default=0.85, space='entry', decimals=3, optimize=True, load=True)
    # 12 hours strict
    entry_pump_pull_threshold_4 = DecimalParameter(1.5, 3.0, default=2.2, space='entry', decimals=2, optimize=True, load=True)
    entry_pump_threshold_4 = DecimalParameter(0.4, 1.0, default=0.4, space='entry', decimals=3, optimize=True, load=True)
    # 36 hours strict
    entry_pump_pull_threshold_5 = DecimalParameter(1.5, 3.0, default=2.0, space='entry', decimals=2, optimize=True, load=True)
    entry_pump_threshold_5 = DecimalParameter(0.4, 1.0, default=0.56, space='entry', decimals=3, optimize=True, load=True)
    # 48 hours strict
    entry_pump_pull_threshold_6 = DecimalParameter(1.5, 3.0, default=2.0, space='entry', decimals=2, optimize=True, load=True)
    entry_pump_threshold_6 = DecimalParameter(0.4, 1.0, default=0.68, space='entry', decimals=3, optimize=True, load=True)
    entry_min_inc_1 = DecimalParameter(0.01, 0.05, default=0.022, space='entry', decimals=3, optimize=True, load=True)
    entry_rsi_1h_min_1 = DecimalParameter(25.0, 40.0, default=30.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_1h_max_1 = DecimalParameter(70.0, 90.0, default=80.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_1 = DecimalParameter(20.0, 40.0, default=36.0, space='entry', decimals=1, optimize=True, load=True)
    entry_mfi_1 = DecimalParameter(20.0, 56.0, default=26.0, space='entry', decimals=1, optimize=True, load=True)
    entry_volume_2 = DecimalParameter(1.0, 10.0, default=2.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_1h_min_2 = DecimalParameter(30.0, 40.0, default=36.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_1h_max_2 = DecimalParameter(70.0, 95.0, default=90.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_1h_diff_2 = DecimalParameter(30.0, 50.0, default=34.0, space='entry', decimals=1, optimize=True, load=True)
    entry_mfi_2 = DecimalParameter(30.0, 65.0, default=56.0, space='entry', decimals=1, optimize=True, load=True)
    entry_bb_offset_2 = DecimalParameter(0.97, 0.99, default=0.983, space='entry', decimals=3, optimize=True, load=True)
    entry_bb40_bbdelta_close_3 = DecimalParameter(0.005, 0.06, default=0.057, space='entry', optimize=True, load=True)
    entry_bb40_closedelta_close_3 = DecimalParameter(0.01, 0.03, default=0.023, space='entry', optimize=True, load=True)
    entry_bb40_tail_bbdelta_3 = DecimalParameter(0.15, 0.45, default=0.418, space='entry', optimize=True, load=True)
    entry_ema_rel_3 = DecimalParameter(0.97, 0.999, default=0.988, space='entry', decimals=3, optimize=True, load=True)
    entry_bb20_close_bblowerband_4 = DecimalParameter(0.9, 0.99, default=0.979, space='entry', optimize=True, load=True)
    entry_bb20_volume_4 = IntParameter(16, 35, default=18, space='entry', optimize=True, load=True)
    entry_volume_5 = DecimalParameter(1.0, 10.0, default=6.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ema_open_mult_5 = DecimalParameter(0.016, 0.03, default=0.019, space='entry', decimals=3, optimize=True, load=True)
    entry_bb_offset_5 = DecimalParameter(0.98, 1.0, default=0.999, space='entry', decimals=3, optimize=True, load=True)
    entry_ema_rel_5 = DecimalParameter(0.97, 0.999, default=0.988, space='entry', decimals=3, optimize=True, load=True)
    entry_volume_6 = DecimalParameter(1.0, 10.0, default=1.5, space='entry', decimals=1, optimize=True, load=True)
    entry_ema_open_mult_6 = DecimalParameter(0.03, 0.04, default=0.025, space='entry', decimals=3, optimize=True, load=True)
    entry_bb_offset_6 = DecimalParameter(0.98, 0.999, default=0.995, space='entry', decimals=3, optimize=True, load=True)
    entry_volume_7 = DecimalParameter(1.0, 10.0, default=2.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ema_open_mult_7 = DecimalParameter(0.02, 0.04, default=0.03, space='entry', decimals=3, optimize=True, load=True)
    entry_rsi_7 = DecimalParameter(24.0, 50.0, default=36.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_8 = DecimalParameter(30.0, 50.0, default=46.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ema_rel_8 = DecimalParameter(0.97, 0.999, default=0.988, space='entry', decimals=3, optimize=True, load=True)
    entry_volume_9 = DecimalParameter(1.0, 4.0, default=2.0, space='entry', decimals=2, optimize=True, load=True)
    entry_ma_offset_9 = DecimalParameter(0.94, 0.99, default=0.958, space='entry', decimals=3, optimize=True, load=True)
    entry_bb_offset_9 = DecimalParameter(0.97, 0.99, default=0.984, space='entry', decimals=3, optimize=True, load=True)
    entry_rsi_1h_min_9 = DecimalParameter(26.0, 40.0, default=30.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_1h_max_9 = DecimalParameter(70.0, 90.0, default=80.0, space='entry', decimals=1, optimize=True, load=True)
    entry_mfi_9 = DecimalParameter(36.0, 65.0, default=56.0, space='entry', decimals=1, optimize=True, load=True)
    entry_volume_10 = DecimalParameter(1.0, 26.0, default=23.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ma_offset_10 = DecimalParameter(0.93, 0.97, default=0.94, space='entry', decimals=3, optimize=True, load=True)
    entry_bb_offset_10 = DecimalParameter(0.97, 0.99, default=0.994, space='entry', decimals=3, optimize=True, load=True)
    entry_rsi_1h_10 = DecimalParameter(20.0, 40.0, default=39.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ma_offset_11 = DecimalParameter(0.93, 0.99, default=0.938, space='entry', decimals=3, optimize=True, load=True)
    entry_min_inc_11 = DecimalParameter(0.005, 0.05, default=0.01, space='entry', decimals=3, optimize=True, load=True)
    entry_rsi_1h_min_11 = DecimalParameter(40.0, 60.0, default=55.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_1h_max_11 = DecimalParameter(70.0, 90.0, default=82.0, space='entry', decimals=1, optimize=True, load=True)
    entry_rsi_11 = DecimalParameter(30.0, 48.0, default=46.0, space='entry', decimals=1, optimize=True, load=True)
    entry_mfi_11 = DecimalParameter(36.0, 56.0, default=38.0, space='entry', decimals=1, optimize=True, load=True)
    entry_volume_12 = DecimalParameter(1.0, 10.0, default=2.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ma_offset_12 = DecimalParameter(0.93, 0.97, default=0.936, space='entry', decimals=3, optimize=True, load=True)
    entry_rsi_12 = DecimalParameter(26.0, 40.0, default=30.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ewo_12 = DecimalParameter(2.0, 6.0, default=2.8, space='entry', decimals=1, optimize=True, load=True)
    entry_ma_offset_13 = DecimalParameter(0.93, 0.98, default=0.952, space='entry', decimals=3, optimize=True, load=True)
    entry_ewo_13 = DecimalParameter(-14.0, -7.0, default=-7.9, space='entry', decimals=1, optimize=True, load=True)
    entry_volume_14 = DecimalParameter(1.0, 10.0, default=2.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ema_open_mult_14 = DecimalParameter(0.01, 0.03, default=0.014, space='entry', decimals=3, optimize=True, load=True)
    entry_bb_offset_14 = DecimalParameter(0.98, 1.0, default=0.992, space='entry', decimals=3, optimize=True, load=True)
    entry_ma_offset_14 = DecimalParameter(0.93, 0.99, default=0.998, space='entry', decimals=3, optimize=True, load=True)
    entry_ema_open_mult_15 = DecimalParameter(0.02, 0.04, default=0.026, space='entry', decimals=3, optimize=True, load=True)
    entry_ma_offset_15 = DecimalParameter(0.93, 0.99, default=0.985, space='entry', decimals=3, optimize=True, load=True)
    entry_rsi_15 = DecimalParameter(30.0, 50.0, default=32.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ema_rel_15 = DecimalParameter(0.97, 0.999, default=0.988, space='entry', decimals=3, optimize=True, load=True)
    entry_volume_16 = DecimalParameter(1.0, 10.0, default=2.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ma_offset_16 = DecimalParameter(0.93, 0.97, default=0.95, space='entry', decimals=3, optimize=True, load=True)
    entry_rsi_16 = DecimalParameter(26.0, 50.0, default=38.0, space='entry', decimals=1, optimize=True, load=True)
    entry_ewo_16 = DecimalParameter(4.0, 8.0, default=3.6, space='entry', decimals=1, optimize=True, load=True)
    entry_ma_offset_17 = DecimalParameter(0.93, 0.98, default=0.958, space='entry', decimals=3, optimize=True, load=True)
    entry_ewo_17 = DecimalParameter(-18.0, -10.0, default=-12.0, space='entry', decimals=1, optimize=True, load=True)
    # Sell
    exit_condition_1_enable = CategoricalParameter([True, False], default=True, space='exit', optimize=True, load=True)
    exit_condition_2_enable = CategoricalParameter([True, False], default=True, space='exit', optimize=True, load=True)
    exit_condition_3_enable = CategoricalParameter([True, False], default=True, space='exit', optimize=True, load=True)
    exit_condition_4_enable = CategoricalParameter([True, False], default=True, space='exit', optimize=True, load=True)
    exit_condition_5_enable = CategoricalParameter([True, False], default=True, space='exit', optimize=True, load=True)
    exit_condition_6_enable = CategoricalParameter([True, False], default=True, space='exit', optimize=True, load=True)
    exit_condition_7_enable = CategoricalParameter([True, False], default=True, space='exit', optimize=True, load=True)
    exit_condition_8_enable = CategoricalParameter([True, False], default=True, space='exit', optimize=True, load=True)
    exit_rsi_bb_1 = DecimalParameter(60.0, 80.0, default=79.5, space='exit', decimals=1, optimize=True, load=True)
    exit_rsi_bb_2 = DecimalParameter(72.0, 90.0, default=81, space='exit', decimals=1, optimize=True, load=True)
    exit_rsi_main_3 = DecimalParameter(77.0, 90.0, default=82, space='exit', decimals=1, optimize=True, load=True)
    exit_dual_rsi_rsi_4 = DecimalParameter(72.0, 84.0, default=73.4, space='exit', decimals=1, optimize=True, load=True)
    exit_dual_rsi_rsi_1h_4 = DecimalParameter(78.0, 92.0, default=79.6, space='exit', decimals=1, optimize=True, load=True)
    exit_ema_relative_5 = DecimalParameter(0.005, 0.05, default=0.024, space='exit', optimize=True, load=True)
    exit_rsi_diff_5 = DecimalParameter(0.0, 20.0, default=4.382, space='exit', optimize=True, load=True)
    exit_rsi_under_6 = DecimalParameter(72.0, 90.0, default=79.0, space='exit', decimals=1, optimize=True, load=True)
    exit_rsi_1h_7 = DecimalParameter(80.0, 95.0, default=81.7, space='exit', decimals=1, optimize=True, load=True)
    exit_bb_relative_8 = DecimalParameter(1.05, 1.3, default=1.1, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_profit_0 = DecimalParameter(0.01, 0.1, default=0.01, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_rsi_0 = DecimalParameter(30.0, 40.0, default=30.0, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_profit_1 = DecimalParameter(0.01, 0.1, default=0.03, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_rsi_1 = DecimalParameter(30.0, 50.0, default=36.0, space='exit', decimals=2, optimize=True, load=True)
    exit_custom_profit_2 = DecimalParameter(0.01, 0.1, default=0.05, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_rsi_2 = DecimalParameter(34.0, 50.0, default=43.0, space='exit', decimals=2, optimize=True, load=True)
    exit_custom_profit_3 = DecimalParameter(0.06, 0.3, default=0.08, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_rsi_3 = DecimalParameter(38.0, 55.0, default=48.0, space='exit', decimals=2, optimize=True, load=True)
    exit_custom_profit_4 = DecimalParameter(0.3, 0.6, default=0.25, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_rsi_4 = DecimalParameter(40.0, 58.0, default=50.0, space='exit', decimals=2, optimize=True, load=True)
    exit_custom_under_profit_1 = DecimalParameter(0.01, 0.1, default=0.02, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_under_profit_2 = DecimalParameter(0.01, 0.1, default=0.035, space='exit', decimals=3, optimize=True, load=True)
    exit_custom_under_profit_3 = DecimalParameter(0.05, 0.2, default=0.07, space='exit', decimals=3, optimize=True, load=True)
    exit_trail_profit_min_1 = DecimalParameter(0.1, 0.25, default=0.15, space='exit', decimals=3, optimize=True, load=True)
    exit_trail_profit_max_1 = DecimalParameter(0.3, 0.5, default=0.46, space='exit', decimals=2, optimize=True, load=True)
    exit_trail_down_1 = DecimalParameter(0.04, 0.2, default=0.18, space='exit', decimals=3, optimize=True, load=True)
    exit_trail_profit_min_2 = DecimalParameter(0.01, 0.1, default=0.01, space='exit', decimals=3, optimize=True, load=True)
    exit_trail_profit_max_2 = DecimalParameter(0.08, 0.25, default=0.12, space='exit', decimals=2, optimize=True, load=True)
    exit_trail_down_2 = DecimalParameter(0.04, 0.2, default=0.14, space='exit', decimals=3, optimize=True, load=True)
    #############################################################
    ## smoothed Heiken Ashi

    def HA(self, 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 hansen_HA(self, informative_df, period=6):
        dataframe = informative_df.copy()
        dataframe['hhclose'] = (dataframe['open'] + dataframe['high'] + dataframe['low'] + dataframe['close']) / 4
        dataframe['hhopen'] = (dataframe['open'].shift(2) + dataframe['close'].shift(2)) / 2  #it is not the same as real heikin ashi since I found that this is better.
        dataframe['hhhigh'] = dataframe[['open', 'close', 'high']].max(axis=1)
        dataframe['hhlow'] = dataframe[['open', 'close', 'low']].min(axis=1)
        dataframe['emac'] = ta.SMA(dataframe['hhclose'], timeperiod=period)  #to smooth out the data and thus less noise.
        dataframe['emao'] = ta.SMA(dataframe['hhopen'], timeperiod=period)
        return {'emac': dataframe['emac'], 'emao': dataframe['emao']}
    ## detect BB width expansion to indicate possible volatility

    def bbw_expansion(self, bbw_rolling, mult=1.1):
        bbw = list(bbw_rolling)
        m = 0.0
        for i in range(len(bbw) - 1):
            if bbw[i] > m:
                m = bbw[i]
        if bbw[-1] > m * mult:
            return 1
        return 0
    ## do_indicator style a la Obelisk strategies

    def do_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Stoch fast - mainly due to 5m timeframes
        stoch_fast = ta.STOCHF(dataframe)
        dataframe['fastd'] = stoch_fast['fastd']
        dataframe['fastk'] = stoch_fast['fastk']
        #StochRSI for double checking things
        period = 14
        smoothD = 3
        SmoothK = 3
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        stochrsi = (dataframe['rsi'] - dataframe['rsi'].rolling(period).min()) / (dataframe['rsi'].rolling(period).max() - dataframe['rsi'].rolling(period).min())
        dataframe['srsi_k'] = stochrsi.rolling(SmoothK).mean() * 100
        dataframe['srsi_d'] = dataframe['srsi_k'].rolling(smoothD).mean()
        # Bollinger Bands because obviously
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=1)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']
        # SAR Parabol - probably don't need this
        dataframe['sar'] = ta.SAR(dataframe)
        ## confirm wideboi variance signal with bbw expansion
        dataframe['bb_width'] = (dataframe['bb_upperband'] - dataframe['bb_lowerband']) / dataframe['bb_middleband']
        dataframe['bbw_expansion'] = dataframe['bb_width'].rolling(window=4).apply(self.bbw_expansion)
        # confirm entry and exit on smoothed HA
        dataframe = self.HA(dataframe, 4)
        # thanks to Hansen_Khornelius for this idea that I apply to the 1hr informative
        # https://github.com/hansen1015/freqtrade_strategy
        hansencalc = self.hansen_HA(dataframe, 6)
        dataframe['emac'] = hansencalc['emac']
        dataframe['emao'] = hansencalc['emao']
        # money flow index (MFI) for in/outflow of money, like RSI adjusted for vol
        dataframe['mfi'] = fta.MFI(dataframe)
        ## sqzmi to detect quiet periods
        dataframe['sqzmi'] = fta.SQZMI(dataframe)  #, MA=hansencalc['emac'])
        # Volume Flow Indicator (MFI) for volume based on the direction of price movement
        dataframe['vfi'] = fta.VFI(dataframe, period=14)
        dmi = fta.DMI(dataframe, period=14)
        dataframe['dmi_plus'] = dmi['DI+']
        dataframe['dmi_minus'] = dmi['DI-']
        dataframe['adx'] = fta.ADX(dataframe, period=14)
        ## for stoploss - all from Solipsis4
        ## simple ATR and ROC for stoploss
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['roc'] = ta.ROC(dataframe, timeperiod=9)
        dataframe['rmi'] = RMI(dataframe, length=24, mom=5)
        ssldown, sslup = SSLChannels_ATR(dataframe, length=21)
        dataframe['sroc'] = SROC(dataframe, roclen=21, emalen=13, smooth=21)
        dataframe['ssl-dir'] = np.where(sslup > ssldown, 'up', 'down')
        dataframe['rmi-up'] = np.where(dataframe['rmi'] >= dataframe['rmi'].shift(), 1, 0)
        dataframe['rmi-up-trend'] = np.where(dataframe['rmi-up'].rolling(5).sum() >= 3, 1, 0)
        dataframe['candle-up'] = np.where(dataframe['close'] >= dataframe['close'].shift(), 1, 0)
        dataframe['candle-up-trend'] = np.where(dataframe['candle-up'].rolling(5).sum() >= 3, 1, 0)
        return dataframe

    def get_ticker_indicator(self):
        return int(self.timeframe[:-1])

    def custom_exit(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()
        max_profit = (trade.max_rate - trade.open_rate) / trade.open_rate
        if last_candle is not None:
            if (current_profit > self.exit_custom_profit_4.value) & (last_candle['rsi'] < self.exit_custom_rsi_4.value):
                return 'signal_profit_4'
            elif (current_profit > self.exit_custom_profit_3.value) & (last_candle['rsi'] < self.exit_custom_rsi_3.value):
                return 'signal_profit_3'
            elif (current_profit > self.exit_custom_profit_2.value) & (last_candle['rsi'] < self.exit_custom_rsi_2.value):
                return 'signal_profit_2'
            elif (current_profit > self.exit_custom_profit_1.value) & (last_candle['rsi'] < self.exit_custom_rsi_1.value):
                return 'signal_profit_1'
            elif (current_profit > self.exit_custom_profit_0.value) & (last_candle['rsi'] < self.exit_custom_rsi_0.value):
                return 'signal_profit_0'
            elif (current_profit > self.exit_custom_under_profit_1.value) & (last_candle['close'] < last_candle['ema_200']):
                return 'signal_profit_u_1'
            elif (current_profit > self.exit_custom_under_profit_2.value) & last_candle['sma_200_dec']:
                return 'signal_profit_u_2'
            elif (current_profit > self.exit_custom_under_profit_3.value) & (last_candle['close'] < last_candle['ema_100']):
                return 'signal_profit_u_3'
            elif (current_profit > self.exit_trail_profit_min_1.value) & (current_profit < self.exit_trail_profit_max_1.value) & (max_profit > current_profit + self.exit_trail_down_1.value):
                return 'signal_profit_t_1'
            elif (current_profit > self.exit_trail_profit_min_2.value) & (current_profit < self.exit_trail_profit_max_2.value) & (max_profit > current_profit + self.exit_trail_down_2.value):
                return 'signal_profit_t_2'
        return None

    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]
        return informative_pairs

    def informative_1h_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        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.inf_1h)
        # EMA
        informative_1h['ema_15'] = ta.EMA(informative_1h, timeperiod=15)
        informative_1h['ema_50'] = ta.EMA(informative_1h, timeperiod=50)
        informative_1h['ema_100'] = ta.EMA(informative_1h, timeperiod=100)
        informative_1h['ema_200'] = ta.EMA(informative_1h, timeperiod=200)
        # SMA
        informative_1h['sma_200'] = ta.SMA(informative_1h, timeperiod=200)
        # RSI
        informative_1h['rsi'] = ta.RSI(informative_1h, timeperiod=14)
        # BB
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative_1h), window=20, stds=2)
        informative_1h['bb_lowerband'] = bollinger['lower']
        informative_1h['bb_middleband'] = bollinger['mid']
        informative_1h['bb_upperband'] = bollinger['upper']
        # Pump protections
        informative_1h['safe_pump_24'] = ((informative_1h['open'].rolling(24).max() - informative_1h['close'].rolling(24).min()) / informative_1h['close'].rolling(24).min() < self.entry_pump_threshold_1.value) | ((informative_1h['open'].rolling(24).max() - informative_1h['close'].rolling(24).min()) / self.entry_pump_pull_threshold_1.value > informative_1h['close'] - informative_1h['close'].rolling(24).min())
        informative_1h['safe_pump_36'] = ((informative_1h['open'].rolling(36).max() - informative_1h['close'].rolling(36).min()) / informative_1h['close'].rolling(36).min() < self.entry_pump_threshold_2.value) | ((informative_1h['open'].rolling(36).max() - informative_1h['close'].rolling(36).min()) / self.entry_pump_pull_threshold_2.value > informative_1h['close'] - informative_1h['close'].rolling(36).min())
        informative_1h['safe_pump_48'] = ((informative_1h['open'].rolling(48).max() - informative_1h['close'].rolling(48).min()) / informative_1h['close'].rolling(48).min() < self.entry_pump_threshold_3.value) | ((informative_1h['open'].rolling(48).max() - informative_1h['close'].rolling(48).min()) / self.entry_pump_pull_threshold_3.value > informative_1h['close'] - informative_1h['close'].rolling(48).min())
        informative_1h['safe_pump_24_strict'] = ((informative_1h['open'].rolling(24).max() - informative_1h['close'].rolling(24).min()) / informative_1h['close'].rolling(24).min() < self.entry_pump_threshold_4.value) | ((informative_1h['open'].rolling(24).max() - informative_1h['close'].rolling(24).min()) / self.entry_pump_pull_threshold_4.value > informative_1h['close'] - informative_1h['close'].rolling(24).min())
        informative_1h['safe_pump_36_strict'] = ((informative_1h['open'].rolling(36).max() - informative_1h['close'].rolling(36).min()) / informative_1h['close'].rolling(36).min() < self.entry_pump_threshold_5.value) | ((informative_1h['open'].rolling(36).max() - informative_1h['close'].rolling(36).min()) / self.entry_pump_pull_threshold_5.value > informative_1h['close'] - informative_1h['close'].rolling(36).min())
        informative_1h['safe_pump_48_strict'] = ((informative_1h['open'].rolling(48).max() - informative_1h['close'].rolling(48).min()) / informative_1h['close'].rolling(48).min() < self.entry_pump_threshold_6.value) | ((informative_1h['open'].rolling(48).max() - informative_1h['close'].rolling(48).min()) / self.entry_pump_pull_threshold_6.value > informative_1h['close'] - informative_1h['close'].rolling(48).min())
        return informative_1h

    def normal_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # BB 40
        bb_40 = qtpylib.bollinger_bands(dataframe['close'], window=40, stds=2)
        dataframe['lower'] = bb_40['lower']
        dataframe['mid'] = bb_40['mid']
        dataframe['bbdelta'] = (bb_40['mid'] - dataframe['lower']).abs()
        dataframe['closedelta'] = (dataframe['close'] - dataframe['close'].shift()).abs()
        dataframe['tail'] = (dataframe['close'] - dataframe['low']).abs()
        # BB 20
        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']
        # EMA 200
        dataframe['ema_12'] = ta.EMA(dataframe, timeperiod=12)
        dataframe['ema_20'] = ta.EMA(dataframe, timeperiod=20)
        dataframe['ema_26'] = ta.EMA(dataframe, timeperiod=26)
        dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50)
        dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100)
        dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)
        # SMA
        dataframe['sma_5'] = ta.SMA(dataframe, timeperiod=5)
        dataframe['sma_30'] = ta.SMA(dataframe, timeperiod=30)
        dataframe['sma_200'] = ta.SMA(dataframe, timeperiod=200)
        dataframe['sma_200_dec'] = dataframe['sma_200'] < dataframe['sma_200'].shift(20)
        # MFI
        dataframe['mfi'] = ta.MFI(dataframe)
        # EWO
        dataframe['ewo'] = EWO(dataframe, 50, 200)
        # Alligator
        dataframe['lips'] = ta.SMA(dataframe, timeperiod=5)
        dataframe['smma_lips'] = dataframe['lips'].rolling(3).mean()
        dataframe['teeth'] = ta.SMA(dataframe, timeperiod=8)
        dataframe['smma_teeth'] = dataframe['teeth'].rolling(5).mean()
        dataframe['jaw'] = ta.SMA(dataframe, timeperiod=13)
        dataframe['smma_jaw'] = dataframe['jaw'].rolling(8).mean()
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        # Dip protection
        dataframe['safe_dips'] = ((dataframe['open'] - dataframe['close']) / dataframe['close'] < self.entry_dip_threshold_1.value) & ((dataframe['open'].rolling(2).max() - dataframe['close']) / dataframe['close'] < self.entry_dip_threshold_2.value) & ((dataframe['open'].rolling(12).max() - dataframe['close']) / dataframe['close'] < self.entry_dip_threshold_3.value) & ((dataframe['open'].rolling(144).max() - dataframe['close']) / dataframe['close'] < self.entry_dip_threshold_4.value)
        dataframe['safe_dips_strict'] = ((dataframe['open'] - dataframe['close']) / dataframe['close'] < self.entry_dip_threshold_5.value) & ((dataframe['open'].rolling(2).max() - dataframe['close']) / dataframe['close'] < self.entry_dip_threshold_6.value) & ((dataframe['open'].rolling(12).max() - dataframe['close']) / dataframe['close'] < self.entry_dip_threshold_7.value) & ((dataframe['open'].rolling(144).max() - dataframe['close']) / dataframe['close'] < self.entry_dip_threshold_8.value)
        # Volume
        dataframe['volume_mean_4'] = dataframe['volume'].rolling(4).mean().shift(1)
        dataframe['volume_mean_30'] = dataframe['volume'].rolling(30).mean()
        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)
        # Populate/update the trade data if there is any, set trades to false if not live/dry
        self.custom_trade_info[metadata['pair']] = self.populate_trades(metadata['pair'])
        if self.config['runmode'].value in ('backtest', 'hyperopt'):
            assert timeframe_to_minutes(self.timeframe) <= 30, 'Backtest this strategy in 5m or 1m timeframe.'
        if self.timeframe == self.inf_1h:
            dataframe = self.do_indicators(dataframe, metadata)
        else:
            if not self.dp:
                return dataframe
            informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_1h)
            informative = self.do_indicators(informative.copy(), metadata)
            dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_1h, ffill=True)
            skip_columns = [s + '_' + self.inf_1h for s in ['date', 'open', 'high', 'low', 'close', 'volume', 'emac', 'emao']]
            dataframe.rename(columns=lambda s: s.replace('_{}'.format(self.inf_1h), '') if not s in skip_columns else s, inplace=True)
        # Slam some indicators into the trade_info dict so we can dynamic roi and custom stoploss in backtest
        if self.dp.runmode.value in ('backtest', 'hyperopt'):
            self.custom_trade_info[metadata['pair']]['roc'] = dataframe[['date', 'roc']].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['atr'] = dataframe[['date', 'atr']].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['sroc'] = dataframe[['date', 'sroc']].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['ssl-dir'] = dataframe[['date', 'ssl-dir']].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['rmi-up-trend'] = dataframe[['date', 'rmi-up-trend']].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['candle-up-trend'] = dataframe[['date', 'candle-up-trend']].copy().set_index('date')
        dataframe = merge_informative_pair(dataframe, informative_1h, self.timeframe, self.inf_1h, ffill=True)
        # The indicators for the normal (5m) timeframe
        dataframe = self.normal_tf_indicators(dataframe, metadata)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        conditions.append(self.entry_condition_1_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & (dataframe['sma_200'] > dataframe['sma_200'].shift(20)) & dataframe['safe_dips'] & dataframe['safe_pump_48_1h'] & ((dataframe['close'] - dataframe['open'].rolling(36).min()) / dataframe['open'].rolling(36).min() > self.entry_min_inc_1.value) & (dataframe['rsi_1h'] > self.entry_rsi_1h_min_1.value) & (dataframe['rsi_1h'] < self.entry_rsi_1h_max_1.value) & (dataframe['rsi'] < self.entry_rsi_1.value) & (dataframe['mfi'] < self.entry_mfi_1.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_2_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & dataframe['safe_pump_24_strict_1h'] & (dataframe['volume_mean_4'] * self.entry_volume_2.value > dataframe['volume']) & (dataframe['rsi_1h'] > self.entry_rsi_1h_min_2.value) & (dataframe['rsi_1h'] < self.entry_rsi_1h_max_2.value) & (dataframe['rsi'] < dataframe['rsi_1h'] - self.entry_rsi_1h_diff_2.value) & (dataframe['mfi'] < self.entry_mfi_2.value) & (dataframe['close'] < dataframe['bb_lowerband'] * self.entry_bb_offset_2.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_3_enable.value & (dataframe['close'] > dataframe['ema_200_1h'] * self.entry_ema_rel_3.value) & (dataframe['ema_100'] > dataframe['ema_200']) & (dataframe['ema_50_1h'] > dataframe['ema_100_1h']) & (dataframe['ema_100_1h'] > dataframe['ema_200_1h']) & dataframe['safe_pump_36_1h'] & dataframe['lower'].shift().gt(0) & dataframe['bbdelta'].gt(dataframe['close'] * self.entry_bb40_bbdelta_close_3.value) & dataframe['closedelta'].gt(dataframe['close'] * self.entry_bb40_closedelta_close_3.value) & dataframe['tail'].lt(dataframe['bbdelta'] * self.entry_bb40_tail_bbdelta_3.value) & dataframe['close'].lt(dataframe['lower'].shift()) & dataframe['close'].le(dataframe['close'].shift()) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_4_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & dataframe['safe_dips_strict'] & dataframe['safe_pump_24_1h'] & (dataframe['close'] < dataframe['ema_50']) & (dataframe['close'] < self.entry_bb20_close_bblowerband_4.value * dataframe['bb_lowerband']) & (dataframe['volume'] < dataframe['volume_mean_30'].shift(1) * self.entry_bb20_volume_4.value))
        conditions.append(self.entry_condition_5_enable.value & (dataframe['ema_100'] > dataframe['ema_200']) & (dataframe['close'] > dataframe['ema_200_1h'] * self.entry_ema_rel_5.value) & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & dataframe['safe_dips'] & dataframe['safe_pump_36_strict_1h'] & (dataframe['volume_mean_4'] * self.entry_volume_5.value > dataframe['volume']) & (dataframe['ema_26'] > dataframe['ema_12']) & (dataframe['ema_26'] - dataframe['ema_12'] > dataframe['open'] * self.entry_ema_open_mult_5.value) & (dataframe['ema_26'].shift() - dataframe['ema_12'].shift() > dataframe['open'] / 100) & (dataframe['close'] < dataframe['bb_lowerband'] * self.entry_bb_offset_5.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_6_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & dataframe['safe_dips_strict'] & (dataframe['volume'].rolling(4).mean() * self.entry_volume_6.value > dataframe['volume']) & (dataframe['ema_26'] > dataframe['ema_12']) & (dataframe['ema_26'] - dataframe['ema_12'] > dataframe['open'] * self.entry_ema_open_mult_6.value) & (dataframe['ema_26'].shift() - dataframe['ema_12'].shift() > dataframe['open'] / 100) & (dataframe['close'] < dataframe['bb_lowerband'] * self.entry_bb_offset_6.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_7_enable.value & (dataframe['ema_100'] > dataframe['ema_200']) & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & dataframe['safe_dips'] & (dataframe['volume'].rolling(4).mean() * self.entry_volume_6.value > dataframe['volume']) & (dataframe['ema_26'] > dataframe['ema_12']) & (dataframe['ema_26'] - dataframe['ema_12'] > dataframe['open'] * self.entry_ema_open_mult_7.value) & (dataframe['ema_26'].shift() - dataframe['ema_12'].shift() > dataframe['open'] / 100) & (dataframe['rsi'] < self.entry_rsi_7.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_8_enable.value & (dataframe['close'] > dataframe['ema_200_1h'] * self.entry_ema_rel_8.value) & (dataframe['ema_50_1h'] > dataframe['ema_100_1h']) & (dataframe['sma_200_1h'] > dataframe['sma_200_1h'].shift(24)) & (dataframe['close'] > dataframe['open']) & (dataframe['close'] > dataframe['smma_lips']) & (dataframe['smma_lips'] > dataframe['smma_teeth']) & (dataframe['smma_teeth'] > dataframe['smma_jaw']) & (dataframe['smma_lips'].shift(1) > dataframe['smma_teeth'].shift(1)) & (dataframe['smma_teeth'].shift(1) > dataframe['smma_jaw'].shift(1)) & (dataframe['smma_lips'] > dataframe['smma_lips'].shift(1)) & (dataframe['smma_teeth'] > dataframe['smma_teeth'].shift(1)) & (dataframe['smma_jaw'] > dataframe['smma_jaw'].shift(1)) & (dataframe['rsi'] < self.entry_rsi_8.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_9_enable.value & (dataframe['ema_50'] > dataframe['ema_200']) & (dataframe['ema_50_1h'] > dataframe['ema_100_1h']) & dataframe['safe_dips_strict'] & (dataframe['volume_mean_4'] * self.entry_volume_9.value > dataframe['volume']) & (dataframe['close'] < dataframe['sma_30'] * self.entry_ma_offset_9.value) & (dataframe['close'] < dataframe['bb_lowerband'] * self.entry_bb_offset_9.value) & (dataframe['rsi_1h'] > self.entry_rsi_1h_min_9.value) & (dataframe['rsi_1h'] < self.entry_rsi_1h_max_9.value) & (dataframe['mfi'] < self.entry_mfi_9.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_10_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_100_1h']) & (dataframe['sma_200_1h'] > dataframe['sma_200_1h'].shift(24)) & dataframe['safe_dips'] & dataframe['safe_pump_24_1h'] & (dataframe['volume_mean_4'] * self.entry_volume_10.value > dataframe['volume']) & (dataframe['close'] < dataframe['sma_30'] * self.entry_ma_offset_10.value) & (dataframe['close'] < dataframe['bb_lowerband'] * self.entry_bb_offset_10.value) & (dataframe['rsi_1h'] < self.entry_rsi_1h_10.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_11_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_100_1h']) & dataframe['safe_pump_24_1h'] & ((dataframe['close'] - dataframe['open'].rolling(36).min()) / dataframe['open'].rolling(36).min() > self.entry_min_inc_11.value) & (dataframe['close'] < dataframe['sma_30'] * self.entry_ma_offset_11.value) & (dataframe['rsi_1h'] > self.entry_rsi_1h_min_11.value) & (dataframe['rsi_1h'] < self.entry_rsi_1h_max_11.value) & (dataframe['rsi'] < self.entry_rsi_11.value) & (dataframe['mfi'] < self.entry_mfi_11.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_12_enable.value & (dataframe['sma_200_1h'] > dataframe['sma_200_1h'].shift(24)) & dataframe['safe_dips_strict'] & dataframe['safe_pump_24_strict_1h'] & (dataframe['volume_mean_4'] * self.entry_volume_12.value > dataframe['volume']) & (dataframe['close'] < dataframe['sma_30'] * self.entry_ma_offset_12.value) & (dataframe['ewo'] > self.entry_ewo_12.value) & (dataframe['rsi'] < self.entry_rsi_12.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_13_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_100_1h']) & (dataframe['sma_200_1h'] > dataframe['sma_200_1h'].shift(24)) & dataframe['safe_dips_strict'] & dataframe['safe_pump_24_strict_1h'] & (dataframe['close'] < dataframe['sma_30'] * self.entry_ma_offset_13.value) & (dataframe['ewo'] < self.entry_ewo_13.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_14_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & (dataframe['sma_200'] > dataframe['sma_200'].shift(20)) & dataframe['safe_dips_strict'] & dataframe['safe_pump_48_1h'] & (dataframe['volume_mean_4'] * self.entry_volume_14.value > dataframe['volume']) & (dataframe['ema_26'] > dataframe['ema_12']) & (dataframe['ema_26'] - dataframe['ema_12'] > dataframe['open'] * self.entry_ema_open_mult_14.value) & (dataframe['ema_26'].shift() - dataframe['ema_12'].shift() > dataframe['open'] / 100) & (dataframe['close'] < dataframe['bb_lowerband'] * self.entry_bb_offset_14.value) & (dataframe['close'] < dataframe['sma_30'] * self.entry_ma_offset_14.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_15_enable.value & (dataframe['close'] > dataframe['ema_200_1h'] * self.entry_ema_rel_15.value) & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & dataframe['safe_dips_strict'] & (dataframe['ema_26'] > dataframe['ema_12']) & (dataframe['ema_26'] - dataframe['ema_12'] > dataframe['open'] * self.entry_ema_open_mult_15.value) & (dataframe['ema_26'].shift() - dataframe['ema_12'].shift() > dataframe['open'] / 100) & (dataframe['rsi'] < self.entry_rsi_15.value) & (dataframe['close'] < dataframe['sma_30'] * self.entry_ma_offset_15.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_16_enable.value & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & dataframe['safe_dips_strict'] & dataframe['safe_pump_24_strict_1h'] & (dataframe['volume_mean_4'] * self.entry_volume_16.value > dataframe['volume']) & (dataframe['close'] < dataframe['ema_20'] * self.entry_ma_offset_16.value) & (dataframe['ewo'] > self.entry_ewo_16.value) & (dataframe['rsi'] < self.entry_rsi_16.value) & (dataframe['volume'] > 0))
        conditions.append(self.entry_condition_17_enable.value & dataframe['safe_dips_strict'] & (dataframe['close'] < dataframe['ema_20'] * self.entry_ma_offset_17.value) & (dataframe['ewo'] < self.entry_ewo_17.value) & (dataframe['volume'] > 0))
        if conditions:
            dataframe.loc[reduce(lambda x, y: x | y, conditions), 'entry'] = 1
        ## close ALWAYS needs to be lower than the heiken low at 5m
        ## Hansen's HA EMA at informative timeframe
        ## potential uptick incoming so entry
        # this tries to find extra entrys in undersold regions
        # find smaller temporary dips in sideways
        ## if nothing else is making a entry signal
        ## just throw in any old SQZMI shit based fastd
        ## this needs work!
        ## volume sanity checks
        dataframe.loc[(dataframe['close'] < dataframe['Smooth_HA_L']) & (dataframe['emac_1h'] < dataframe['emao_1h']) & ((dataframe['bbw_expansion'] == 1) & (dataframe['sqzmi'] == False) & ((dataframe['mfi'] < 20) | (dataframe['dmi_minus'] > 30)) | (dataframe['close'] < dataframe['sar']) & ((dataframe['srsi_d'] >= dataframe['srsi_k']) & (dataframe['srsi_d'] < 30)) & ((dataframe['fastd'] > dataframe['fastk']) & (dataframe['fastd'] < 23)) & (dataframe['mfi'] < 30) | ((dataframe['dmi_minus'] > 30) & qtpylib.crossed_above(dataframe['dmi_minus'], dataframe['dmi_plus']) & (dataframe['close'] < dataframe['bb_lowerband']) | (dataframe['sqzmi'] == True) & ((dataframe['fastd'] > dataframe['fastk']) & (dataframe['fastd'] < 20))) & (dataframe['vfi'] < 0.0) & (dataframe['volume'] > 0)), 'entry'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        conditions.append(self.exit_condition_1_enable.value & (dataframe['rsi'] > self.exit_rsi_bb_1.value) & (dataframe['close'] > dataframe['bb_upperband']) & (dataframe['close'].shift(1) > dataframe['bb_upperband'].shift(1)) & (dataframe['close'].shift(2) > dataframe['bb_upperband'].shift(2)) & (dataframe['close'].shift(3) > dataframe['bb_upperband'].shift(3)) & (dataframe['close'].shift(4) > dataframe['bb_upperband'].shift(4)) & (dataframe['close'].shift(5) > dataframe['bb_upperband'].shift(5)) & (dataframe['volume'] > 0))
        conditions.append(self.exit_condition_2_enable.value & (dataframe['rsi'] > self.exit_rsi_bb_2.value) & (dataframe['close'] > dataframe['bb_upperband']) & (dataframe['close'].shift(1) > dataframe['bb_upperband'].shift(1)) & (dataframe['close'].shift(2) > dataframe['bb_upperband'].shift(2)) & (dataframe['volume'] > 0))
        conditions.append(self.exit_condition_3_enable.value & (dataframe['rsi'] > self.exit_rsi_main_3.value) & (dataframe['volume'] > 0))
        conditions.append(self.exit_condition_4_enable.value & (dataframe['rsi'] > self.exit_dual_rsi_rsi_4.value) & (dataframe['rsi_1h'] > self.exit_dual_rsi_rsi_1h_4.value) & (dataframe['volume'] > 0))
        conditions.append(self.exit_condition_5_enable.value & (dataframe['close'] < dataframe['ema_200']) & ((dataframe['ema_200'] - dataframe['close']) / dataframe['close'] < self.exit_ema_relative_5.value) & (dataframe['rsi'] > dataframe['rsi_1h'] + self.exit_rsi_diff_5.value) & (dataframe['volume'] > 0))
        conditions.append(self.exit_condition_6_enable.value & (dataframe['close'] < dataframe['ema_200']) & (dataframe['close'] > dataframe['ema_50']) & (dataframe['rsi'] > self.exit_rsi_under_6.value) & (dataframe['volume'] > 0))
        conditions.append(self.exit_condition_7_enable.value & (dataframe['rsi_1h'] > self.exit_rsi_1h_7.value) & qtpylib.crossed_below(dataframe['ema_12'], dataframe['ema_26']) & (dataframe['volume'] > 0))
        conditions.append(self.exit_condition_8_enable.value & (dataframe['close'] > dataframe['bb_upperband_1h'] * self.exit_bb_relative_8.value) & (dataframe['volume'] > 0))
        if conditions:
            dataframe.loc[reduce(lambda x, y: x | y, conditions), 'exit'] = 1
        ## close ALWAYS needs to be higher than the heiken high at 5m
        ## Hansen's HA EMA at informative timeframe
        ## try to find oversold regions with a corresponding BB expansion
        ## volume sanity checks
        dataframe.loc[(dataframe['close'] > dataframe['Smooth_HA_H']) & (dataframe['emac_1h'] > dataframe['emao_1h']) & ((dataframe['bbw_expansion'] == 1) & ((dataframe['mfi'] > 80) | (dataframe['dmi_plus'] > 30)) & (dataframe['vfi'] > 0.0) & (dataframe['volume'] > 0)), 'exit'] = 1
        return dataframe
    '\n    Everything from here completely stolen from the godly work of @werkkrew\n    \n    Custom Stoploss \n    '

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
        trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)
        if self.config['runmode'].value in ('live', 'dry_run'):
            dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
            sroc = dataframe['sroc'].iat[-1]
        else:
            # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
            sroc = self.custom_trade_info[trade.pair]['sroc'].loc[current_time]['sroc']
        if current_profit < self.cstp_threshold.value:
            if self.cstp_bail_how.value == 'roc' or self.cstp_bail_how.value == 'any':
                # Dynamic bailout based on rate of change
                if sroc / 100 <= self.cstp_bail_roc.value:
                    return 0.001
            if self.cstp_bail_how.value == 'time' or self.cstp_bail_how.value == 'any':
                # Dynamic bailout based on time
                if trade_dur > self.cstp_bail_time.value:
                    return 0.001
        return 1
    '\n    Freqtrade ROI Overload for dynamic ROI functionality\n    '

    def min_roi_reached_dynamic(self, trade: Trade, current_profit: float, current_time: datetime, trade_dur: int) -> Tuple[Optional[int], Optional[float]]:
        minimal_roi = self.minimal_roi
        _, table_roi = self.min_roi_reached_entry(trade_dur)
        # see if we have the data we need to do this, otherwise fall back to the standard table
        if self.custom_trade_info and trade and (trade.pair in self.custom_trade_info):
            if self.config['runmode'].value in ('live', 'dry_run'):
                dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=trade.pair, timeframe=self.timeframe)
                rmi_trend = dataframe['rmi-up-trend'].iat[-1]
                candle_trend = dataframe['candle-up-trend'].iat[-1]
                ssl_dir = dataframe['ssl-dir'].iat[-1]
            else:
                # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
                rmi_trend = self.custom_trade_info[trade.pair]['rmi-up-trend'].loc[current_time]['rmi-up-trend']
                candle_trend = self.custom_trade_info[trade.pair]['candle-up-trend'].loc[current_time]['candle-up-trend']
                ssl_dir = self.custom_trade_info[trade.pair]['ssl-dir'].loc[current_time]['ssl-dir']
            min_roi = table_roi
            max_profit = trade.calc_profit_ratio(trade.max_rate)
            pullback_value = max_profit - self.droi_pullback_amount.value
            in_trend = False
            if self.droi_trend_type.value == 'rmi' or self.droi_trend_type.value == 'any':
                if rmi_trend == 1:
                    in_trend = True
            if self.droi_trend_type.value == 'ssl' or self.droi_trend_type.value == 'any':
                if ssl_dir == 'up':
                    in_trend = True
            if self.droi_trend_type.value == 'candle' or self.droi_trend_type.value == 'any':
                if candle_trend == 1:
                    in_trend = True
            # Force the ROI value high if in trend
            if in_trend == True:
                min_roi = 100
                # If pullback is enabled, allow to exit if a pullback from peak has happened regardless of trend
                if self.droi_pullback.value == True and current_profit < pullback_value:
                    if self.droi_pullback_respect_table.value == True:
                        min_roi = table_roi
                    else:
                        min_roi = current_profit / 2
        else:
            min_roi = table_roi
        return (trade_dur, min_roi)
    # Change here to allow loading of the dynamic_roi settings

    def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool:
        trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)
        if self.use_dynamic_roi:
            _, roi = self.min_roi_reached_dynamic(trade, current_profit, current_time, trade_dur)
        else:
            _, roi = self.min_roi_reached_entry(trade_dur)
        if roi is None:
            return False
        else:
            return current_profit > roi
    # Get the current price from the exchange (or local cache)

    def get_current_price(self, pair: str, refresh: bool) -> float:
        if not refresh:
            rate = self.custom_current_price_cache.get(pair)
            # Check if cache has been invalidated
            if rate:
                return rate
        ask_strategy = self.config.get('ask_strategy', {})
        if ask_strategy.get('use_order_book', False):
            ob = self.dp.orderbook(pair, 1)
            rate = ob[f"{ask_strategy['price_side']}s"][0][0]
        else:
            ticker = self.dp.ticker(pair)
            rate = ticker['last']
        self.custom_current_price_cache[pair] = rate
        return rate
    '\n    Stripped down version from Schism, meant only to update the price data a bit\n    more frequently than the default instead of getting all sorts of trade information\n    '

    def populate_trades(self, pair: str) -> dict:
        # Initialize the trades dict if it doesn't exist, persist it otherwise
        if not pair in self.custom_trade_info:
            self.custom_trade_info[pair] = {}
        # init the temp dicts and set the trade stuff to false
        trade_data = {}
        trade_data['active_trade'] = False
        # active trade stuff only works in live and dry, not backtest
        if self.config['runmode'].value in ('live', 'dry_run'):
            # find out if we have an open trade for this pair
            active_trade = Trade.get_trades([Trade.pair == pair, Trade.is_open.is_(True)]).all()
            # if so, get some information
            if active_trade:
                # get current price and update the min/max rate
                current_rate = self.get_current_price(pair, True)
                active_trade[0].adjust_min_max_rates(current_rate)
        return trade_data
# Elliot Wave Oscillator

def EWO(dataframe, sma1_length=5, sma2_length=35):
    df = dataframe.copy()
    sma1 = ta.EMA(df, timeperiod=sma1_length)
    sma2 = ta.EMA(df, timeperiod=sma2_length)
    smadif = (sma1 - sma2) / df['close'] * 100
    return smadif

def RMI(dataframe, *, length=20, mom=5):
    """
    Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/indicators.py#L912
    """
    df = dataframe.copy()
    df['maxup'] = (df['close'] - df['close'].shift(mom)).clip(lower=0)
    df['maxdown'] = (df['close'].shift(mom) - df['close']).clip(lower=0)
    df.fillna(0, inplace=True)
    df['emaInc'] = ta.EMA(df, price='maxup', timeperiod=length)
    df['emaDec'] = ta.EMA(df, price='maxdown', timeperiod=length)
    df['RMI'] = np.where(df['emaDec'] == 0, 0, 100 - 100 / (1 + df['emaInc'] / df['emaDec']))
    return df['RMI']

def SSLChannels_ATR(dataframe, length=7):
    """
    SSL Channels with ATR: https://www.tradingview.com/script/SKHqWzql-SSL-ATR-channel/
    Credit to @JimmyNixx for python
    """
    df = dataframe.copy()
    df['ATR'] = ta.ATR(df, timeperiod=14)
    df['smaHigh'] = df['high'].rolling(length).mean() + df['ATR']
    df['smaLow'] = df['low'].rolling(length).mean() - df['ATR']
    df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, np.where(df['close'] < df['smaLow'], -1, np.NAN))
    df['hlv'] = df['hlv'].ffill()
    df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow'])
    df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh'])
    return (df['sslDown'], df['sslUp'])

def SROC(dataframe, roclen=21, emalen=13, smooth=21):
    df = dataframe.copy()
    roc = ta.ROC(df, timeperiod=roclen)
    ema = ta.EMA(df, timeperiod=emalen)
    sroc = ta.ROC(ema, timeperiod=smooth)
    return sroc