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