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) DUALFIT = False COUNT = 10 GAP = 3 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) class IchiV1_Marius_fixed_2(IStrategy): 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_params = { "max_slip": 0.668, "antipump_threshold": 0.265, "antipump_threshold_2": 0.133, "buy_btc_safe_1d": -0.236, "buy_btc_safe": -213, "buy_threshold": 0.012, "pump_limit": 1000, "pump_pause_duration": 192, "pump_period": 14, "pump_recorver_price": 1.1, "buy_trend_above_senkou_level": 1, "buy_trend_bullish_level": 6, "tesla_enabled": True, "buy_min_fan_magnitude_gain": 1.0022, # NOTE: Good value (Win% ~70%), alot of trades } sell_params = { "ProfitLoss1": 0.005, "ProfitLoss2": 0.021, "ProfitMargin1": 0.018, "ProfitMargin2": 0.051, "pHSL": -0.08, "sell_trend_indicator": "trend_close_2h", } buy_minimum_conditions = IntParameter( 1, 2, default=1, space="buy", 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_buys = trade.select_filled_orders('buy') count_of_buys = len(filled_buys) if 0 < count_of_buys <= self.max_dca_orders: try: stake_amount = filled_buys[0].cost stake_amount = stake_amount * self.dca_stake_multiplier return stake_amount except Exception as exception: return None return None pump_period = IntParameter( 5, 24, default=buy_params['pump_period'], space='buy', optimize=False) pump_limit = IntParameter( 100,10000, default=buy_params['pump_limit'], space='buy', optimize=True) pump_recorver_price = DecimalParameter( 1.0, 1.3, default=buy_params['pump_recorver_price'], space='buy', optimize=True) pump_pause_duration = IntParameter( 6, 500, default=buy_params['pump_pause_duration'], space='buy', optimize=True) is_optimize_slip = False max_slip = DecimalParameter(0.33, 0.80, default=0.33, decimals=3, optimize=is_optimize_slip , load=True) buy_btc_safe = IntParameter(-300, 50, default=buy_params['buy_btc_safe'], optimize = True) buy_btc_safe_1d = DecimalParameter(-0.5, -0.015, default=buy_params['buy_btc_safe_1d'], optimize=True) antipump_threshold = DecimalParameter(0, 0.4, default=buy_params['antipump_threshold'], space='buy', optimize=True) antipump_threshold_2 = DecimalParameter(0, 0.4, default=buy_params['antipump_threshold_2'], space='buy', optimize=True) buy_min_fan_magnitude_gain = DecimalParameter(70, 90, default=buy_params['buy_min_fan_magnitude_gain'], space='buy', optimize=False, load=True) # Multi Offset buy_threshold = DecimalParameter(0.003, 0.012, default=buy_params['buy_threshold'], optimize=True) minimal_roi = { "0": 0.215, "40": 0.032, "87": 0.016, "201": 0 } stoploss = -0.275 # value loaded from strategy trailing_stop = False window_buy = IntParameter(60, 1000, default=500, space='buy', optimize=True) bandwidth_buy = IntParameter(2, 15, default=8, space='buy', optimize=True) mult_buy = DecimalParameter(0.5, 20.0, default=3, space='buy', optimize=True) order_time_in_force = { 'buy': 'gtc', 'sell': 'gtc' } use_custom_stoploss = True timeframe = '5m' informative_timeframe = '1h' inf_15m = '15m' #use for pump detection timeframe_minutes = timeframe_to_minutes(timeframe) custom_info = {} startup_candle_count: int = 499 process_only_new_candles = True timeperiods = [ 180 // timeframe_minutes, 360 // timeframe_minutes, 420 // timeframe_minutes, 560 // timeframe_minutes, ] use_sell_signal = False sell_profit_only = False ignore_roi_if_buy_signal = True pHSL = DecimalParameter(-0.15, -0.08, default=sell_params['pHSL'], decimals=3, space='sell', optimize=True) ProfitMargin1 = DecimalParameter(0.009, 0.019, default=sell_params['ProfitMargin1'], decimals=3, space='sell', optimize=True) ProfitLoss1 = DecimalParameter(0.005, 0.012, default=sell_params['ProfitLoss1'], decimals=3, space='sell', optimize=True) ProfitMargin2 = DecimalParameter(0.033, 0.099, default=sell_params['ProfitMargin2'], decimals=3, space='sell', optimize=True) ProfitLoss2 = DecimalParameter(0.010, 0.025, default=sell_params['ProfitLoss2'], decimals=3, space='sell', optimize=True) plot_config = { 'main_plot': { 'senkou_a': { 'color': 'green', #optional 'fill_to': 'senkou_b', 'fill_label': 'Ichimoku Cloud', #optional 'fill_color': 'rgba(255,76,46,0.2)', #optional }, 'senkou_b': {}, }, 'subplots': { 'fan_magnitude': { 'fan_magnitude': {} }, 'fan_magnitude_gain': { 'fan_magnitude_gain': {} } } } slippage_protection = { 'retries': 3, 'max_slippage': -0.02 } def custom_sell(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, sell_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.buy_tag and current_profit > 0.01: return True if (trade.buy_tag == 'telsa_'): if (sell_reason in ['sell_signal'])or (sell_reason in ['roi']) or (sell_reason in ['trailing_stop_loss']): return False if (last_candle is not None): if (sell_reason in ['sell_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 (sell_reason in ['sell_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 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): pairs = self.dp.current_whitelist() 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/BUSD", "1m")] informative_pairs += [("BTC/BUSD", "5m")] informative_pairs += [("BTC/BUSD", "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." informative_1h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.informative_timeframe) informative_1h['rsi'] = ta.RSI(informative_1h, timeperiod=14) informative_1h['weekly_close_avg'] = informative_1h['close'].rolling(168).mean() return informative_1h def informative_15m_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: assert self.dp, "DataProvider is required for multiple timeframes." 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: 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/BUSD" 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) dataframe['btc_5m']= self.dp.get_pair_dataframe('BTC/BUSD', timeframe='5m')['close'] btc_1d = self.dp.get_pair_dataframe('BTC/BUSD', timeframe='1d')[['date', 'close']].rename(columns={"close": "btc"}).shift(1) dataframe = merge_informative_pair(dataframe, btc_1d, '5m', '1d', ffill=True) informative = self.dp.get_pair_dataframe(pair="BTC/BUSD", timeframe="5m") informative_past = informative.copy().shift(1) 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.buy_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 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) 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) 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) dataframe['hk_open'] = heikinashi['open'] dataframe['hk_high'] = heikinashi['high'] dataframe['hk_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['hk_open'] dataframe['trend_open_15m'] = ta.EMA(dataframe['hk_open'], timeperiod=3) dataframe['trend_open_30m'] = ta.EMA(dataframe['hk_open'], timeperiod=6) dataframe['trend_open_1h'] = ta.EMA(dataframe['hk_open'], timeperiod=12) dataframe['trend_open_2h'] = ta.EMA(dataframe['hk_open'], timeperiod=24) dataframe['trend_open_4h'] = ta.EMA(dataframe['hk_open'], timeperiod=48) dataframe['trend_open_6h'] = ta.EMA(dataframe['hk_open'], timeperiod=72) dataframe['trend_open_8h'] = ta.EMA(dataframe['hk_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['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) dataframe['mfi'] = ta.MFI(dataframe) return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: informative_1h = self.informative_1h_indicators(dataframe, metadata) dataframe = merge_informative_pair(dataframe, informative_1h, self.timeframe, self.informative_timeframe, ffill=True) dataframe = self.normal_tf_indicators(dataframe, metadata) 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) 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 dataframe['pump_dump_alert'] = dataframe['price_test'] & dataframe['pump_price_test'] dataframe['buy_ok'] = np.where(dataframe['pump_dump_alert'], False, True) return dataframe 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_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] dataframe.loc[:, 'buy_tag'] = '' is_protection = ( (pct_change(dataframe['btc_1d'], dataframe['btc_5m']).fillna(0) > self.buy_btc_safe_1d.value) & (dataframe['pump_strength_2'] < self.antipump_threshold_2.value)& (dataframe['buy_ok'])& (dataframe['volume'] > 0) ) if self.buy_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.buy_min_fan_magnitude_gain.value) & (dataframe['fan_magnitude'] > 0.99) ) conditions.append(tesla) dataframe.loc[tesla, 'buy_tag'] += 'tesla_' if conditions: dataframe.loc[ is_protection & reduce(lambda x, y: x & y, conditions), 'buy'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] conditions.append(qtpylib.crossed_below(dataframe['trend_close_5m'], dataframe[self.sell_params['sell_trend_indicator']])) if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), 'sell'] = 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 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']