import freqtrade.vendor.qtpylib.indicators as qtpylib import talib.abstract as ta import pandas_ta as pta import pandas as pd from freqtrade.persistence import Trade from freqtrade.strategy.interface import IStrategy from pandas import DataFrame, Series from datetime import datetime from freqtrade.strategy import DecimalParameter, IntParameter, informative, stoploss_from_open from functools import reduce # import warnings # warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning) # custom indicators # ################################################################################################## def ewo(dataframe, ema_length=5, ema2_length=35): df = dataframe.copy() ema1 = ta.EMA(df, timeperiod=ema_length) ema2 = ta.EMA(df, timeperiod=ema2_length) emadif = (ema1 - ema2) / df['low'] * 100 return emadif # Williams %R def williams_r(dataframe: DataFrame, period: int = 14) -> Series: highest_high = dataframe["high"].rolling(center=False, window=period).max() lowest_low = dataframe["low"].rolling(center=False, window=period).min() wr = Series( (highest_high - dataframe["close"]) / (highest_high - lowest_low), name=f"{period} Williams %R", ) return wr * -100 # ##################################################################################################### class BBModCE(IStrategy): minimal_roi = { "0": 100 } # Optimal timeframe for the strategy timeframe = '5m' # Run "populate_indicators()" only for new candle. process_only_new_candles = True startup_candle_count = 20 order_types = { 'entry': 'market', 'exit': 'market', 'emergency_exit': 'market', 'force_entry': 'market', 'force_exit': "market", 'stoploss': 'market', 'stoploss_on_exchange': False, 'stoploss_on_exchange_interval': 60, 'stoploss_on_exchange_market_ratio': 0.99 } # Disabled stoploss = -0.99 # Custom stoploss use_custom_stoploss = True # Buy params leverage_optimize = False leverage_num = IntParameter(low=1, high=3, default=3, space='buy', optimize=leverage_optimize) is_optimize_ewo = True buy_rsi_fast = IntParameter(35, 50, default=45, space='buy', optimize=is_optimize_ewo) buy_rsi = IntParameter(15, 35, default=35, space='buy', optimize=is_optimize_ewo) buy_ewo = DecimalParameter(-6.0, 5, default=-5.585, space='buy', optimize=is_optimize_ewo) buy_ema_low = DecimalParameter(0.9, 0.99, default=0.942, space='buy', optimize=is_optimize_ewo) buy_ema_high = DecimalParameter(0.95, 1.2, default=1.084, space='buy', optimize=is_optimize_ewo) is_optimize_cofi = True buy_roc_1h = IntParameter(-25, 200, default=10, space='buy', optimize=is_optimize_cofi) buy_bb_width_1h = DecimalParameter(0.3, 2.0, default=0.3, space='buy', optimize=is_optimize_cofi) buy_ema_cofi = DecimalParameter(0.94, 1.2, default=0.97, space='buy', optimize=is_optimize_cofi) buy_fastk = IntParameter(0, 40, default=20, space='buy', optimize=is_optimize_cofi) buy_fastd = IntParameter(0, 40, default=20, space='buy', optimize=is_optimize_cofi) buy_adx = IntParameter(0, 30, default=30, space='buy', optimize=is_optimize_cofi) buy_ewo_high = DecimalParameter(2, 12, default=3.553, space='buy', optimize=is_optimize_cofi) buy_cofi_cti = DecimalParameter(-0.9, -0.0, default=-0.5, space='buy', optimize=is_optimize_cofi) buy_cofi_r14 = DecimalParameter(-100, -44, default=-60, space='buy', optimize=is_optimize_cofi) # custom stoploss trailing_optimize = False pHSL = DecimalParameter(-0.990, -0.040, default=-0.15, decimals=3, space='sell', optimize=False) pPF_1 = DecimalParameter(0.008, 0.100, default=0.03, decimals=3, space='sell', optimize=False) pSL_1 = DecimalParameter(0.01, 0.030, default=0.025, decimals=3, space='sell', optimize=trailing_optimize) pPF_2 = DecimalParameter(0.040, 0.200, default=0.080, decimals=3, space='sell', optimize=False) pSL_2 = DecimalParameter(0.050, 0.080, default=0.075, decimals=3, space='sell', optimize=trailing_optimize) sell_fastx = IntParameter(50, 100, default=75, space='sell', optimize=True) def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: # hard stoploss profit HSL = self.pHSL.value PF_1 = self.pPF_1.value SL_1 = self.pSL_1.value PF_2 = self.pPF_2.value SL_2 = self.pSL_2.value # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. if current_profit > PF_2: sl_profit = SL_2 + (current_profit - PF_2) elif current_profit > PF_1: sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) else: sl_profit = HSL if self.can_short: if (-1 + ((1 - sl_profit) / (1 - current_profit))) <= 0: return 1 else: if (1 - ((1 + sl_profit) / (1 + current_profit))) <= 0: return 1 return stoploss_from_open(sl_profit, current_profit, is_short=trade.is_short) @informative('1h') def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['roc'] = ta.ROC(dataframe, timeperiod=9) # # Bollinger bands bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) dataframe['bb_lowerband2'] = bollinger2['lower'] dataframe['bb_middleband2'] = bollinger2['mid'] dataframe['bb_upperband2'] = bollinger2['upper'] dataframe['bb_width'] = ((dataframe['bb_upperband2'] - dataframe['bb_lowerband2']) / dataframe['bb_middleband2']) return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['sma_15'] = ta.SMA(dataframe, timeperiod=15) dataframe['cti'] = pta.cti(dataframe["close"], length=20) # EMA dataframe['ema_8'] = ta.EMA(dataframe, timeperiod=8) dataframe['ema_16'] = ta.EMA(dataframe, timeperiod=16) dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) # Elliot dataframe['EWO'] = ewo(dataframe, 50, 200) dataframe['r_14'] = williams_r(dataframe, period=14) # Cofi stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0) dataframe['fastd'] = stoch_fast['fastd'] dataframe['fastk'] = stoch_fast['fastk'] dataframe['adx'] = ta.ADX(dataframe) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] dataframe.loc[:, 'enter_tag'] = '' is_cofi = ( (dataframe['roc_1h'] < self.buy_roc_1h.value) & (dataframe['bb_width_1h'] < self.buy_bb_width_1h.value) & (dataframe['open'] < dataframe['ema_8'] * self.buy_ema_cofi.value) & (qtpylib.crossed_above(dataframe['fastk'], dataframe['fastd'])) & (dataframe['fastk'] < self.buy_fastk.value) & (dataframe['fastd'] < self.buy_fastd.value) & (dataframe['adx'] > self.buy_adx.value) & (dataframe['EWO'] > self.buy_ewo_high.value) & (dataframe['cti'] < self.buy_cofi_cti.value) & (dataframe['r_14'] < self.buy_cofi_r14.value) ) is_ewo = ( (dataframe['rsi_fast'] < self.buy_rsi_fast.value) & (dataframe['close'] < dataframe['ema_8'] * self.buy_ema_low.value) & (dataframe['EWO'] > self.buy_ewo.value) & (dataframe['close'] < dataframe['ema_16'] * self.buy_ema_high.value) & (dataframe['rsi'] < self.buy_rsi.value) ) is_nfi_32 = ( (dataframe['rsi_slow'] < dataframe['rsi_slow'].shift(1)) & (dataframe['rsi_fast'] < 46) & (dataframe['rsi'] > 19) & (dataframe['close'] < dataframe['sma_15'] * 0.942) & (dataframe['cti'] < -0.86) ) conditions.append(is_cofi) dataframe.loc[is_cofi, 'enter_tag'] += 'cofi ' conditions.append(is_ewo) dataframe.loc[is_ewo, 'enter_tag'] += 'ewo ' conditions.append(is_nfi_32) dataframe.loc[is_nfi_32, 'enter_tag'] += 'nfi_32 ' if conditions: dataframe.loc[ reduce(lambda x, y: x | y, conditions), 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] dataframe.loc[:, 'exit_tag'] = '' fastk_cross = ( (qtpylib.crossed_above(dataframe['fastk'], self.sell_fastx.value)) ) conditions.append(fastk_cross) dataframe.loc[fastk_cross, 'exit_tag'] += 'fastk_cross ' if conditions: dataframe.loc[ reduce(lambda x, y: x | y, conditions), 'exit_long'] = 1 return dataframe def leverage(self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, side: str, **kwargs) -> float: return self.leverage_num.value