class FrostAuraM5Strategy(IStrategy): """ This is FrostAura's mark 5 strategy which aims to make purchase decisions based on the ADX and RSI indicators. A momentum-based strategy. Last Optimization: Profit % : 4.03% Optimized for : Last 45 days, 1h Avg : 1d 2h 49m """ # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. INTERFACE_VERSION = 2 # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi". minimal_roi = {"0": 0.36, "298": 0.18, "731": 0.057, "1050": 0} stoploss = -0.271 # Trailing stop: trailing_stop = False # value loaded from strategy trailing_stop_positive = None # value loaded from strategy trailing_stop_positive_offset = 0.0 # value loaded from strategy trailing_only_offset_is_reached = False # value loaded from strategy # Optimal ticker interval for the strategy. timeframe = '1h' # Run "populate_indicators()" only for new candle. process_only_new_candles = False # These values can be overridden in the "ask_strategy" section in the config. use_sell_signal = True sell_profit_only = False ignore_roi_if_buy_signal = False # Number of candles the strategy requires before producing valid signals startup_candle_count: int = 50 # Optional order type mapping. order_types = { 'buy': 'limit', 'sell': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } # Optional order time in force. order_time_in_force = {'buy': 'gtc', 'sell': 'gtc'} plot_config = { 'main_plot': { 'tema': {}, 'sar': { 'color': 'white' }, }, 'subplots': { "MACD": { 'macd': { 'color': 'blue' }, 'macdsignal': { 'color': 'orange' }, }, "RSI": { 'rsi': { 'color': 'red' }, } } } def informative_pairs(self): return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # ADX dataframe['adx'] = ta.ADX(dataframe) # Plus Directional Indicator / Movement dataframe['plus_di'] = ta.PLUS_DI(dataframe) # Minus Directional Indicator / Movement dataframe['minus_di'] = ta.MINUS_DI(dataframe) # RSI dataframe['rsi'] = ta.RSI(dataframe) return dataframe buy_adx = IntParameter([0, 50], default=50, space='buy') buy_di_direction = CategoricalParameter(['<', '>'], default='>', space='buy') def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: minimum_coin_price = 0.0000015 dataframe.loc[((dataframe['adx'] > self.buy_adx.value) & (dataframe['plus_di'] > dataframe['minus_di'] if self. buy_di_direction.value == '>' else dataframe['plus_di'] < dataframe['minus_di']) & (dataframe["close"] > minimum_coin_price)), 'buy'] = 1 return dataframe sell_rsi = IntParameter([20, 80], default=29, space='sell') sell_adx = IntParameter([0, 50], default=45, space='sell') def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[((dataframe['rsi'] < self.sell_rsi.value) & (dataframe['adx'] < self.sell_adx.value)), 'sell'] = 1 return dataframe
class FrostAuraM3Strategy(IStrategy): """ This is FrostAura's mark 3 strategy which aims to make purchase decisions based on the BB, RSI and Stochastic. Last Optimization: Profit % : 7.04% Optimized for : Last 45 days, 1h Avg : 2d 5h 20m """ # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. INTERFACE_VERSION = 2 # Minimal ROI designed for the strategy. minimal_roi = {"0": 0.186, "226": 0.145, "809": 0.085, "2235": 0} # Stoploss: stoploss = -0.303 # Trailing stop: trailing_stop = False # value loaded from strategy trailing_stop_positive = None # value loaded from strategy trailing_stop_positive_offset = 0.0 # value loaded from strategy trailing_only_offset_is_reached = False # value loaded from strategy # Trailing stoploss trailing_stop = False # Optimal ticker interval for the strategy. timeframe = '1h' # Run "populate_indicators()" only for new candle. process_only_new_candles = False # These values can be overridden in the "ask_strategy" section in the config. use_sell_signal = True sell_profit_only = False ignore_roi_if_buy_signal = False # Number of candles the strategy requires before producing valid signals. startup_candle_count: int = 30 # Optional order type mapping. order_types = { 'buy': 'limit', 'sell': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } # Optional order time in force. order_time_in_force = {'buy': 'gtc', 'sell': 'gtc'} plot_config = { 'main_plot': { 'tema': {}, 'sar': { 'color': 'white' }, }, 'subplots': { "MACD": { 'macd': { 'color': 'blue' }, 'macdsignal': { 'color': 'orange' }, }, "RSI": { 'rsi': { 'color': 'red' }, } } } def informative_pairs(self): return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # RSI dataframe['rsi'] = ta.RSI(dataframe) # Stochastic Slow stoch = ta.STOCH(dataframe) dataframe['slowd'] = stoch['slowd'] dataframe['slowk'] = stoch['slowk'] # Bollinger Bands bollinger1 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=1) dataframe['bb_lowerband1'] = bollinger1['lower'] dataframe['bb_middleband1'] = bollinger1['mid'] dataframe['bb_upperband1'] = bollinger1['upper'] 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'] bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=3) dataframe['bb_lowerband3'] = bollinger3['lower'] dataframe['bb_middleband3'] = bollinger3['mid'] dataframe['bb_upperband3'] = bollinger3['upper'] bollinger4 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=4) dataframe['bb_lowerband4'] = bollinger4['lower'] dataframe['bb_middleband4'] = bollinger4['mid'] dataframe['bb_upperband4'] = bollinger4['upper'] return dataframe buy_rsi = IntParameter([20, 80], default=20, space='buy') buy_band = CategoricalParameter(['lower', 'middle', 'upper'], default='lower', space='buy') buy_std = CategoricalParameter(['1', '2', '3', '4'], default='2', space='buy') buy_slowd = IntParameter([20, 80], default=23, space='buy') buy_slowk = IntParameter([20, 80], default=22, space='buy') def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: minimum_coin_price = 0.0000015 var_buy_rsi = self.buy_rsi.value var_buy_band = self.buy_band.value var_buy_std = self.buy_std.value var_buy_band_value = dataframe['bb_' + var_buy_band + 'band' + var_buy_std] var_buy_slowd = self.buy_slowd.value var_buy_slowk = self.buy_slowk.value dataframe.loc[((dataframe['slowd'] < var_buy_slowd) & (dataframe['slowk'] < var_buy_slowk) & (dataframe['rsi'] < var_buy_rsi) & (dataframe["close"] < var_buy_band_value) & (dataframe["close"] > minimum_coin_price)), 'buy'] = 1 return dataframe sell_rsi = IntParameter([20, 80], default=77, space='sell') sell_band = CategoricalParameter(['lower', 'middle', 'upper'], default='lower', space='sell') sell_std = CategoricalParameter(['1', '2', '3', '4'], default='4', space='sell') sell_slowd = IntParameter([20, 80], default=30, space='sell') sell_slowk = IntParameter([20, 80], default=61, space='sell') def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: var_sell_rsi = self.sell_rsi.value var_sell_band = self.sell_band.value var_sell_std = self.sell_std.value var_sell_band_value = dataframe['bb_' + var_sell_band + 'band' + var_sell_std] var_sell_slowd = self.sell_slowd.value var_sell_slowk = self.sell_slowk.value dataframe.loc[((dataframe['slowd'] > var_sell_slowd) & (dataframe['slowk'] > var_sell_slowk) & (dataframe['rsi'] > var_sell_rsi) & (dataframe["close"] < var_sell_band_value)), 'sell'] = 1 return dataframe
class Trend_Strength_Directional(IStrategy): INTERFACE_VERSION = 2 timeframe = '15m' # ROI table: minimal_roi = {"0": 0.383, "120": 0.082, "283": 0.045, "495": 0} # Stoploss: stoploss = -0.314 # Trailing stop: trailing_stop = True trailing_stop_positive = 0.307 trailing_stop_positive_offset = 0.364 trailing_only_offset_is_reached = False # Hyperopt Buy Parameters buy_plusdi_enabled = CategoricalParameter([True, False], space='buy', optimize=True, default=False) buy_adx = IntParameter(low=1, high=100, default=12, space='buy', optimize=True, load=True) buy_adx_timeframe = IntParameter(low=1, high=50, default=9, space='buy', optimize=True, load=True) buy_plusdi = IntParameter(low=1, high=100, default=44, space='buy', optimize=True, load=True) buy_minusdi = IntParameter(low=1, high=100, default=74, space='buy', optimize=True, load=True) # Hyperopt Sell Parameters sell_plusdi_enabled = CategoricalParameter([True, False], space='sell', optimize=True, default=True) sell_adx = IntParameter(low=1, high=100, default=3, space='sell', optimize=True, load=True) sell_adx_timeframe = IntParameter(low=1, high=50, default=41, space='sell', optimize=True, load=True) sell_plusdi = IntParameter(low=1, high=100, default=49, space='sell', optimize=True, load=True) sell_minusdi = IntParameter(low=1, high=100, default=11, space='sell', optimize=True, load=True) def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # RSI dataframe['rsi'] = ta.RSI(dataframe) return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # GUARDS if self.buy_plusdi_enabled.value: conditions.append( ta.PLUS_DI(dataframe, timeperiod=int(self.buy_plusdi.value)) > ta.MINUS_DI(dataframe, timeperiod=int(self.buy_minusdi.value))) # TRIGGERS try: conditions.append( ta.ADX(dataframe, timeperiod=int(self.buy_adx_timeframe.value)) > self.buy_adx.value) except Exception: pass if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # GUARDS if self.sell_plusdi_enabled.value: conditions.append( ta.PLUS_DI(dataframe, timeperiod=int(self.sell_plusdi.value)) < ta.MINUS_DI(dataframe, timeperiod=int(self.sell_minusdi.value)) ) # TRIGGERS try: conditions.append( ta.ADX(dataframe, timeperiod=int(self.sell_adx_timeframe.value)) < self.sell_adx.value) except Exception: pass if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1 return dataframe
class SMAOG(IStrategy): INTERFACE_VERSION = 2 buy_params = { "base_nb_candles_buy": 26, "buy_trigger": "SMA", "low_offset": 0.968, "pair_is_bad_0_threshold": 0.555, "pair_is_bad_1_threshold": 0.172, "pair_is_bad_2_threshold": 0.198, } sell_params = { "base_nb_candles_sell": 28, "high_offset": 0.985, "sell_trigger": "EMA", } base_nb_candles_buy = IntParameter( 16, 45, default=buy_params['base_nb_candles_buy'], space='buy', optimize=False, load=True) base_nb_candles_sell = IntParameter( 16, 45, default=sell_params['base_nb_candles_sell'], space='sell', optimize=False, load=True) low_offset = DecimalParameter(0.8, 0.99, default=buy_params['low_offset'], space='buy', optimize=False, load=True) high_offset = DecimalParameter(0.8, 1.1, default=sell_params['high_offset'], space='sell', optimize=False, load=True) buy_trigger = CategoricalParameter(ma_types.keys(), default=buy_params['buy_trigger'], space='buy', optimize=False, load=True) sell_trigger = CategoricalParameter(ma_types.keys(), default=sell_params['sell_trigger'], space='sell', optimize=False, load=True) pair_is_bad_0_threshold = DecimalParameter(0.0, 0.600, default=0.220, space='buy', optimize=True, load=True) pair_is_bad_1_threshold = DecimalParameter(0.0, 0.350, default=0.090, space='buy', optimize=True, load=True) pair_is_bad_2_threshold = DecimalParameter(0.0, 0.200, default=0.060, space='buy', optimize=True, load=True) timeframe = '5m' stoploss = -0.23 minimal_roi = { "0": 10, } trailing_stop = True trailing_only_offset_is_reached = True trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.02 use_sell_signal = True sell_profit_only = False ignore_roi_if_buy_signal = False process_only_new_candles = True startup_candle_count = 400 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if not self.config['runmode'].value == 'hyperopt': dataframe['ma_offset_buy'] = ma_types[self.buy_trigger.value]( dataframe, int( self.base_nb_candles_buy.value)) * self.low_offset.value dataframe['ma_offset_sell'] = ma_types[self.sell_trigger.value]( dataframe, int( self.base_nb_candles_sell.value)) * self.high_offset.value dataframe['pair_is_bad'] = ( (((dataframe['open'].rolling(144).min() - dataframe['close']) / dataframe['close']) >= self.pair_is_bad_0_threshold.value) | (((dataframe['open'].rolling(12).min() - dataframe['close']) / dataframe['close']) >= self.pair_is_bad_1_threshold.value) | (((dataframe['open'].rolling(2).min() - dataframe['close']) / dataframe['close']) >= self.pair_is_bad_2_threshold.value) ).astype('int') dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) dataframe['rsi_exit'] = ta.RSI(dataframe, timeperiod=2) return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if self.config['runmode'].value == 'hyperopt': dataframe['ma_offset_buy'] = ma_types[self.buy_trigger.value]( dataframe, int( self.base_nb_candles_buy.value)) * self.low_offset.value dataframe['pair_is_bad'] = ( (((dataframe['open'].rolling(144).min() - dataframe['close']) / dataframe['close']) >= self.pair_is_bad_0_threshold.value) | (((dataframe['open'].rolling(12).min() - dataframe['close']) / dataframe['close']) >= self.pair_is_bad_1_threshold.value) | (((dataframe['open'].rolling(2).min() - dataframe['close']) / dataframe['close']) >= self.pair_is_bad_2_threshold.value) ).astype('int') dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) dataframe.loc[((dataframe['ema_50'] > dataframe['ema_200']) & (dataframe['close'] > dataframe['ema_200']) & (dataframe['pair_is_bad'] < 1) & (dataframe['close'] < dataframe['ma_offset_buy']) & (dataframe['volume'] > 0)), 'buy'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if self.config['runmode'].value == 'hyperopt': dataframe['ma_offset_sell'] = ta.EMA( dataframe, int( self.base_nb_candles_sell.value)) * self.high_offset.value dataframe.loc[((dataframe['close'] > dataframe['ma_offset_sell']) & ( (dataframe['open'] < dataframe['open'].shift(1)) | (dataframe['rsi_exit'] < 50) | (dataframe['rsi_exit'] < dataframe['rsi_exit'].shift(1))) & (dataframe['volume'] > 0)), 'sell'] = 1 return dataframe
class FrostAuraM4Strategy(IStrategy): """ This is FrostAura's mark 4 stretagy with RSI and MACD. Last Optimization: Profit % : 7.07% Optimized for : Last 45 days, 4h Avg : 5d 18h 30m """ # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. INTERFACE_VERSION = 2 # Minimal ROI designed for the strategy. minimal_roi = { "0": 0.578, "1617": 0.167, "2673": 0.105, "6903": 0 } # Optimal stoploss designed for the strategy. stoploss = -0.307 # Trailing stop: trailing_stop = False # value loaded from strategy trailing_stop_positive = None # value loaded from strategy trailing_stop_positive_offset = 0.0 # value loaded from strategy trailing_only_offset_is_reached = False # value loaded from strategy # Trailing stoploss trailing_stop = False # Optimal ticker interval for the strategy. timeframe = '4h' # Run "populate_indicators()" only for new candle. process_only_new_candles = False # These values can be overridden in the "ask_strategy" section in the config. use_sell_signal = True sell_profit_only = False ignore_roi_if_buy_signal = False # Number of candles the strategy requires before producing valid signals. startup_candle_count: int = 30 # Optional order type mapping. order_types = { 'buy': 'limit', 'sell': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } # Optional order time in force. order_time_in_force = { 'buy': 'gtc', 'sell': 'gtc' } plot_config = { 'main_plot': { 'tema': {}, 'sar': {'color': 'white'}, }, 'subplots': { "MACD": { 'macd': {'color': 'blue'}, 'macdsignal': {'color': 'orange'}, }, "RSI": { 'rsi': {'color': 'red'}, } } } def informative_pairs(self): return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # RSI dataframe['rsi'] = ta.RSI(dataframe) # MACD macd = ta.MACD(dataframe) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] dataframe['macdhist'] = macd['macdhist'] return dataframe buy_rsi = IntParameter([20, 80], default=24, space='buy') buy_rsi_direction = CategoricalParameter(['<', '>'], default='>', space='buy') buy_macd_direction = CategoricalParameter(['<', '>'], default='<', space='buy') def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: minimum_coin_price = 0.0000015 dataframe.loc[ ( (dataframe['rsi'] < self.buy_rsi.value if self.buy_rsi_direction.value == '<' else dataframe['rsi'] > self.buy_rsi.value) & (dataframe['macd'] > dataframe['macdsignal'] if self.buy_macd_direction.value == '<' else dataframe['macd'] < dataframe['macdsignal']) & (dataframe["close"] > minimum_coin_price) ), 'buy'] = 1 return dataframe sell_rsi = IntParameter([20, 80], default=57, space='sell') sell_rsi_direction = CategoricalParameter(['<', '>'], default='<', space='sell') sell_macd_direction = CategoricalParameter(['<', '>'], default='>', space='sell') def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( (dataframe['rsi'] < self.sell_rsi.value if self.sell_rsi_direction.value == '<' else dataframe['rsi'] > self.sell_rsi.value) & (dataframe['macd'] > dataframe['macdsignal'] if self.sell_macd_direction.value == '<' else dataframe['macd'] < dataframe['macdsignal']) ), 'sell'] = 1 return dataframe
class Solipsis4(IStrategy): ## Buy Space Hyperopt Variables # Base Pair Params base_mp = IntParameter(10, 50, default=30, space='buy') base_rmi_max = IntParameter(30, 60, default=50, space='buy') base_rmi_min = IntParameter(0, 30, default=20, space='buy') base_ma_streak = IntParameter(1, 4, default=1, space='buy') base_rmi_streak = IntParameter(3, 8, default=3, space='buy') base_trigger = CategoricalParameter(['pcc', 'rmi', 'none'], default='rmi', space='buy', optimize=False) inf_pct_adr = DecimalParameter(0.70, 0.99, default=0.80, space='buy') # BTC Informative xbtc_guard = CategoricalParameter(['strict', 'lazy', 'none'], default='lazy', space='buy', optimize=True) xbtc_base_rmi = IntParameter(20, 70, default=40, space='buy') # BTC / ETH Stake Parameters xtra_base_stake_rmi = IntParameter(10, 50, default=50, space='buy') xtra_base_fiat_rmi = IntParameter(30, 70, default=50, space='buy') ## Sell Space Params are being "hijacked" for custom_stoploss and dynamic_roi # Dynamic ROI droi_trend_type = CategoricalParameter(['rmi', 'ssl', 'candle', 'any'], default='any', space='sell', optimize=True) droi_pullback = CategoricalParameter([True, False], default=True, space='sell', optimize=True) droi_pullback_amount = DecimalParameter(0.005, 0.02, default=0.005, space='sell') droi_pullback_respect_table = CategoricalParameter([True, False], default=False, space='sell', optimize=True) # Custom Stoploss cstp_threshold = DecimalParameter(-0.05, 0, default=-0.03, space='sell') cstp_bail_how = CategoricalParameter(['roc', 'time', 'any'], default='roc', space='sell', optimize=True) cstp_bail_roc = DecimalParameter(-0.05, -0.01, default=-0.03, space='sell') cstp_bail_time = IntParameter(720, 1440, default=720, space='sell') timeframe = '5m' inf_timeframe = '1h' buy_params = { 'base_ma_streak': 1, 'base_mp': 12, 'base_rmi_max': 50, 'base_rmi_min': 20, 'base_rmi_streak': 3, 'inf_pct_adr': 950, 'xbtc_base_rmi': 20, 'xbtc_guard': 'none', 'xtra_base_fiat_rmi': 45, 'xtra_base_stake_rmi': 13 } sell_params = { 'droi_pullback': True, 'droi_pullback_amount': 0.006, 'droi_pullback_respect_table': False, 'droi_trend_type': 'any' } minimal_roi = {"0": 0.01, "1440": 0} # Enable or disable these as desired # Must be enabled when hyperopting the respective spaces use_dynamic_roi = True use_custom_stoploss = True stoploss = -0.10 # Recommended use_sell_signal = False sell_profit_only = False ignore_roi_if_buy_signal = False # Required startup_candle_count: int = 233 process_only_new_candles = False # Strategy Specific Variable Storage custom_trade_info = {} custom_fiat = "USD" # Only relevant if stake is BTC or ETH custom_btc_inf = False # Don't change this. """ Informative Pair Definitions """ def informative_pairs(self): # add all whitelisted pairs on informative timeframe pairs = self.dp.current_whitelist() informative_pairs = [(pair, self.inf_timeframe) for pair in pairs] # add extra informative pairs if the stake is BTC or ETH if self.config['stake_currency'] in ('BTC', 'ETH'): for pair in pairs: coin, stake = pair.split('/') coin_fiat = f"{coin}/{self.custom_fiat}" informative_pairs += [(coin_fiat, self.timeframe)] stake_fiat = f"{self.config['stake_currency']}/{self.custom_fiat}" informative_pairs += [(stake_fiat, self.timeframe)] # if BTC/STAKE is not in whitelist, add it as an informative pair on both timeframes else: btc_stake = f"BTC/{self.config['stake_currency']}" if not btc_stake in pairs: informative_pairs += [(btc_stake, self.timeframe)] return informative_pairs """ Indicator Definitions """ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if not metadata['pair'] in self.custom_trade_info: self.custom_trade_info[metadata['pair']] = {} ## Base Timeframe / Pair dataframe['kama'] = ta.KAMA(dataframe, length=233) # RMI: https://www.tradingview.com/script/kwIt9OgQ-Relative-Momentum-Index/ dataframe['rmi'] = cta.RMI(dataframe, length=24, mom=5) # Momentum Pinball: https://www.tradingview.com/script/fBpVB1ez-Momentum-Pinball-Indicator/ dataframe['roc-mp'] = ta.ROC(dataframe, timeperiod=1) dataframe['mp'] = ta.RSI(dataframe['roc-mp'], timeperiod=3) # MA Streak: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/ dataframe['mastreak'] = cta.mastreak(dataframe, period=4) # Percent Change Channel: https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/ upper, mid, lower = cta.pcc(dataframe, period=40, mult=3) dataframe['pcc-lowerband'] = lower dataframe['pcc-upperband'] = upper lookup_idxs = dataframe.index.values - ( abs(dataframe['mastreak'].values) + 1) valid_lookups = lookup_idxs >= 0 dataframe['sbc'] = np.nan dataframe.loc[valid_lookups, 'sbc'] = dataframe['close'].to_numpy()[ lookup_idxs[valid_lookups].astype(int)] dataframe['streak-roc'] = 100 * (dataframe['close'] - dataframe['sbc']) / dataframe['sbc'] # Trends, Peaks and Crosses 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) 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['rmi-dn'] = np.where( dataframe['rmi'] <= dataframe['rmi'].shift(), 1, 0) dataframe['rmi-dn-count'] = dataframe['rmi-dn'].rolling(8).sum() dataframe['streak-bo'] = np.where( dataframe['streak-roc'] < dataframe['pcc-lowerband'], 1, 0) dataframe['streak-bo-count'] = dataframe['streak-bo'].rolling(8).sum() # Indicators used only for ROI and Custom Stoploss ssldown, sslup = cta.SSLChannels_ATR(dataframe, length=21) dataframe['sroc'] = cta.SROC(dataframe, roclen=21, emalen=13, smooth=21) dataframe['ssl-dir'] = np.where(sslup > ssldown, 'up', 'down') # Base pair informative timeframe indicators informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe) # Get the "average day range" between the 1d high and 1d low to set up guards informative['1d-high'] = informative['close'].rolling(24).max() informative['1d-low'] = informative['close'].rolling(24).min() informative['adr'] = informative['1d-high'] - informative['1d-low'] dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_timeframe, ffill=True) # Other stake specific informative indicators # e.g if stake is BTC and current coin is XLM (pair: XLM/BTC) if self.config['stake_currency'] in ('BTC', 'ETH'): coin, stake = metadata['pair'].split('/') fiat = self.custom_fiat coin_fiat = f"{coin}/{fiat}" stake_fiat = f"{stake}/{fiat}" # Informative COIN/FIAT e.g. XLM/USD - Base Timeframe coin_fiat_tf = self.dp.get_pair_dataframe(pair=coin_fiat, timeframe=self.timeframe) dataframe[f"{fiat}_rmi"] = cta.RMI(coin_fiat_tf, length=55, mom=5) # Informative STAKE/FIAT e.g. BTC/USD - Base Timeframe stake_fiat_tf = self.dp.get_pair_dataframe( pair=stake_fiat, timeframe=self.timeframe) dataframe[f"{stake}_rmi"] = cta.RMI(stake_fiat_tf, length=55, mom=5) # Informatives for BTC/STAKE if not in whitelist else: pairs = self.dp.current_whitelist() btc_stake = f"BTC/{self.config['stake_currency']}" if not btc_stake in pairs: self.custom_btc_inf = True # BTC/STAKE - Base Timeframe btc_stake_tf = self.dp.get_pair_dataframe( pair=btc_stake, timeframe=self.timeframe) dataframe['BTC_rmi'] = cta.RMI(btc_stake_tf, length=55, mom=5) dataframe['BTC_close'] = btc_stake_tf['close'] dataframe['BTC_kama'] = ta.KAMA(btc_stake_tf, length=144) # 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']]['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') return dataframe """ Buy Signal """ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # Informative Timeframe Guards conditions.append(( dataframe['close'] <= dataframe[f"1d-low_{self.inf_timeframe}"] + (self.inf_pct_adr.value * dataframe[f"adr_{self.inf_timeframe}"]))) # Base Timeframe Guards conditions.append( (dataframe['rmi-dn-count'] >= self.base_rmi_streak.value) & (dataframe['streak-bo-count'] >= self.base_ma_streak.value) & (dataframe['rmi'] <= self.base_rmi_max.value) & (dataframe['rmi'] >= self.base_rmi_min.value) & (dataframe['mp'] <= self.base_mp.value)) # Base Timeframe Trigger if self.base_trigger.value == 'pcc': conditions.append( qtpylib.crossed_above(dataframe['streak-roc'], dataframe['pcc-lowerband'])) if self.base_trigger.value == 'rmi': conditions.append(dataframe['rmi-up-trend'] == 1) # Extra conditions for */BTC and */ETH stakes on additional informative pairs if self.config['stake_currency'] in ('BTC', 'ETH'): conditions.append(( dataframe[f"{self.custom_fiat}_rmi"] > self.xtra_base_fiat_rmi. value) | (dataframe[f"{self.config['stake_currency']}_rmi"] < self.xtra_base_stake_rmi.value)) # Extra conditions for BTC/STAKE if not in whitelist else: if self.custom_btc_inf: if self.xbtc_guard.value == 'strict': conditions.append( ((dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) & (dataframe['BTC_close'] > dataframe['BTC_kama']))) if self.xbtc_guard.value == 'lazy': conditions.append( (dataframe['close'] > dataframe['kama']) | ((dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) & (dataframe['BTC_close'] > dataframe['BTC_kama']))) conditions.append(dataframe['volume'].gt(0)) if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1 return dataframe """ Sell Signal """ def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['sell'] = 0 return dataframe """ Custom Stoploss """ 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] # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!) else: 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 """ Freqtrade ROI Overload for dynamic ROI functionality """ 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] # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!) else: 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 sell 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 """ Trade Timeout Overloads """ def check_buy_timeout(self, pair: str, trade: Trade, order: dict, **kwargs) -> bool: bid_strategy = self.config.get('bid_strategy', {}) ob = self.dp.orderbook(pair, 1) current_price = ob[f"{bid_strategy['price_side']}s"][0][0] if current_price > order['price'] * 1.01: return True return False def check_sell_timeout(self, pair: str, trade: Trade, order: dict, **kwargs) -> bool: ask_strategy = self.config.get('ask_strategy', {}) ob = self.dp.orderbook(pair, 1) current_price = ob[f"{ask_strategy['price_side']}s"][0][0] if current_price < order['price'] * 0.99: return True return False
class Strategy005(IStrategy): """ Strategy 005 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies How to use it? > python3 ./freqtrade/main.py -s Strategy005 """ INTERFACE_VERSION = 2 # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi" minimal_roi = { "0": 0.154, "15": 0.084, "67": 0.028, "125": 0 # "1440": 0.01, # "80": 0.02, # "40": 0.03, # "20": 0.04, # "0": 0.05 } # Optimal stoploss designed for the strategy # This attribute will be overridden if the config file contains "stoploss" stoploss = -0.05 # Optimal timeframe for the strategy timeframe = '5m' # trailing stoploss trailing_stop = False trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.02 # run "populate_indicators" only for new candle process_only_new_candles = False # Experimental settings (configuration will overide these if set) use_sell_signal = True sell_profit_only = True ignore_roi_if_buy_signal = False # Optional order type mapping order_types = { 'buy': 'limit', 'sell': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } buy_volumeAVG = IntParameter(low=50, high=300, default=70, space='buy', optimize=True) buy_rsi = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) buy_fastd = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) buy_fishRsiNorma = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) sell_rsi = IntParameter(low=1, high=100, default=70, space='sell', optimize=True) sell_minusDI = IntParameter(low=1, high=100, default=50, space='sell', optimize=True) sell_fishRsiNorma = IntParameter(low=1, high=100, default=50, space='sell', optimize=True) sell_trigger = CategoricalParameter(["rsi-macd-minusdi", "sar-fisherRsi"], default=30, space='sell', optimize=True) # Buy hyperspace params: buy_params = { # "buy_fastd": 1, # "buy_fishRsiNorma": 5, # "buy_rsi": 26, # "buy_volumeAVG": 150, "buy_fastd": 92, "buy_fishRsiNorma": 39, "buy_rsi": 37, "buy_volumeAVG": 185, } # Sell hyperspace params: sell_params = { # "sell_fishRsiNorma": 30, # "sell_minusDI": 4, # "sell_rsi": 74, # "sell_trigger": "rsi-macd-minusdi", "sell_fishRsiNorma": 63, "sell_minusDI": 35, "sell_rsi": 41, "sell_trigger": "rsi-macd-minusdi", } def informative_pairs(self): """ Define additional, informative pair/interval combinations to be cached from the exchange. These pair/interval combinations are non-tradeable, unless they are part of the whitelist as well. For more information, please consult the documentation :return: List of tuples in the format (pair, interval) Sample: return [("ETH/USDT", "5m"), ("BTC/USDT", "15m"), ] """ return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Adds several different TA indicators to the given DataFrame Performance Note: For the best performance be frugal on the number of indicators you are using. Let uncomment only the indicator you are using in your strategies or your hyperopt configuration, otherwise you will waste your memory and CPU usage. """ # MACD macd = ta.MACD(dataframe) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] # Minus Directional Indicator / Movement dataframe['minus_di'] = ta.MINUS_DI(dataframe) # RSI dataframe['rsi'] = ta.RSI(dataframe) # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) rsi = 0.1 * (dataframe['rsi'] - 50) dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy) dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) # Stoch fast stoch_fast = ta.STOCHF(dataframe) dataframe['fastd'] = stoch_fast['fastd'] dataframe['fastk'] = stoch_fast['fastk'] # Overlap Studies # ------------------------------------ # SAR Parabol dataframe['sar'] = ta.SAR(dataframe) # SMA - Simple Moving Average dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Based on TA indicators, populates the buy signal for the given dataframe :param dataframe: DataFrame :return: DataFrame with buy column """ dataframe.loc[ # Prod ((dataframe['close'] > 0.00000200) & (dataframe['volume'] > dataframe['volume']. rolling(self.buy_volumeAVG.value).mean() * 4) & (dataframe['close'] < dataframe['sma']) & (dataframe['fastd'] > dataframe['fastk']) & (dataframe['rsi'] > self.buy_rsi.value) & (dataframe['fastd'] > self.buy_fastd.value) & (dataframe['fisher_rsi_norma'] < self.buy_fishRsiNorma.value)), 'buy'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Based on TA indicators, populates the sell signal for the given dataframe :param dataframe: DataFrame :return: DataFrame with buy column """ conditions = [] if self.sell_trigger.value == 'rsi-macd-minusdi': conditions.append( qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) conditions.append(dataframe['macd'] < 0) conditions.append(dataframe['minus_di'] > self.sell_minusDI.value) if self.sell_trigger.value == 'sar-fisherRsi': conditions.append(dataframe['sar'] > dataframe['close']) conditions.append( dataframe['fisher_rsi'] > self.sell_fishRsiNorma.value) if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1 return dataframe
class BigZ04_TSL4(IStrategy): INTERFACE_VERSION = 2 minimal_roi = {"0": 100.0} stoploss = -0.99 # effectively disabled. timeframe = '5m' inf_1h = '1h' # Sell signal use_sell_signal = True sell_profit_only = False sell_profit_offset = 0.001 # it doesn't meant anything, just to guarantee there is a minimal profit. ignore_roi_if_buy_signal = False # Trailing stoploss trailing_stop = False trailing_only_offset_is_reached = False trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.025 # Custom stoploss use_custom_stoploss = True # Run "populate_indicators()" only for new candle. process_only_new_candles = True # Number of candles the strategy requires before producing valid signals startup_candle_count: int = 400 # Optional order type mapping. order_types = { 'buy': 'market', 'sell': 'market', 'stoploss': 'market', 'stoploss_on_exchange': False } buy_params = { ############# # Enable/Disable conditions "buy_condition_0_enable": True, "buy_condition_1_enable": True, "buy_condition_2_enable": True, "buy_condition_3_enable": True, "buy_condition_4_enable": True, "buy_condition_5_enable": True, "buy_condition_6_enable": True, "buy_condition_7_enable": True, "buy_condition_8_enable": True, "buy_condition_9_enable": True, "buy_condition_10_enable": True, "buy_condition_11_enable": True, "buy_condition_12_enable": True, "buy_condition_13_enable": False, } # V1 original # Sell hyperspace params: sell_params = { "base_nb_candles_sell": 49, "high_offset": 1.006, "pHSL": -0.08, "pPF_1": 0.016, "pSL_1": 0.011, "pPF_2": 0.080, "pSL_2": 0.040, } ############################################################################ # Buy buy_condition_0_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_1_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_2_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_3_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_4_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_5_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_6_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_7_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_8_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_9_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_10_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_11_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_12_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_condition_13_enable = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) buy_bb20_close_bblowerband_safe_1 = DecimalParameter(0.950, 1.050, default=0.989, decimals=3, space='buy', optimize=False, load=True) buy_bb20_close_bblowerband_safe_2 = DecimalParameter(0.700, 1.100, default=0.982, decimals=2, space='buy', optimize=False, load=True) buy_volume_pump_1 = DecimalParameter(0.1, 0.9, default=0.4, space='buy', decimals=1, optimize=False, load=True) buy_volume_drop_1 = DecimalParameter(1, 10, default=3.8, space='buy', decimals=1, optimize=False, load=True) buy_volume_drop_2 = DecimalParameter(1, 10, default=3, space='buy', decimals=1, optimize=False, load=True) buy_volume_drop_3 = DecimalParameter(1, 10, default=2.7, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1h_0 = DecimalParameter(55.0, 85.0, default=71.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1h_1a = DecimalParameter(65.0, 78.0, default=69.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1h_1 = DecimalParameter(10.0, 40.0, default=16.5, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1h_2 = DecimalParameter(10.0, 40.0, default=15.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1h_3 = DecimalParameter(10.0, 40.0, default=20.0, space='buy', decimals=1, optimize=True, load=True) buy_rsi_1h_4 = DecimalParameter(10.0, 40.0, default=35.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1h_5 = DecimalParameter(10.0, 60.0, default=39.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_0 = DecimalParameter(10.0, 40.0, default=30.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_1 = DecimalParameter(10.0, 40.0, default=28.0, space='buy', decimals=1, optimize=True, load=True) buy_rsi_2 = DecimalParameter(7.0, 40.0, default=10.0, space='buy', decimals=1, optimize=False, load=True) buy_rsi_3 = DecimalParameter(7.0, 40.0, default=14.2, space='buy', decimals=1, optimize=False, load=True) buy_macd_1 = DecimalParameter(0.01, 0.09, default=0.02, space='buy', decimals=2, optimize=False, load=True) buy_macd_2 = DecimalParameter(0.01, 0.09, default=0.03, space='buy', decimals=2, optimize=False, load=True) buy_dip_0 = DecimalParameter(1.015, 1.040, default=1.024, space='buy', decimals=3, optimize=False, load=True) # hyperopt parameters for custom_stoploss() trade_time = IntParameter(25, 65, default=35, space='sell', optimize=False, load=True) rsi_1h_val = IntParameter(25, 45, default=32, space='sell', optimize=False, load=True) narrow_stop = DecimalParameter(1.005, 1.030, default=1.020, space='sell', decimals=3, optimize=False, load=True) wide_stop = DecimalParameter(1.010, 1.045, default=1.035, space='sell', decimals=3, optimize=False, load=True) # hyperopt parameters for SMAOffsetProtectOptV1 sell signal base_nb_candles_sell = IntParameter(5, 80, default=49, space='sell', optimize=False, load=True) high_offset = DecimalParameter(0.99, 1.1, default=1.006, space='sell', optimize=False, load=True) # trailing stoploss hyperopt parameters # hard stoploss profit pHSL = DecimalParameter(-0.200, -0.040, default=-0.08, decimals=3, space='sell', optimize=False, load=True) # profit threshold 1, trigger point, SL_1 is used pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', optimize=False, load=True) pSL_1 = DecimalParameter(0.008, 0.020, default=0.011, decimals=3, space='sell', optimize=False, load=True) # profit threshold 2, SL_2 is used pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', optimize=False, load=True) pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', optimize=False, load=True) def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, **kwargs) -> bool: return True def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs): return False # new custom stoploss, both hard and trailing functions. Trailing stoploss first rises at a slower # rate than the current rate until a profit threshold is reached, after which it rises at a constant # percentage as per a normal trailing stoploss. This allows more margin for pull-backs during a rise. 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 return stoploss_from_open(sl_profit, current_profit) def informative_pairs(self): pairs = self.dp.current_whitelist() 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_50'] = ta.SMA(informative_1h, timeperiod=50) informative_1h['ema_200'] = ta.SMA(informative_1h, timeperiod=200) # RSI informative_1h['rsi'] = ta.RSI(informative_1h, timeperiod=14) 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'] return informative_1h def normal_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] dataframe['bb_middleband'] = bollinger['mid'] dataframe['bb_upperband'] = bollinger['upper'] dataframe['volume_mean_slow'] = dataframe['volume'].rolling( window=48).mean() # EMA dataframe['ema_200'] = ta.SMA(dataframe, timeperiod=200) dataframe['ema_26'] = ta.EMA(dataframe, timeperiod=26) dataframe['ema_12'] = ta.EMA(dataframe, timeperiod=12) # MACD dataframe['macd'], dataframe['signal'], dataframe['hist'] = ta.MACD( dataframe['close'], fastperiod=12, slowperiod=26, signalperiod=9) # SMA dataframe['sma_5'] = ta.EMA(dataframe, timeperiod=5) # RSI dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) # ------ ATR stuff dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) # Calculate all ma_sell values for val in self.base_nb_candles_sell.range: dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # The indicators for the 1h informative timeframe informative_1h = self.informative_1h_indicators(dataframe, metadata) dataframe = merge_informative_pair(dataframe, informative_1h, self.timeframe, self.inf_1h, ffill=True) # The indicators for the normal (5m) timeframe dataframe = self.normal_tf_indicators(dataframe, metadata) return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] conditions.append( (self.buy_condition_12_enable.value & (dataframe['close'] > dataframe['ema_200']) & (dataframe['close'] > dataframe['ema_200_1h']) & (dataframe['close'] < dataframe['bb_lowerband'] * 0.993) & (dataframe['low'] < dataframe['bb_lowerband'] * 0.985) & (dataframe['close'].shift() > dataframe['bb_lowerband']) & (dataframe['rsi_1h'] < 72.8) & (dataframe['open'] > dataframe['close']) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & ((dataframe['open'] - dataframe['close']) < dataframe['bb_upperband'].shift(2) - dataframe['bb_lowerband'].shift(2)) & (dataframe['volume'] > 0))) conditions.append(( self.buy_condition_11_enable.value & (dataframe['close'] > dataframe['ema_200']) & (dataframe['hist'] > 0) & (dataframe['hist'].shift() > 0) & (dataframe['hist'].shift(2) > 0) & (dataframe['hist'].shift(3) > 0) & (dataframe['hist'].shift(5) > 0) & (dataframe['bb_middleband'] - dataframe['bb_middleband'].shift(5) > dataframe['close'] / 200) & (dataframe['bb_middleband'] - dataframe['bb_middleband'].shift(10) > dataframe['close'] / 100) & ((dataframe['bb_upperband'] - dataframe['bb_lowerband']) < (dataframe['close'] * 0.1)) & ((dataframe['open'].shift() - dataframe['close'].shift()) < (dataframe['close'] * 0.018)) & (dataframe['rsi'] > 51) & (dataframe['open'] < dataframe['close']) & (dataframe['open'].shift() > dataframe['close'].shift()) & (dataframe['close'] > dataframe['bb_middleband']) & (dataframe['close'].shift() < dataframe['bb_middleband'].shift()) & (dataframe['low'].shift(2) > dataframe['bb_middleband'].shift(2)) & (dataframe['volume'] > 0) # Make sure Volume is not 0 )) conditions.append(( self.buy_condition_0_enable.value & (dataframe['close'] > dataframe['ema_200']) & (dataframe['rsi'] < self.buy_rsi_0.value) & ((dataframe['close'] * self.buy_dip_0.value < dataframe['open'].shift(3)) | (dataframe['close'] * self.buy_dip_0.value < dataframe['open'].shift(2)) | (dataframe['close'] * self.buy_dip_0.value < dataframe['open'].shift(1))) & (dataframe['rsi_1h'] < self.buy_rsi_1h_0.value) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] > 0) # Make sure Volume is not 0 )) conditions.append( (self.buy_condition_1_enable.value & (dataframe['close'] > dataframe['ema_200']) & (dataframe['close'] > dataframe['ema_200_1h']) & (dataframe['close'] < dataframe['bb_lowerband'] * self.buy_bb20_close_bblowerband_safe_1.value) & (dataframe['rsi_1h'] < self.buy_rsi_1h_1a.value) & (dataframe['open'] > dataframe['close']) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & ((dataframe['open'] - dataframe['close']) < dataframe['bb_upperband'].shift(2) - dataframe['bb_lowerband'].shift(2)) & (dataframe['volume'] > 0))) conditions.append( (self.buy_condition_2_enable.value & (dataframe['close'] > dataframe['ema_200']) & (dataframe['close'] < dataframe['bb_lowerband'] * self.buy_bb20_close_bblowerband_safe_2.value) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['open'] - dataframe['close'] < dataframe['bb_upperband'].shift(2) - dataframe['bb_lowerband'].shift(2)) & (dataframe['volume'] > 0))) conditions.append( (self.buy_condition_3_enable.value & (dataframe['close'] > dataframe['ema_200_1h']) & (dataframe['close'] < dataframe['bb_lowerband']) & (dataframe['rsi'] < self.buy_rsi_3.value) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_3.value)) & (dataframe['volume'] > 0))) conditions.append( (self.buy_condition_4_enable.value & (dataframe['rsi_1h'] < self.buy_rsi_1h_1.value) & (dataframe['close'] < dataframe['bb_lowerband']) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume'] > 0))) conditions.append(( self.buy_condition_5_enable.value & (dataframe['close'] > dataframe['ema_200']) & (dataframe['close'] > dataframe['ema_200_1h']) & (dataframe['ema_26'] > dataframe['ema_12']) & ((dataframe['ema_26'] - dataframe['ema_12']) > (dataframe['open'] * self.buy_macd_1.value)) & ((dataframe['ema_26'].shift() - dataframe['ema_12'].shift()) > (dataframe['open'] / 100)) & (dataframe['close'] < (dataframe['bb_lowerband'])) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] > 0) # Make sure Volume is not 0 )) conditions.append( (self.buy_condition_6_enable.value & (dataframe['rsi_1h'] < self.buy_rsi_1h_5.value) & (dataframe['ema_26'] > dataframe['ema_12']) & ((dataframe['ema_26'] - dataframe['ema_12']) > (dataframe['open'] * self.buy_macd_2.value)) & ((dataframe['ema_26'].shift() - dataframe['ema_12'].shift()) > (dataframe['open'] / 100)) & (dataframe['close'] < (dataframe['bb_lowerband'])) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume'] > 0))) conditions.append( (self.buy_condition_7_enable.value & (dataframe['rsi_1h'] < self.buy_rsi_1h_2.value) & (dataframe['ema_26'] > dataframe['ema_12']) & ((dataframe['ema_26'] - dataframe['ema_12']) > (dataframe['open'] * self.buy_macd_1.value)) & ((dataframe['ema_26'].shift() - dataframe['ema_12'].shift()) > (dataframe['open'] / 100)) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] > 0))) conditions.append( (self.buy_condition_8_enable.value & (dataframe['rsi_1h'] < self.buy_rsi_1h_3.value) & (dataframe['rsi'] < self.buy_rsi_1.value) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume'] > 0))) conditions.append( (self.buy_condition_9_enable.value & (dataframe['rsi_1h'] < self.buy_rsi_1h_4.value) & (dataframe['rsi'] < self.buy_rsi_2.value) & (dataframe['volume'] < (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) & (dataframe['volume_mean_slow'] > dataframe['volume_mean_slow'].shift(48) * self.buy_volume_pump_1.value) & (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value < dataframe['volume_mean_slow'].shift(48)) & (dataframe['volume'] > 0))) conditions.append( (self.buy_condition_10_enable.value & (dataframe['rsi_1h'] < self.buy_rsi_1h_4.value) & (dataframe['close_1h'] < dataframe['bb_lowerband_1h']) & (dataframe['hist'] > 0) & (dataframe['hist'].shift(2) < 0) & (dataframe['rsi'] < 40.5) & (dataframe['hist'] > dataframe['close'] * 0.0012) & (dataframe['open'] < dataframe['close']) & (dataframe['volume'] > 0))) if conditions: dataframe.loc[reduce(lambda x, y: x | y, conditions), 'buy'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[((dataframe['close'] > dataframe['bb_middleband'] * 1.01) & # Don't be gready, sell fast (dataframe['volume'] > 0) # Make sure Volume is not 0 ), 'sell'] = 0 return dataframe
class Bandtastic(IStrategy): INTERFACE_VERSION = 2 timeframe = '15m' # ROI table: minimal_roi = {"0": 0.162, "69": 0.097, "229": 0.061, "566": 0} # Stoploss: stoploss = -0.345 # Trailing stop: trailing_stop = True trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.058 trailing_only_offset_is_reached = False # Hyperopt Buy Parameters buy_fastema = IntParameter(low=1, high=236, default=211, space='buy', optimize=True, load=True) buy_slowema = IntParameter(low=1, high=126, default=364, space='buy', optimize=True, load=True) buy_rsi = IntParameter(low=15, high=70, default=52, space='buy', optimize=True, load=True) buy_mfi = IntParameter(low=15, high=70, default=30, space='buy', optimize=True, load=True) buy_rsi_enabled = CategoricalParameter([True, False], space='buy', optimize=True, default=False) buy_mfi_enabled = CategoricalParameter([True, False], space='buy', optimize=True, default=False) buy_ema_enabled = CategoricalParameter([True, False], space='buy', optimize=True, default=False) buy_trigger = CategoricalParameter( ["bb_lower1", "bb_lower2", "bb_lower3", "bb_lower4"], default="bb_lower1", space="buy") # Hyperopt Sell Parameters sell_fastema = IntParameter(low=1, high=365, default=7, space='sell', optimize=True, load=True) sell_slowema = IntParameter(low=1, high=365, default=6, space='sell', optimize=True, load=True) sell_rsi = IntParameter(low=30, high=100, default=57, space='sell', optimize=True, load=True) sell_mfi = IntParameter(low=30, high=100, default=46, space='sell', optimize=True, load=True) sell_rsi_enabled = CategoricalParameter([True, False], space='sell', optimize=True, default=False) sell_mfi_enabled = CategoricalParameter([True, False], space='sell', optimize=True, default=True) sell_ema_enabled = CategoricalParameter([True, False], space='sell', optimize=True, default=False) sell_trigger = CategoricalParameter([ "sell-bb_upper1", "sell-bb_upper2", "sell-bb_upper3", "sell-bb_upper4" ], default="sell-bb_upper2", space="sell") def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # RSI dataframe['rsi'] = ta.RSI(dataframe) dataframe['mfi'] = ta.MFI(dataframe) # Bollinger Bands 1,2,3 and 4 bollinger1 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=1) dataframe['bb_lowerband1'] = bollinger1['lower'] dataframe['bb_middleband1'] = bollinger1['mid'] dataframe['bb_upperband1'] = bollinger1['upper'] 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'] bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=3) dataframe['bb_lowerband3'] = bollinger3['lower'] dataframe['bb_middleband3'] = bollinger3['mid'] dataframe['bb_upperband3'] = bollinger3['upper'] bollinger4 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=4) dataframe['bb_lowerband4'] = bollinger4['lower'] dataframe['bb_middleband4'] = bollinger4['mid'] dataframe['bb_upperband4'] = bollinger4['upper'] return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # GUARDS if self.buy_rsi_enabled.value: conditions.append(dataframe['rsi'] < self.buy_rsi.value) if self.buy_mfi_enabled.value: conditions.append(dataframe['mfi'] < self.buy_mfi.value) if self.buy_ema_enabled.value: try: conditions.append( ta.EMA(dataframe, timeperiod=int(self.buy_fastema.value)) > ta.EMA(dataframe, timeperiod=int(self.buy_slowema.value))) except Exception: pass # TRIGGERS if self.buy_trigger.value == 'bb_lower1': conditions.append(dataframe["close"] < dataframe['bb_lowerband1']) if self.buy_trigger.value == 'bb_lower2': conditions.append(dataframe["close"] < dataframe['bb_lowerband2']) if self.buy_trigger.value == 'bb_lower3': conditions.append(dataframe["close"] < dataframe['bb_lowerband3']) if self.buy_trigger.value == 'bb_lower4': conditions.append(dataframe["close"] < dataframe['bb_lowerband4']) # Check that volume is not 0 conditions.append(dataframe['volume'] > 0) if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # GUARDS if self.sell_rsi_enabled.value: conditions.append(dataframe['rsi'] > self.sell_rsi.value) if self.sell_mfi_enabled.value: conditions.append(dataframe['mfi'] > self.sell_mfi.value) if self.sell_ema_enabled.value: try: conditions.append( ta.EMA(dataframe, timeperiod=int(self.sell_fastema.value)) < ta.EMA(dataframe, timeperiod=int(self.sell_slowema.value))) except Exception: pass # TRIGGERS if self.sell_trigger.value == 'sell-bb_upper1': conditions.append(dataframe["close"] > dataframe['bb_upperband1']) if self.sell_trigger.value == 'sell-bb_upper2': conditions.append(dataframe["close"] > dataframe['bb_upperband2']) if self.sell_trigger.value == 'sell-bb_upper3': conditions.append(dataframe["close"] > dataframe['bb_upperband3']) if self.sell_trigger.value == 'sell-bb_upper4': conditions.append(dataframe["close"] > dataframe['bb_upperband4']) # Check that volume is not 0 conditions.append(dataframe['volume'] > 0) if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1 return dataframe
class Solipsis5(IStrategy): ## Buy Space Hyperopt Variables # Base Pair Params base_mp = IntParameter(10, 50, default=30, space='buy', load=True, optimize=True) base_rmi_max = IntParameter(30, 60, default=50, space='buy', load=True, optimize=True) base_rmi_min = IntParameter(0, 30, default=20, space='buy', load=True, optimize=True) base_ma_streak = IntParameter(1, 4, default=1, space='buy', load=True, optimize=True) base_rmi_streak = IntParameter(3, 8, default=3, space='buy', load=True, optimize=True) base_trigger = CategoricalParameter(['pcc', 'rmi', 'none'], default='rmi', space='buy', load=True, optimize=True) inf_pct_adr = DecimalParameter(0.70, 0.99, default=0.80, space='buy', load=True, optimize=True) # BTC Informative xbtc_guard = CategoricalParameter(['strict', 'lazy', 'none'], default='lazy', space='buy', optimize=True) xbtc_base_rmi = IntParameter(20, 70, default=40, space='buy', load=True, optimize=True) # BTC / ETH Stake Parameters xtra_base_stake_rmi = IntParameter(10, 50, default=50, space='buy', load=True, optimize=True) xtra_base_fiat_rmi = IntParameter(30, 70, default=50, space='buy', load=True, optimize=True) ## Sell Space Params are being used for both custom_stoploss and custom_sell # Custom Sell Profit (formerly Dynamic ROI) csell_roi_type = CategoricalParameter(['static', 'decay', 'step'], default='step', space='sell', load=True, optimize=True) csell_roi_time = IntParameter(720, 1440, default=720, space='sell', load=True, optimize=True) csell_roi_start = DecimalParameter(0.01, 0.05, default=0.01, space='sell', load=True, optimize=True) csell_roi_end = DecimalParameter(0.0, 0.01, default=0, space='sell', load=True, optimize=True) csell_trend_type = CategoricalParameter(['rmi', 'ssl', 'candle', 'any', 'none'], default='any', space='sell', load=True, optimize=True) csell_pullback = CategoricalParameter([True, False], default=True, space='sell', load=True, optimize=True) csell_pullback_amount = DecimalParameter(0.005, 0.03, default=0.01, space='sell', load=True, optimize=True) csell_pullback_respect_roi = CategoricalParameter([True, False], default=False, space='sell', load=True, optimize=True) csell_endtrend_respect_roi = CategoricalParameter([True, False], default=False, space='sell', load=True, optimize=True) # Custom Stoploss cstop_loss_threshold = DecimalParameter(-0.05, -0.01, default=-0.03, space='sell', load=True, optimize=True) cstop_bail_how = CategoricalParameter(['roc', 'time', 'any', 'none'], default='none', space='sell', load=True, optimize=True) cstop_bail_roc = DecimalParameter(-5.0, -1.0, default=-3.0, space='sell', load=True, optimize=True) cstop_bail_time = IntParameter(60, 1440, default=720, space='sell', load=True, optimize=True) cstop_bail_time_trend = CategoricalParameter([True, False], default=True, space='sell', load=True, optimize=True) timeframe = '5m' inf_timeframe = '1h' buy_params = {} sell_params = {} minimal_roi = { "0": 100 } stoploss = -0.99 use_custom_stoploss = True # Recommended use_sell_signal = True sell_profit_only = True ignore_roi_if_buy_signal = True # Required startup_candle_count: int = 233 process_only_new_candles = False # Strategy Specific Variable Storage custom_trade_info = {} custom_fiat = "USD" # Only relevant if stake is BTC or ETH custom_btc_inf = False # Don't change this. """ Informative Pair Definitions """ def informative_pairs(self): # add all whitelisted pairs on informative timeframe pairs = self.dp.current_whitelist() informative_pairs = [(pair, self.inf_timeframe) for pair in pairs] # add extra informative pairs if the stake is BTC or ETH if self.config['stake_currency'] in ('BTC', 'ETH'): for pair in pairs: coin, stake = pair.split('/') coin_fiat = f"{coin}/{self.custom_fiat}" informative_pairs += [(coin_fiat, self.timeframe)] stake_fiat = f"{self.config['stake_currency']}/{self.custom_fiat}" informative_pairs += [(stake_fiat, self.timeframe)] # if BTC/STAKE is not in whitelist, add it as an informative pair on both timeframes else: btc_stake = f"BTC/{self.config['stake_currency']}" if not btc_stake in pairs: informative_pairs += [(btc_stake, self.timeframe)] return informative_pairs """ Indicator Definitions """ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if not metadata['pair'] in self.custom_trade_info: self.custom_trade_info[metadata['pair']] = {} if not 'had-trend' in self.custom_trade_info[metadata["pair"]]: self.custom_trade_info[metadata['pair']]['had-trend'] = False ## Base Timeframe / Pair # Kaufmann Adaptive Moving Average dataframe['kama'] = ta.KAMA(dataframe, length=233) # RMI: https://www.tradingview.com/script/kwIt9OgQ-Relative-Momentum-Index/ dataframe['rmi'] = cta.RMI(dataframe, length=24, mom=5) # Momentum Pinball: https://www.tradingview.com/script/fBpVB1ez-Momentum-Pinball-Indicator/ dataframe['roc-mp'] = ta.ROC(dataframe, timeperiod=1) dataframe['mp'] = ta.RSI(dataframe['roc-mp'], timeperiod=3) # MA Streak: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/ dataframe['mastreak'] = cta.mastreak(dataframe, period=4) # Percent Change Channel: https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/ upper, mid, lower = cta.pcc(dataframe, period=40, mult=3) dataframe['pcc-lowerband'] = lower dataframe['pcc-upperband'] = upper lookup_idxs = dataframe.index.values - (abs(dataframe['mastreak'].values) + 1) valid_lookups = lookup_idxs >= 0 dataframe['sbc'] = np.nan dataframe.loc[valid_lookups, 'sbc'] = dataframe['close'].to_numpy()[lookup_idxs[valid_lookups].astype(int)] dataframe['streak-roc'] = 100 * (dataframe['close'] - dataframe['sbc']) / dataframe['sbc'] # Trends, Peaks and Crosses dataframe['candle-up'] = np.where(dataframe['close'] >= dataframe['open'],1,0) dataframe['candle-up-trend'] = np.where(dataframe['candle-up'].rolling(5).sum() >= 3,1,0) 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['rmi-dn'] = np.where(dataframe['rmi'] <= dataframe['rmi'].shift(),1,0) dataframe['rmi-dn-count'] = dataframe['rmi-dn'].rolling(8).sum() dataframe['streak-bo'] = np.where(dataframe['streak-roc'] < dataframe['pcc-lowerband'],1,0) dataframe['streak-bo-count'] = dataframe['streak-bo'].rolling(8).sum() # Indicators used only for ROI and Custom Stoploss ssldown, sslup = cta.SSLChannels_ATR(dataframe, length=21) dataframe['sroc'] = cta.SROC(dataframe, roclen=21, emalen=13, smooth=21) dataframe['ssl-dir'] = np.where(sslup > ssldown,'up','down') # Base pair informative timeframe indicators informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe) # Get the "average day range" between the 1d high and 1d low to set up guards informative['1d-high'] = informative['close'].rolling(24).max() informative['1d-low'] = informative['close'].rolling(24).min() informative['adr'] = informative['1d-high'] - informative['1d-low'] dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_timeframe, ffill=True) # Other stake specific informative indicators # e.g if stake is BTC and current coin is XLM (pair: XLM/BTC) if self.config['stake_currency'] in ('BTC', 'ETH'): coin, stake = metadata['pair'].split('/') fiat = self.custom_fiat coin_fiat = f"{coin}/{fiat}" stake_fiat = f"{stake}/{fiat}" # Informative COIN/FIAT e.g. XLM/USD - Base Timeframe coin_fiat_tf = self.dp.get_pair_dataframe(pair=coin_fiat, timeframe=self.timeframe) dataframe[f"{fiat}_rmi"] = cta.RMI(coin_fiat_tf, length=55, mom=5) # Informative STAKE/FIAT e.g. BTC/USD - Base Timeframe stake_fiat_tf = self.dp.get_pair_dataframe(pair=stake_fiat, timeframe=self.timeframe) dataframe[f"{stake}_rmi"] = cta.RMI(stake_fiat_tf, length=55, mom=5) # Informatives for BTC/STAKE if not in whitelist else: pairs = self.dp.current_whitelist() btc_stake = f"BTC/{self.config['stake_currency']}" if not btc_stake in pairs: self.custom_btc_inf = True # BTC/STAKE - Base Timeframe btc_stake_tf = self.dp.get_pair_dataframe(pair=btc_stake, timeframe=self.timeframe) dataframe['BTC_rmi'] = cta.RMI(btc_stake_tf, length=55, mom=5) dataframe['BTC_close'] = btc_stake_tf['close'] dataframe['BTC_kama'] = ta.KAMA(btc_stake_tf, length=144) return dataframe """ Buy Signal """ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # Informative Timeframe Guards conditions.append( (dataframe['close'] <= dataframe[f"1d-low_{self.inf_timeframe}"] + (self.inf_pct_adr.value * dataframe[f"adr_{self.inf_timeframe}"])) ) # Base Timeframe Guards conditions.append( (dataframe['rmi-dn-count'] >= self.base_rmi_streak.value) & (dataframe['streak-bo-count'] >= self.base_ma_streak.value) & (dataframe['rmi'] <= self.base_rmi_max.value) & (dataframe['rmi'] >= self.base_rmi_min.value) & (dataframe['mp'] <= self.base_mp.value) ) # Base Timeframe Trigger if self.base_trigger.value == 'pcc': conditions.append(qtpylib.crossed_above(dataframe['streak-roc'], dataframe['pcc-lowerband'])) if self.base_trigger.value == 'rmi': conditions.append(dataframe['rmi-up-trend'] == 1) # Extra conditions for */BTC and */ETH stakes on additional informative pairs if self.config['stake_currency'] in ('BTC', 'ETH'): conditions.append( (dataframe[f"{self.custom_fiat}_rmi"] > self.xtra_base_fiat_rmi.value) | (dataframe[f"{self.config['stake_currency']}_rmi"] < self.xtra_base_stake_rmi.value) ) # Extra conditions for BTC/STAKE if not in whitelist else: if self.custom_btc_inf: if self.xbtc_guard.value == 'strict': conditions.append( ( (dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) & (dataframe['BTC_close'] > dataframe['BTC_kama']) ) ) if self.xbtc_guard.value == 'lazy': conditions.append( (dataframe['close'] > dataframe['kama']) | ( (dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) & (dataframe['BTC_close'] > dataframe['BTC_kama']) ) ) conditions.append(dataframe['volume'].gt(0)) if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), 'buy'] = 1 return dataframe """ Sell Signal """ def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['sell'] = 0 return dataframe """ Custom Stoploss """ def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) last_candle = dataframe.iloc[-1].squeeze() trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60) in_trend = self.custom_trade_info[trade.pair]['had-trend'] # Determine how we sell when we are in a loss if current_profit < self.cstop_loss_threshold.value: if self.cstop_bail_how.value == 'roc' or self.cstop_bail_how.value == 'any': # Dynamic bailout based on rate of change if last_candle['sroc'] <= self.cstop_bail_roc.value: return 0.01 if self.cstop_bail_how.value == 'time' or self.cstop_bail_how.value == 'any': # Dynamic bailout based on time, unless time_trend is true and there is a potential reversal if trade_dur > self.cstop_bail_time.value: if self.cstop_bail_time_trend.value == True and in_trend == True: return 1 else: return 0.01 return 1 """ Custom Sell """ def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs): dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) last_candle = dataframe.iloc[-1].squeeze() trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60) max_profit = max(0, trade.calc_profit_ratio(trade.max_rate)) pullback_value = max(0, (max_profit - self.csell_pullback_amount.value)) in_trend = False # Determine our current ROI point based on the defined type if self.csell_roi_type.value == 'static': min_roi = self.csell_roi_start.value elif self.csell_roi_type.value == 'decay': min_roi = cta.linear_decay(self.csell_roi_start.value, self.csell_roi_end.value, 0, self.csell_roi_time.value, trade_dur) elif self.csell_roi_type.value == 'step': if trade_dur < self.csell_roi_time.value: min_roi = self.csell_roi_start.value else: min_roi = self.csell_roi_end.value # Determine if there is a trend if self.csell_trend_type.value == 'rmi' or self.csell_trend_type.value == 'any': if last_candle['rmi-up-trend'] == 1: in_trend = True if self.csell_trend_type.value == 'ssl' or self.csell_trend_type.value == 'any': if last_candle['ssl-dir'] == 'up': in_trend = True if self.csell_trend_type.value == 'candle' or self.csell_trend_type.value == 'any': if last_candle['candle-up-trend'] == 1: in_trend = True # Don't sell if we are in a trend unless the pullback threshold is met if in_trend == True and current_profit > 0: # Record that we were in a trend for this trade/pair for a more useful sell message later self.custom_trade_info[trade.pair]['had-trend'] = True # If pullback is enabled and profit has pulled back allow a sell, maybe if self.csell_pullback.value == True and (current_profit <= pullback_value): if self.csell_pullback_respect_roi.value == True and current_profit > min_roi: return 'intrend_pullback_roi' elif self.csell_pullback_respect_roi.value == False: if current_profit > min_roi: return 'intrend_pullback_roi' else: return 'intrend_pullback_noroi' # We are in a trend and pullback is disabled or has not happened or various criteria were not met, hold return None # If we are not in a trend, just use the roi value elif in_trend == False: if self.custom_trade_info[trade.pair]['had-trend']: if current_profit > min_roi: self.custom_trade_info[trade.pair]['had-trend'] = False return 'trend_roi' elif self.csell_endtrend_respect_roi.value == False: self.custom_trade_info[trade.pair]['had-trend'] = False return 'trend_noroi' elif current_profit > min_roi: return 'notrend_roi' else: return None
class Seb(IStrategy): """ Strategy 001 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies How to use it? > python3 ./freqtrade/main.py -s Strategy001 """ # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi" minimal_roi = {"60": 0.01, "30": 0.03, "20": 0.04, "0": 0.05} # Optimal stoploss designed for the strategy # This attribute will be overridden if the config file contains "stoploss" stoploss = -0.10 # Optimal timeframe for the strategy timeframe = '5m' # trailing stoploss trailing_stop = False trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.02 # run "populate_indicators" only for new candle process_only_new_candles = False # Experimental settings (configuration will overide these if set) use_sell_signal = True sell_profit_only = True ignore_roi_if_buy_signal = False # Optional order type mapping order_types = { 'buy': 'limit', 'sell': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } buy_volumeAVG = IntParameter(low=50, high=300, default=70, space='buy', optimize=True) buy_rsi = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) buy_fastd = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) buy_fishRsiNorma = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) sell_rsi = IntParameter(low=1, high=100, default=70, space='sell', optimize=True) sell_minusDI = IntParameter(low=1, high=100, default=50, space='sell', optimize=True) sell_fishRsiNorma = IntParameter(low=1, high=100, default=50, space='sell', optimize=True) sell_trigger = CategoricalParameter(["rsi-macd-minusdi", "sar-fisherRsi"], default=30, space='sell', optimize=True) # Buy hyperspace params: buy_params = { "buy_fastd": 1, "buy_fishRsiNorma": 5, "buy_rsi": 26, "buy_volumeAVG": 150, } # Sell hyperspace params: sell_params = { "sell_fishRsiNorma": 30, "sell_minusDI": 4, "sell_rsi": 74, "sell_trigger": "rsi-macd-minusdi", } def informative_pairs(self): """ Define additional, informative pair/interval combinations to be cached from the exchange. These pair/interval combinations are non-tradeable, unless they are part of the whitelist as well. For more information, please consult the documentation :return: List of tuples in the format (pair, interval) Sample: return [("ETH/USDT", "5m"), ("BTC/USDT", "15m"), ] """ return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Adds several different TA indicators to the given DataFrame Performance Note: For the best performance be frugal on the number of indicators you are using. Let uncomment only the indicator you are using in your strategies or your hyperopt configuration, otherwise you will waste your memory and CPU usage. """ dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20) dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) heikinashi = qtpylib.heikinashi(dataframe) dataframe['ha_open'] = heikinashi['open'] dataframe['ha_close'] = heikinashi['close'] dataframe['adx'] = ta.ADX(dataframe) dataframe['rsi'] = ta.RSI(dataframe) macd = ta.MACD(dataframe) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] dataframe['macdhist'] = macd['macdhist'] bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0) dataframe['bb_lowerband'] = bollinger['lowerband'] dataframe['bb_middleband'] = bollinger['middleband'] dataframe['bb_upperband'] = bollinger['upperband'] # Stoch stoch = ta.STOCH(dataframe) dataframe['slowk'] = stoch['slowk'] # Commodity Channel Index: values Oversold:<-100, Overbought:>100 dataframe['cci'] = ta.CCI(dataframe) # Stoch stoch = ta.STOCHF(dataframe, 5) dataframe['fastd'] = stoch['fastd'] dataframe['fastk'] = stoch['fastk'] dataframe['fastk-previous'] = dataframe.fastk.shift(1) dataframe['fastd-previous'] = dataframe.fastd.shift(1) # Slow Stoch slowstoch = ta.STOCHF(dataframe, 50) dataframe['slowfastd'] = slowstoch['fastd'] dataframe['slowfastk'] = slowstoch['fastk'] dataframe['slowfastk-previous'] = dataframe.slowfastk.shift(1) dataframe['slowfastd-previous'] = dataframe.slowfastd.shift(1) # RSI dataframe['rsi'] = ta.RSI(dataframe) # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) rsi = 0.1 * (dataframe['rsi'] - 50) dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) # Bollinger bands bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] # SAR Parabol dataframe['sar'] = ta.SAR(dataframe) # Hammer: values [0, 100] dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe) # SMA - Simple Moving Average dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Based on TA indicators, populates the buy signal for the given dataframe :param dataframe: DataFrame :return: DataFrame with buy column """ dataframe.loc[( qtpylib.crossed_above(dataframe['ema20'], dataframe['ema50']) & (dataframe['ha_close'] > dataframe['ema20']) & (dataframe['ha_open'] < dataframe['ha_close']) # green bar ), 'buy'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Based on TA indicators, populates the sell signal for the given dataframe :param dataframe: DataFrame :return: DataFrame with buy column """ dataframe.loc[( qtpylib.crossed_above(dataframe['ema50'], dataframe['ema100']) & (dataframe['ha_close'] < dataframe['ema20']) & (dataframe['ha_open'] > dataframe['ha_close']) # red bar ), 'sell'] = 1 return dataframe
class CryptoFrog(IStrategy): # ROI table - this strat REALLY benefits from roi and trailing hyperopt: minimal_roi = {"0": 0.213, "39": 0.103, "96": 0.037, "166": 0} # Stoploss: stoploss = -0.085 # Trailing stop: trailing_stop = True trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.047 trailing_only_offset_is_reached = False use_custom_stoploss = True custom_stop = { # Linear Decay Parameters 'decay-time': 166, # minutes to reach end, I find it works well to match this to the final ROI value - default 1080 'decay-delay': 0, # minutes to wait before decay starts 'decay-start': -0.085, # -0.32118, # -0.07163, # starting value: should be the same or smaller than initial stoploss - default -0.30 'decay-end': -0.02, # ending value - default -0.03 # Profit and TA 'cur-min-diff': 0.03, # diff between current and minimum profit to move stoploss up to min profit point 'cur-threshold': -0.02, # how far negative should current profit be before we consider moving it up based on cur/min or roc 'roc-bail': -0.03, # value for roc to use for dynamic bailout 'rmi-trend': 50, # rmi-slow value to pause stoploss decay 'bail-how': 'immediate', # set the stoploss to the atr offset below current price, or immediate # Positive Trailing 'pos-trail': True, # enable trailing once positive 'pos-threshold': 0.005, # trail after how far positive 'pos-trail-dist': 0.015 # how far behind to place the trail } # Dynamic ROI droi_trend_type = CategoricalParameter(['rmi', 'ssl', 'candle', 'any'], default='any', space='sell', optimize=True) droi_pullback = CategoricalParameter([True, False], default=True, space='sell', optimize=True) droi_pullback_amount = DecimalParameter(0.005, 0.02, default=0.005, space='sell') droi_pullback_respect_table = CategoricalParameter([True, False], default=False, space='sell', optimize=True) # Custom Stoploss cstp_threshold = DecimalParameter(-0.05, 0, default=-0.03, space='sell') cstp_bail_how = CategoricalParameter(['roc', 'time', 'any'], default='roc', space='sell', optimize=True) cstp_bail_roc = DecimalParameter(-0.05, -0.01, default=-0.03, space='sell') cstp_bail_time = IntParameter(720, 1440, default=720, space='sell') stoploss = custom_stop['decay-start'] custom_trade_info = {} custom_current_price_cache: TTLCache = TTLCache(maxsize=100, ttl=300) # 5 minutes # run "populate_indicators" only for new candle process_only_new_candles = False # Experimental settings (configuration will overide these if set) use_sell_signal = True sell_profit_only = False ignore_roi_if_buy_signal = False use_dynamic_roi = True timeframe = '5m' informative_timeframe = '1h' # Optional order type mapping order_types = { 'buy': 'limit', 'sell': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } plot_config = { 'main_plot': { 'Smooth_HA_H': { 'color': 'orange' }, 'Smooth_HA_L': { 'color': 'yellow' }, }, 'subplots': { "StochRSI": { 'srsi_k': { 'color': 'blue' }, 'srsi_d': { 'color': 'red' }, }, "MFI": { 'mfi': { 'color': 'green' }, }, "BBEXP": { 'bbw_expansion': { 'color': 'orange' }, }, "FAST": { 'fastd': { 'color': 'red' }, 'fastk': { 'color': 'blue' }, }, "SQZMI": { 'sqzmi': { 'color': 'lightgreen' }, }, "VFI": { 'vfi': { 'color': 'lightblue' }, }, "DMI": { 'dmi_plus': { 'color': 'orange' }, 'dmi_minus': { 'color': 'yellow' }, }, "EMACO": { 'emac_1h': { 'color': 'red' }, 'emao_1h': { 'color': 'blue' }, }, } } def informative_pairs(self): pairs = self.dp.current_whitelist() #pairs.append("BTC/USDT") #pairs.append("ETH/USDT") informative_pairs = [(pair, self.informative_timeframe) for pair in pairs] return informative_pairs ## 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 ## stolen from Obelisk's Ichi strat code and backtest blog post, and Solipsis4 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 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.informative_timeframe: dataframe = self.do_indicators(dataframe, metadata) else: if not self.dp: return dataframe informative = self.dp.get_pair_dataframe( pair=metadata['pair'], timeframe=self.informative_timeframe) informative = self.do_indicators(informative.copy(), metadata) dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.informative_timeframe, ffill=True) skip_columns = [(s + "_" + self.informative_timeframe) for s in [ 'date', 'open', 'high', 'low', 'close', 'volume', 'emac', 'emao' ]] dataframe.rename(columns=lambda s: s.replace( "_{}".format(self.informative_timeframe), "") 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') return dataframe ## cryptofrog signals def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[( ( ## close ALWAYS needs to be lower than the heiken low at 5m (dataframe['close'] < dataframe['Smooth_HA_L']) & ## Hansen's HA EMA at informative timeframe (dataframe['emac_1h'] < dataframe['emao_1h'])) & (( ## potential uptick incoming so buy (dataframe['bbw_expansion'] == 1) & (dataframe['sqzmi'] == False) & ((dataframe['mfi'] < 20) | (dataframe['dmi_minus'] > 30))) | ( # this tries to find extra buys in undersold regions (dataframe['close'] < dataframe['sar']) & ((dataframe['srsi_d'] >= dataframe['srsi_k']) & (dataframe['srsi_d'] < 30)) & ((dataframe['fastd'] > dataframe['fastk']) & (dataframe['fastd'] < 23)) & (dataframe['mfi'] < 30)) | ( # find smaller temporary dips in sideways (((dataframe['dmi_minus'] > 30) & qtpylib.crossed_above( dataframe['dmi_minus'], dataframe['dmi_plus'])) & (dataframe['close'] < dataframe['bb_lowerband'])) | ( ## if nothing else is making a buy signal ## just throw in any old SQZMI shit based fastd ## this needs work! (dataframe['sqzmi'] == True) & ((dataframe['fastd'] > dataframe['fastk']) & (dataframe['fastd'] < 20)))) ## volume sanity checks & (dataframe['vfi'] < 0.0) & (dataframe['volume'] > 0))), 'buy'] = 1 return dataframe ## more going on here def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[( ( ## close ALWAYS needs to be higher than the heiken high at 5m (dataframe['close'] > dataframe['Smooth_HA_H']) & ## Hansen's HA EMA at informative timeframe (dataframe['emac_1h'] > dataframe['emao_1h'])) & ( ## try to find oversold regions with a corresponding BB expansion ((dataframe['bbw_expansion'] == 1) & ((dataframe['mfi'] > 80) | (dataframe['dmi_plus'] > 30))) ## volume sanity checks & (dataframe['vfi'] > 0.0) & (dataframe['volume'] > 0))), 'sell'] = 1 return dataframe """ Everything from here completely stolen from the godly work of @werkkrew Custom Stoploss """ 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] # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!) else: 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 """ Freqtrade ROI Overload for dynamic ROI functionality """ 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] # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!) else: 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 sell 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 """ Stripped down version from Schism, meant only to update the price data a bit more frequently than the default instead of getting all sorts of trade information """ 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 # nested hyperopt class class HyperOpt: # defining as dummy, so that no error is thrown about missing # sell indicator space when hyperopting for all spaces @staticmethod def indicator_space() -> List[Dimension]: return []
class SMAOffsetStrategy(IStrategy): INTERFACE_VERSION = 2 # ROI table: minimal_roi = {"0": 1} # Stoploss: stoploss = -0.5 base_nb_candles_buy = IntParameter( 5, 80, default=buy_params['base_nb_candles_buy'], space='buy') base_nb_candles_sell = IntParameter( 5, 80, default=sell_params['base_nb_candles_sell'], space='sell') low_offset = DecimalParameter(0.9, 0.99, default=buy_params['low_offset'], space='buy') high_offset = DecimalParameter(0.99, 1.1, default=sell_params['high_offset'], space='sell') buy_trigger = CategoricalParameter([SMA, EMA], default=buy_params['buy_trigger'], space='buy') sell_trigger = CategoricalParameter([SMA, EMA], default=sell_params['sell_trigger'], space='sell') # Trailing stop: trailing_stop = False trailing_stop_positive = 0.1 trailing_stop_positive_offset = 0 trailing_only_offset_is_reached = False # Optimal timeframe for the strategy timeframe = '5m' use_sell_signal = True sell_profit_only = False process_only_new_candles = True startup_candle_count = 30 plot_config = { 'main_plot': { 'ma_offset_buy': { 'color': 'orange' }, 'ma_offset_sell': { 'color': 'orange' }, }, } use_custom_stoploss = False def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # uncomment for plotting #if self.buy_trigger.value == 'EMA': # dataframe['ma_buy'] = ta.EMA(dataframe, timeperiod=self.base_nb_candles_buy.value) #else: # dataframe['ma_buy'] = ta.SMA(dataframe, timeperiod=self.base_nb_candles_buy.value) #if self.sell_trigger.value == 'EMA': # dataframe['ma_sell'] = ta.EMA(dataframe, timeperiod=self.base_nb_candles_sell.value) #else: # dataframe['ma_sell'] = ta.SMA(dataframe, timeperiod=self.base_nb_candles_sell.value) #dataframe['ma_offset_buy'] = dataframe['ma_buy'] * self.low_offset.value #dataframe['ma_offset_sell'] = dataframe['ma_sell'] * self.high_offset.value return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if self.buy_trigger.value == EMA: dataframe['ma_buy'] = ta.EMA(dataframe, timeperiod=int( self.base_nb_candles_buy.value)) else: dataframe['ma_buy'] = ta.SMA(dataframe, timeperiod=int( self.base_nb_candles_buy.value)) dataframe[ 'ma_offset_buy'] = dataframe['ma_buy'] * self.low_offset.value dataframe.loc[((dataframe['close'] < dataframe['ma_offset_buy']) & (dataframe['volume'] > 0)), 'buy'] = 1 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if self.sell_trigger.value == EMA: dataframe['ma_sell'] = ta.EMA(dataframe, timeperiod=int( self.base_nb_candles_sell.value)) else: dataframe['ma_sell'] = ta.SMA(dataframe, timeperiod=int( self.base_nb_candles_sell.value)) dataframe[ 'ma_offset_sell'] = dataframe['ma_sell'] * self.high_offset.value dataframe.loc[((dataframe['close'] > dataframe['ma_offset_sell']) & (dataframe['volume'] > 0)), 'sell'] = 1 return dataframe
class CombinedBinHAndClucV6H(IStrategy): minimal_roi = {"0": 0.0181} max_open_trades = 5 # Using custom stoploss stoploss = -0.99 use_custom_stoploss = True # Trailing stoploss trailing_stop = True trailing_only_offset_is_reached = True trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.025 timeframe = '5m' informative_timeframe = '1h' # derived from EMA 200 startup_candle_count: int = 200 # Run "populate_indicators()" only for new candle. process_only_new_candles = False use_sell_signal = True sell_profit_only = True sell_profit_offset = 0.001 ignore_roi_if_buy_signal = True # hyperspace default buy params buy_params = { 'buy_bin_bbdelta_close': 0.031, 'buy_bin_closedelta_close': 0.018, 'buy_bin_tail_bbdelta': 0.233, 'buy_bin_guard': True, 'buy_cluc_close_bblowerband': 0.993, 'buy_cluc_volume': 21, 'buy_cluc_guard': True, 'buy_long_rsi_diff': 43.276, 'buy_bin_enable': True, 'buy_cluc_enable': True, 'buy_long_enable': True, 'buy_minimum_conditions': 1 } # hyperspace default sell params sell_params = { 'sell_roi_override_rsi_threshold': 50, # to disable holding with high RSI set to 100 'cstp_loss_threshold': 0, 'cstp_bail_time': 5 } # if you want to see which buy conditions were met # or if there is an trade exit override due to high RSI set to True # logger will output the buy and trade exit conditions cust_log_verbose = False ########################################################################################################### ## IMPORTANT NOTICE REGARDING HYPEROPT: ## ## Use the optimize dict below to enable optimization (set to True) of one or more ## ## hyperspace parameters. ## ## ## ## Please be aware: Enabling ALL at once will very likely produce very low results! ## ## So please enable max. 1-2 parameters at once, you'll need to play around ## ## what makes more or less sense. At the end it always depends on a lot more ## ## parameters (stake, max trades, hyperopt-loss, etc.) ## ########################################################################################################### # use this switches to enable the optimization of hyperspace params cust_optimize = { 'buy_bin_bbdelta_close': False, 'buy_bin_closedelta_close': False, 'buy_bin_tail_bbdelta': False, 'buy_bin_guard': False, 'buy_cluc_close_bblowerband': False, 'buy_cluc_volume': False, 'buy_cluc_guard': False, 'buy_long_rsi_diff': False, 'buy_bin_enable': False, 'buy_cluc_enable': False, 'buy_long_enable': False, 'buy_minimum_conditions': False, 'sell_roi_override_rsi_threshold': False, 'cstp_bail_time': False, 'cstp_loss_threshold': False } # bin buy parameters buy_bin_bbdelta_close = DecimalParameter( 0.0, 0.05, default=0.031, space='buy', optimize=cust_optimize['buy_bin_bbdelta_close'], load=True) buy_bin_closedelta_close = DecimalParameter( 0.0, 0.03, default=0.018, decimals=4, space='buy', optimize=cust_optimize['buy_bin_closedelta_close'], load=True) buy_bin_tail_bbdelta = DecimalParameter( 0.0, 1.0, default=0.233, decimals=3, space='buy', optimize=cust_optimize['buy_bin_tail_bbdelta'], load=True) buy_bin_guard = CategoricalParameter( [True, False], default=True, space='buy', optimize=cust_optimize['buy_bin_guard'], load=True) # cluc buy parameters buy_cluc_close_bblowerband = DecimalParameter( 0.0, 1.5, default=0.993, decimals=3, space='buy', optimize=cust_optimize['buy_cluc_close_bblowerband'], load=True) buy_cluc_volume = IntParameter(10, 40, default=21, space='buy', optimize=cust_optimize['buy_cluc_volume'], load=True) buy_cluc_guard = CategoricalParameter( [True, False], default=True, space='buy', optimize=cust_optimize['buy_cluc_guard'], load=True) # log buy parameters buy_long_rsi_diff = DecimalParameter( 40, 45, default=43.276, decimals=3, space='buy', optimize=cust_optimize['buy_long_rsi_diff'], load=True) # enable bin, cluc or long buy_bin_enable = CategoricalParameter( [True, False], default=True, space='buy', optimize=cust_optimize['buy_bin_enable'], load=True) buy_cluc_enable = CategoricalParameter( [True, False], default=True, space='buy', optimize=cust_optimize['buy_cluc_enable'], load=True) buy_long_enable = CategoricalParameter( [True, False], default=True, space='buy', optimize=cust_optimize['buy_long_enable'], load=True) # minimum conditions to match in buy buy_minimum_conditions = IntParameter( 1, 2, default=1, space='buy', optimize=cust_optimize['buy_minimum_conditions'], load=True) # if RSI above threshold, override ROI sell_roi_override_rsi_threshold = IntParameter( 40, 70, default=50, space='sell', optimize=cust_optimize['sell_roi_override_rsi_threshold'], load=True) # custom stoploss, if trade open > x hours and loss > threshold then sell cstp_bail_time = IntParameter(1, 36, default=5, space='sell', optimize=cust_optimize['cstp_bail_time']) cstp_loss_threshold = DecimalParameter( -0.25, 0, default=0, decimals=2, space='sell', optimize=cust_optimize['cstp_loss_threshold']) """ Informative Pairs """ def informative_pairs(self): pairs = self.dp.current_whitelist() informative_pairs = [(pair, self.informative_timeframe) for pair in pairs] return informative_pairs """ Informative Timeframe Indicators """ def get_informative_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = self.dp.get_pair_dataframe( pair=metadata['pair'], timeframe=self.informative_timeframe) # EMA dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) # RSI dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) # SSL Channels ssldown, sslup = SSLChannels_ATR(dataframe, 20) dataframe['ssl-up'] = sslup dataframe['ssl-down'] = ssldown dataframe['ssl-dir'] = np.where(sslup > ssldown, 'up', 'down') return dataframe """ Main Timeframe Indicators """ def get_main_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # strategy BinHV45 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() # strategy ClucMay72018 bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] dataframe['bb_middleband'] = bollinger['mid'] dataframe['bb_upperband'] = bollinger['upper'] dataframe['volume_mean_slow'] = dataframe['volume'].rolling( window=30).mean() # EMA dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) # SMA dataframe['sma_5'] = ta.EMA(dataframe, timeperiod=5) # RSI dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) # drop NAN in hyperopt to fix "'<' not supported between instances of 'str' and 'int' error if self.config['runmode'].value == 'hyperopt': dataframe = dataframe.dropna() return dataframe """ Populate Informative and Main Timeframe Indicators """ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if self.config['runmode'].value in ('backtest', 'hyperopt'): assert (timeframe_to_minutes(self.timeframe) <= 5), "Backtest this strategy in a timeframe of 5m or less." assert self.dp, "DataProvider is required for multiple timeframes." informative = self.get_informative_indicators(dataframe, metadata) dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.informative_timeframe, ffill=True) dataframe = self.get_main_indicators(dataframe, metadata) return dataframe """ Buy Signal """ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # reset additional dataframe rows dataframe.loc[:, 'buy_cond_bin'] = False dataframe.loc[:, 'buy_cond_cluc'] = False dataframe.loc[:, 'buy_cond_long'] = False dataframe.loc[:, 'conditions_count'] = 0 # strategy BinHV45 + guards by @iterativ dataframe.loc[( # @iterativ adition (guard) (((dataframe['close'] > dataframe['ema_200_1h']) & (dataframe['ema_50'] > dataframe['ema_200']) & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & (self.buy_bin_guard.value == True)) | (self.buy_bin_guard.value == False)) & # strategy BinHV45 dataframe['lower'].shift().gt(0) & dataframe['bbdelta'].gt(dataframe['close'] * self.buy_bin_bbdelta_close.value) & dataframe['closedelta'].gt(dataframe['close'] * self.buy_bin_closedelta_close.value) & dataframe['tail'].lt(dataframe['bbdelta'] * self.buy_bin_tail_bbdelta.value) & dataframe['close'].lt(dataframe['lower'].shift()) & dataframe['close'].le(dataframe['close'].shift()) & (self.buy_bin_enable.value == True)), 'buy_cond_bin'] = 1 # strategy ClucMay72018 + guards by @iterativ dataframe.loc[( # @iterativ addition (guard) (((dataframe['close'] > dataframe['ema_200']) & (dataframe['close'] > dataframe['ema_200_1h']) & (self.buy_cluc_guard.value == True)) | (self.buy_cluc_guard.value == False)) & # strategy ClucMay72018 (dataframe['close'] < dataframe['ema_50']) & (dataframe['close'] < self.buy_cluc_close_bblowerband.value * dataframe['bb_lowerband']) & (dataframe['volume'] < (dataframe['volume_mean_slow'].shift(1) * self.buy_cluc_volume.value)) & (self.buy_cluc_enable.value == True)), 'buy_cond_cluc'] = 1 # long strategy by @iterativ dataframe.loc[((dataframe['close'] < dataframe['sma_5']) & (dataframe['ssl-dir_1h'] == 'up') & (dataframe['ema_50'] > dataframe['ema_200']) & (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) & (dataframe['rsi'] < dataframe['rsi_1h'] - self.buy_long_rsi_diff.value) & (self.buy_long_enable.value == True)), 'buy_cond_long'] = 1 # count the amount of conditions met dataframe.loc[:, 'conditions_count'] = dataframe['buy_cond_bin'].astype( int) + dataframe['buy_cond_cluc'].astype( int) + dataframe['buy_cond_long'].astype(int) # append the minimum amount of conditions to be met conditions.append( dataframe['conditions_count'] >= self.buy_minimum_conditions.value) conditions.append(dataframe['volume'].gt(0)) if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1 # verbose logging enable only for verbose information or troubleshooting if self.cust_log_verbose == True: for index, row in dataframe.iterrows(): if row['buy'] == 1: buy_cond_details = f"count={int(row['conditions_count'])}/bin={int(row['buy_cond_bin'])}/cluc={int(row['buy_cond_cluc'])}/long={int(row['buy_cond_long'])}" logger.info( f"{metadata['pair']} - candle: {row['date']} - buy condition - details: {buy_cond_details}" ) return dataframe """ Sell Signal """ def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] conditions.append( (dataframe['close'] > dataframe['bb_upperband']) & (dataframe['close'].shift(1) > dataframe['bb_upperband'].shift(1)) & (dataframe['volume'] > 0) # Make sure Volume is not 0 ) if conditions: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1 return dataframe """ Custom Stop Loss """ def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: if (current_profit < self.cstp_loss_threshold.value) & ( current_time - timedelta(hours=int(self.cstp_bail_time.value)) > trade.open_date_utc): return 0.01 return self.stoploss """ Trade Exit Confirmation """ def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, **kwargs) -> bool: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() if self.cust_log_verbose == True: logger.info( f"{pair} - candle: {last_candle['date']} - exit trade {sell_reason} with profit {trade.calc_profit_ratio(rate)}" ) # failsafe for user triggered forced sells > always have highest prio! if sell_reason == 'force_sell': return True # Prevent ROI trigger, if there is more potential, in order to maximize profit if last_candle is not None and ((sell_reason == 'roi')): rsi = 0 if 'rsi' in last_candle.index: rsi = last_candle['rsi'] if ((rsi >= self.sell_roi_override_rsi_threshold.value)): if self.cust_log_verbose == True: logger.info( f"{pair} - candle: {last_candle['date']} - not exiting trade with current profit {trade.calc_profit_ratio(rate)}, rsi = {rsi} which is > than {self.sell_roi_override_rsi_threshold.value}" ) return False else: return True return True