import logging import numpy as np import pandas as pd from technical import qtpylib, pivots_points from pandas import DataFrame from datetime import datetime, timezone from typing import Optional from functools import reduce import talib.abstract as ta import pandas_ta as pta from freqtrade.strategy import BooleanParameter, CategoricalParameter, DecimalParameter, IStrategy, IntParameter, RealParameter, merge_informative_pair import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.persistence import Trade logger = logging.getLogger(__name__) class ultratank(IStrategy): INTERFACE_VERSION = 3 ### Strategy parameters ### exit_profit_only = False ### No exiting at a loss use_custom_stoploss = True trailing_stop = True position_adjustment_enable = True ignore_roi_if_entry_signal = True use_exit_signal = True stoploss = -0.25 startup_candle_count: int = 30 timeframe = '1h' # DCA Parameters position_adjustment_enable = True max_entry_position_adjustment = 0 max_dca_multiplier = 1 minimal_roi = {'12000': 0.01, '2400': 0.1, '300': 0.15, '180': 0.3, '120': 0.4, '60': 0.45, '0': 0.5} ### Hyperoptable parameters ### # entry optizimation # max_epa = CategoricalParameter([0, 1, 2, 3], default=1, space="entry", optimize=True) ### protections #### # CooldownPeriod cooldown_lookback = IntParameter(0, 48, default=5, space='protection', optimize=True) # StoplossGuard use_stop_protection = BooleanParameter(default=True, space='protection', optimize=True) stop_duration = IntParameter(12, 200, default=5, space='protection', optimize=True) stop_protection_only_per_pair = BooleanParameter(default=False, space='protection', optimize=True) stop_protection_only_per_side = BooleanParameter(default=False, space='protection', optimize=True) stop_protection_trade_limit = IntParameter(1, 10, default=4, space='protection', optimize=True) stop_protection_required_profit = DecimalParameter(-1.0, 3.0, default=0.0, space='protection', optimize=True) # LowProfitPairs use_lowprofit_protection = BooleanParameter(default=True, space='protection', optimize=True) lowprofit_protection_lookback = IntParameter(1, 10, default=6, space='protection', optimize=True) lowprofit_trade_limit = IntParameter(1, 10, default=4, space='protection', optimize=True) lowprofit_stop_duration = IntParameter(1, 100, default=60, space='protection', optimize=True) lowprofit_required_profit = DecimalParameter(-1.0, 3.0, default=0.0, space='protection', optimize=True) lowprofit_only_per_pair = BooleanParameter(default=False, space='protection', optimize=True) # MaxDrawdown use_maxdrawdown_protection = BooleanParameter(default=True, space='protection', optimize=True) maxdrawdown_protection_lookback = IntParameter(1, 10, default=6, space='protection', optimize=True) maxdrawdown_trade_limit = IntParameter(1, 20, default=10, space='protection', optimize=True) maxdrawdown_stop_duration = IntParameter(1, 100, default=6, space='protection', optimize=True) maxdrawdown_allowed_drawdown = DecimalParameter(0.01, 0.1, default=0.0, decimals=2, space='protection', optimize=True) # indicators entry_offset1 = DecimalParameter(low=0.95, high=0.99, decimals=3, default=0.985, space='entry', optimize=True, load=True) entry_offset2 = DecimalParameter(low=0.9, high=0.95, decimals=3, default=0.95, space='entry', optimize=True, load=True) exit_offset1 = DecimalParameter(low=1.01, high=1.05, decimals=3, default=1.035, space='exit', optimize=True, load=True) max_length = CategoricalParameter([24, 48, 72, 96, 144, 192, 240], default=48, space='entry', optimize=False) filterlength = IntParameter(low=30, high=40, default=35, space='exit', optimize=True) smoothing_length = IntParameter(15, 50, default=30, space='entry', optimize=True) atr_entry = DecimalParameter(0.2, 4, default=1, decimals=1, space='entry', optimize=True) atr_exit = DecimalParameter(0.2, 4, default=1, decimals=1, space='exit', optimize=True) # trading entry_rsi = IntParameter(low=10, high=30, default=25, space='entry', optimize=True, load=True) entry_rsi_bear = IntParameter(low=40, high=55, default=45, space='entry', optimize=True, load=True) ema_entry = IntParameter(low=30, high=60, default=40, space='entry', optimize=True, load=True) entry_rsi_bull = IntParameter(low=55, high=75, default=65, space='entry', optimize=True, load=True) entry_wt_bear = IntParameter(low=-30, high=0, default=0, space='entry', optimize=True, load=True) entry_wt_bull = IntParameter(low=-10, high=30, default=15, space='entry', optimize=True, load=True) exit_rsi = IntParameter(low=50, high=80, default=55, space='exit', optimize=True, load=True) ema_exit = IntParameter(low=30, high=60, default=40, space='exit', optimize=True, load=True) ### exiting values ### exit_slope = DecimalParameter(-0.05, 0.1, default=0.04, decimals=2, space='exit', optimize=True) # pick what works entry01 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry02 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry03 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry04 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry05 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry06 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry07 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry08 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry09 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) entry10 = CategoricalParameter([True, False], default=True, space='entry', optimize=True) exit01 = CategoricalParameter([True, False], default=True, space='exit') exit02 = CategoricalParameter([True, False], default=True, space='exit') exit03 = CategoricalParameter([True, False], default=True, space='exit') exit04 = CategoricalParameter([True, False], default=True, space='exit') exit05 = CategoricalParameter([True, False], default=True, space='exit') exit06 = CategoricalParameter([True, False], default=True, space='exit') exit07 = CategoricalParameter([True, False], default=True, space='exit') exit08 = CategoricalParameter([True, False], default=True, space='exit') exit09 = CategoricalParameter([True, False], default=True, space='exit') exit10 = CategoricalParameter([True, False], default=True, space='exit') # dca level optimization dca1 = DecimalParameter(low=0.01, high=0.08, decimals=2, default=0.05, space='entry', optimize=True, load=True) dca2 = DecimalParameter(low=0.08, high=0.15, decimals=2, default=0.1, space='entry', optimize=True, load=True) dca3 = DecimalParameter(low=0.15, high=0.25, decimals=2, default=0.15, space='entry', optimize=True, load=True) #trailing stop loss optimiziation tsl_target5 = DecimalParameter(low=0.2, high=0.4, decimals=1, default=0.3, space='exit', optimize=True, load=True) ts5 = DecimalParameter(low=0.04, high=0.06, default=0.05, space='exit', optimize=True, load=True) tsl_target4 = DecimalParameter(low=0.18, high=0.3, default=0.2, space='exit', optimize=True, load=True) ts4 = DecimalParameter(low=0.03, high=0.05, default=0.045, space='exit', optimize=True, load=True) tsl_target3 = DecimalParameter(low=0.1, high=0.15, default=0.15, space='exit', optimize=True, load=True) ts3 = DecimalParameter(low=0.025, high=0.04, default=0.035, space='exit', optimize=True, load=True) tsl_target2 = DecimalParameter(low=0.07, high=0.12, default=0.1, space='exit', optimize=True, load=True) ts2 = DecimalParameter(low=0.015, high=0.03, default=0.02, space='exit', optimize=True, load=True) tsl_target1 = DecimalParameter(low=0.04, high=0.06, default=0.06, space='exit', optimize=True, load=True) ts1 = DecimalParameter(low=0.01, high=0.016, default=0.013, space='exit', optimize=True, load=True) tsl_target0 = DecimalParameter(low=0.02, high=0.05, default=0.03, space='exit', optimize=True, load=True) ts0 = DecimalParameter(low=0.008, high=0.015, default=0.013, space='exit', optimize=True, load=True) ### protections ### @property def protections(self): prot = [] prot.append({'method': 'CooldownPeriod', 'stop_duration_candles': self.cooldown_lookback.value}) if self.use_stop_protection.value: prot.append({'method': 'StoplossGuard', 'lookback_period_candles': 24 * 3, 'trade_limit': self.stop_protection_trade_limit.value, 'stop_duration_candles': self.stop_duration.value, 'only_per_pair': self.stop_protection_only_per_pair.value, 'required_profit': self.stop_protection_required_profit.value, 'only_per_side': self.stop_protection_only_per_side.value}) if self.use_lowprofit_protection.value: prot.append({'method': 'LowProfitPairs', 'lookback_period_candles': self.lowprofit_protection_lookback.value, 'trade_limit': self.lowprofit_trade_limit.value, 'stop_duration_candles': self.lowprofit_stop_duration.value, 'required_profit': self.lowprofit_required_profit.value, 'only_per_pair': self.lowprofit_only_per_pair.value}) if self.use_maxdrawdown_protection.value: prot.append({'method': 'MaxDrawdown', 'lookback_period_candles': self.maxdrawdown_protection_lookback.value, 'trade_limit': self.maxdrawdown_trade_limit.value, 'stop_duration_candles': self.maxdrawdown_stop_duration.value, 'max_allowed_drawdown': self.maxdrawdown_allowed_drawdown.value}) return prot ### Dollar Cost Averaging ### # This is called when placing the initial order (opening trade) def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: Optional[float], max_stake: float, leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float: # # if self.max_epa.value == 0: # # self.max_dca_multiplier = 1 # # elif self.max_epa.value == 1: # # self.max_dca_multiplier = 2 # # elif self.max_epa.value == 2: # # self.max_dca_multiplier = 3 # # else: # # self.max_dca_multiplier = 4 # We need to leave most of the funds for possible further DCA orders # This also applies to fixed stakes return proposed_stake / self.max_dca_multiplier def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: Optional[float], max_stake: float, current_entry_rate: float, current_exit_rate: float, current_entry_profit: float, current_exit_profit: float, **kwargs) -> Optional[float]: """ Custom trade adjustment logic, returning the stake amount that a trade should be increased or decreased. This means extra entry or exit orders with additional fees. Only called when `position_adjustment_enable` is set to True. For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ When not implemented by a strategy, returns None :param trade: trade object. :param current_time: datetime object, containing the current datetime :param current_rate: Current entry rate. :param current_profit: Current profit (as ratio), calculated based on current_rate. :param min_stake: Minimal stake size allowed by exchange (for both entries and exits) :param max_stake: Maximum stake allowed (either through balance, or by exchange limits). :param current_entry_rate: Current rate using entry pricing. :param current_exit_rate: Current rate using exit pricing. :param current_entry_profit: Current profit using entry pricing. :param current_exit_profit: Current profit using exit pricing. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return float: Stake amount to adjust your trade, Positive values to increase position, Negative values to decrease position. Return None for no action. """ if current_profit > 0.1 and trade.nr_of_successful_exits == 0: # Take half of the profit at +5% return -(trade.stake_amount / 2) if current_profit > -self.dca1.value and trade.nr_of_successful_entries == 1: return None if current_profit > -self.dca2.value and trade.nr_of_successful_entries == 2: return None if current_profit > -self.dca3.value and trade.nr_of_successful_entries == 3: return None # Obtain pair dataframe (just to show how to access it) dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) filled_entries = trade.select_filled_orders(trade.entry_side) count_of_entries = trade.nr_of_successful_entries # Allow up to 3 additional increasingly larger entrys (4 in total) # Initial entry is 1x # If that falls to -2.5% profit, we entry more, # If that falls down to -5% again, we entry 1.5x more # If that falls once again down to -5%, we entry more # Total stake for this trade would be 1 + 1.5 + 2 + 2.5 = 7x of the initial allowed stake. # That is why max_dca_multiplier is 7 # Hope you have a deep wallet! try: # This returns first order stake size stake_amount = filled_entries[0].cost # This then calculates current safety order size if count_of_entries == 1: stake_amount = stake_amount * 1 elif count_of_entries == 2: stake_amount = stake_amount * 1 elif count_of_entries == 3: stake_amount = stake_amount * 1 else: stake_amount = stake_amount return stake_amount except Exception as exception: return None return None ### Trailing Stop ### def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: for stop5 in self.tsl_target5.range: if current_profit > stop5: for stop5a in self.ts5.range: self.dp.send_msg(f'*** {pair} *** Profit: {current_profit} - lvl5 {stop5}/{stop5a} activated') return stop5a for stop4 in self.tsl_target4.range: if current_profit > stop4: for stop4a in self.ts4.range: self.dp.send_msg(f'*** {pair} *** Profit {current_profit} - lvl4 {stop4}/{stop4a} activated') return stop4a for stop3 in self.tsl_target3.range: if current_profit > stop3: for stop3a in self.ts3.range: self.dp.send_msg(f'*** {pair} *** Profit {current_profit} - lvl3 {stop3}/{stop3a} activated') return stop3a for stop2 in self.tsl_target2.range: if current_profit > stop2: for stop2a in self.ts2.range: self.dp.send_msg(f'*** {pair} *** Profit {current_profit} - lvl2 {stop2}/{stop2a} activated') return stop2a for stop1 in self.tsl_target1.range: if current_profit > stop1: for stop1a in self.ts1.range: self.dp.send_msg(f'*** {pair} *** Profit {current_profit} - lvl1 {stop1}/{stop1a} activated') return stop1a for stop0 in self.tsl_target0.range: if current_profit > stop0: for stop0a in self.ts0.range: self.dp.send_msg(f'*** {pair} *** Profit {current_profit} - lvl0 {stop0}/{stop0a} activated') return stop0a return self.stoploss ### NORMAL INDICATORS ### def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Pivot Points pivots = pivots_points.pivots_points(dataframe, timeperiod=32, levels=5) #50 dataframe['pivot'] = pivots['pivot'] dataframe['s5'] = pivots['s5'] dataframe['r5'] = pivots['r5'] dataframe['s3'] = pivots['s3'] dataframe['r3'] = pivots['r3'] dataframe['s2'] = pivots['s2'] dataframe['r2'] = pivots['r2'] dataframe['r3-dif'] = (dataframe['r3'] - dataframe['r2']) / 4 dataframe['r2.25'] = dataframe['r2'] + dataframe['r3-dif'] dataframe['r2.50'] = dataframe['r2'] + dataframe['r3-dif'] * 2 dataframe['r2.75'] = dataframe['r2'] + dataframe['r3-dif'] * 3 # Filter ZEMA for exiting for length in self.filterlength.range: dataframe[f'ema_1{length}'] = ta.EMA(dataframe['close'], timeperiod=length) dataframe[f'ema_2{length}'] = ta.EMA(dataframe[f'ema_1{length}'], timeperiod=length) dataframe[f'ema_dif{length}'] = dataframe[f'ema_1{length}'] - dataframe[f'ema_2{length}'] dataframe[f'zema_{length}'] = dataframe[f'ema_1{length}'] + dataframe[f'ema_dif{length}'] for x in self.ema_entry.range: dataframe[f'ema_entry{x}'] = ta.EMA(dataframe['close'], timeperiod=x) for y in self.ema_exit.range: dataframe[f'ema_exit{y}'] = ta.EMA(dataframe['close'], timeperiod=y) # RSI dataframe['rsi'] = ta.RSI(dataframe) dataframe['rsi_ma'] = ta.SMA(dataframe['rsi'], timeperiod=10) # WaveTrend using OHLC4 or HA close - 3/21 ap = 0.25 * (dataframe['high'] + dataframe['low'] + dataframe['close'] + dataframe['open']) dataframe['esa'] = ta.EMA(ap, timeperiod=3) dataframe['d'] = ta.EMA(abs(ap - dataframe['esa']), timeperiod=3) dataframe['wave_ci'] = (ap - dataframe['esa']) / (0.015 * dataframe['d']) dataframe['wave_t1'] = ta.EMA(dataframe['wave_ci'], timeperiod=21) dataframe['wave_t2'] = ta.SMA(dataframe['wave_t1'], timeperiod=3) # SMA dataframe['200_SMA'] = ta.SMA(dataframe['close'], timeperiod=200) dataframe['30_SMA'] = ta.SMA(dataframe['close'], timeperiod=30) # TTM Squeeze ttm_Squeeze = pta.squeeze(high=dataframe['high'], low=dataframe['low'], close=dataframe['close'], lazybear=True) dataframe['ttm_Squeeze'] = ttm_Squeeze['SQZ_20_2.0_20_1.5_LB'] dataframe['ttm_ema'] = ta.EMA(dataframe['ttm_Squeeze'], timeperiod=4) dataframe['squeeze_ON'] = ttm_Squeeze['SQZ_ON'] dataframe['squeeze_OFF'] = ttm_Squeeze['SQZ_OFF'] dataframe['NO_squeeze'] = ttm_Squeeze['SQZ_NO'] #ATR active MA Offset adjustments dataframe['atr_pcnt'] = qtpylib.atr(dataframe) / (dataframe['close'] + dataframe['open'] / 2) dataframe['atr_entry'] = dataframe['atr_pcnt'] / self.atr_entry.value dataframe['atr_exit'] = dataframe['atr_pcnt'] / self.atr_exit.value dataframe['entry_offset1'] = dataframe[f'ema_entry{self.ema_entry.value}'] * (self.entry_offset1.value - dataframe['atr_entry']) dataframe['entry_offset2'] = dataframe[f'ema_entry{self.ema_entry.value}'] * (self.entry_offset2.value - dataframe['atr_entry']) dataframe['exit_offset1'] = dataframe[f'ema_exit{self.ema_exit.value}'] * (self.exit_offset1.value + dataframe['atr_exit']) ### I.ntelligent B.uying S.ystem ### # distance from reference ma to current close dataframe['change'] = (dataframe['close'] - dataframe['200_SMA']) / dataframe['close'] * 100 # smoothing the change and offsets for valsma in self.smoothing_length.range: dataframe[f'smooth_change_{valsma}'] = ta.SMA(dataframe['change'], timeperiod=valsma) dataframe['smooth_ma_slope'] = pta.momentum.slope(dataframe[f'smooth_change_{valsma}']) dataframe['smooth_slope_sma'] = ta.SMA(dataframe['smooth_ma_slope'], timeperiod=5) # 300 Candle Rolling Min-Max for l in self.max_length.range: dataframe['min'] = dataframe['open'].rolling(l).min() dataframe['max'] = dataframe['close'].rolling(l).max() # distance from the rolling max in percent dataframe['from_max'] = (dataframe['close'] - dataframe['max']) / dataframe['close'] * 100 # distance from the rolling min in percent dataframe['from_min'] = (dataframe['open'] - dataframe['min']) / dataframe['open'] * 100 return dataframe ### ENTRY CONDITIONS ### def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: # Guard: Wave 1 is raising # Make sure Volume is not 0 df.loc[(df['squeeze_ON'] == 1) & (self.entry01.value == True) & (df['rsi_ma'] > df['rsi_ma'].shift(1)) & (df['200_SMA'] < df['200_SMA'].shift(1)) & (df['rsi'] < self.entry_rsi_bear.value) & (df['wave_t1'] < self.entry_wt_bear.value) & (df['wave_t1'] > df['wave_t1'].shift(1)) & qtpylib.crossed_above(df['wave_t1'], df['wave_t2']) & (df['volume'] > 0), ['enter_long', 'enter_tag']] = (1, 'ttm_Squeeze/WT - bear') # Guard: Wave 1 is raising # Make sure Volume is not 0 df.loc[(df['squeeze_ON'] == 1) & (self.entry02.value == True) & (df['rsi_ma'] > df['rsi_ma'].shift(1)) & (df['rsi'] < self.entry_rsi_bear.value) & (df['200_SMA'] < df['200_SMA'].shift(1)) & (df['wave_t1'] < self.entry_wt_bear.value) & (df['wave_t1'] > df['wave_t1'].shift(1)) & (df['wave_t1'].shift(2) > df['wave_t1'].shift(1)) & (df['volume'] > 0), ['enter_long', 'enter_tag']] = (1, 'ttm_Squeeze/WTT - bear') # Guard: Wave 1 is raising # Make sure Volume is not 0 df.loc[(df['rsi_ma'] > df['rsi_ma'].shift(1)) & (self.entry03.value == True) & (df['rsi'] < self.entry_rsi_bear.value) & (df['200_SMA'] < df['200_SMA'].shift(1)) & (df['wave_t1'] < self.entry_wt_bear.value) & (df['wave_t1'] > df['wave_t1'].shift(1)) & (df['wave_t1'].shift(2) > df['wave_t1'].shift(1)) & (df['volume'] > 0), ['enter_long', 'enter_tag']] = (1, 'WT transition - bear') # Guard: Wave 1 is raising # Make sure Volume is not 0 df.loc[(df['squeeze_ON'] == 1) & (self.entry04.value == True) & (df['rsi_ma'] > df['rsi_ma'].shift(1)) & (df['rsi'] < self.entry_rsi_bull.value) & (df['30_SMA'] > df['close']) & (df['200_SMA'] > df['200_SMA'].shift(1)) & (df['wave_t1'] > df['wave_t1'].shift(1)) & qtpylib.crossed_above(df['wave_t1'], df['wave_t2']) & (df['volume'] > 0), ['enter_long', 'enter_tag']] = (1, 'ttm_Squeeze/WT - bull') # Make sure Volume is not 0 df.loc[(df['squeeze_ON'] == 1) & (self.entry05.value == True) & (df['rsi_ma'] > df['rsi_ma'].shift(1)) & (df['rsi'] < self.entry_rsi_bull.value) & (df['30_SMA'] > df['close']) & (df['200_SMA'] > df['200_SMA'].shift(1)) & (df['wave_t1'] > df['wave_t1'].shift(1)) & (df['wave_t1'] < self.entry_wt_bull.value) & (df['wave_t1'].shift(2) > df['wave_t1'].shift(1)) & (df['volume'] > 0), ['enter_long', 'enter_tag']] = (1, 'ttm_Squeeze/WTT - bull') # Guard: Wave 1 is raising # Make sure Volume is not 0 df.loc[(df['rsi_ma'] > df['rsi_ma'].shift(1)) & (self.entry06.value == True) & (df['rsi'] < self.entry_rsi_bull.value) & (df['30_SMA'] > df['close']) & (df['200_SMA'] > df['200_SMA'].shift(1)) & (df['wave_t1'] > df['wave_t1'].shift(1)) & (df['wave_t1'] < self.entry_wt_bull.value) & (df['wave_t1'].shift(2) > df['wave_t1'].shift(1)) & (df['volume'] > 0), ['enter_long', 'enter_tag']] = (1, 'WT transition - bull') # (df['rsi'] > self.entry_rsi.value) & # (df['rsi_ma'] > df['rsi_ma'].shift(1)) & # (df['200_SMA'] < df['200_SMA'].shift(1)) & # Make sure Volume is not 0 df.loc[(self.entry07.value == True) & (df['close'] < df['entry_offset1']) & (df['rsi'] < self.entry_rsi_bear.value) & qtpylib.crossed_above(df['rsi'], df['rsi_ma']) & (df['volume'] > 0), ['enter_long', 'enter_tag']] = (1, 'RSI-XO < Buy Offset1') # Signal: RSI crosses above 30 # Make sure Volume is not 0 df.loc[(self.entry08.value == True) & (df['rsi'] > self.entry_rsi.value) & (df['rsi_ma'] > df['rsi_ma'].shift(1)) & (df['rsi'] < self.entry_rsi_bull.value) & (df['30_SMA'] > df['close']) & (df['30_SMA'] > df['200_SMA']) & (df['200_SMA'] > df['200_SMA'].shift(1)) & qtpylib.crossed_above(df['rsi'], df['rsi_ma']) & (df['volume'] > 0), ['enter_long', 'enter_tag']] = (1, 'RSI-XO bull') return df ### EXIT CONDITIONS ### def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: # Make sure Volume is not 0 df.loc[qtpylib.crossed_below(df[f'zema_{self.filterlength.value}'], df['r5']) & (df['close'] > df['200_SMA'] * self.exit_offset1.value) & (self.exit01.value == True) & (df['volume'] > 0), ['exit_long', 'exit_tag']] = (1, 'R5 - XO') # Make sure Volume is not 0 df.loc[qtpylib.crossed_below(df[f'zema_{self.filterlength.value}'], df['r3']) & (df['smooth_ma_slope'] < self.exit_slope.value) & (df['rsi'] > self.exit_rsi.value) & (self.exit02.value == True) & (df['volume'] > 0), ['exit_long', 'exit_tag']] = (1, 'R3 - XO') # Make sure Volume is not 0 df.loc[qtpylib.crossed_below(df[f'zema_{self.filterlength.value}'], df['r2.75']) & (df['smooth_ma_slope'] < self.exit_slope.value) & (df['rsi'] > self.exit_rsi.value) & (self.exit03.value == True) & (df['volume'] > 0), ['exit_long', 'exit_tag']] = (1, 'R2.75 - XO') # Make sure Volume is not 0 df.loc[qtpylib.crossed_below(df[f'zema_{self.filterlength.value}'], df['r2.50']) & (df['smooth_ma_slope'] < self.exit_slope.value) & (df['rsi'] > self.exit_rsi.value) & (self.exit04.value == True) & (df['volume'] > 0), ['exit_long', 'exit_tag']] = (1, 'R2.5 - XO') # Make sure Volume is not 0 df.loc[qtpylib.crossed_below(df[f'zema_{self.filterlength.value}'], df['r2.25']) & (df['smooth_ma_slope'] < self.exit_slope.value) & (df['rsi'] > self.exit_rsi.value) & (self.exit05.value == True) & (df['volume'] > 0), ['exit_long', 'exit_tag']] = (1, 'R2.25 - XO') # Make sure Volume is not 0 df.loc[qtpylib.crossed_below(df[f'zema_{self.filterlength.value}'], df['r2']) & (df['smooth_ma_slope'] < self.exit_slope.value) & (df['rsi'] > self.exit_rsi.value) & (self.exit06.value == True) & (df['volume'] > 0), ['exit_long', 'exit_tag']] = (1, 'R2 - XO') # Make sure Volume is not 0 df.loc[qtpylib.crossed_below(df[f'zema_{self.filterlength.value}'], df['pivot']) & (df['smooth_ma_slope'] < self.exit_slope.value) & (df['rsi'] > self.exit_rsi.value) & (self.exit07.value == True) & (df['volume'] > 0), ['exit_long', 'exit_tag']] = (1, 'Pivot - XO') return df # Best result: # 40/250: 1027 trades. 699/290/38 Wins/Draws/Losses. Avg profit 0.91%. Median profit 0.25%. Total profit 6051.24032389 USDT ( 60.51%). Avg duration 11:33:00 min. Objective: -6051.24032 # # Buy hyperspace params: # entry_params = { # "entry_rsi": 21, # "entry_rsi_bear": 48, # "entry_rsi_bull": 75, # "entry_wt_bear": 46, # "entry_wt_bull": 70, # "dca1": 0.01, # "dca2": 0.11, # "dca3": 0.23, # "max_epa": 2, # "wavelength": 10, # } # # Sell hyperspace params: # exit_params = { # "exit_rsi": 71, # "ts0": 0.012, # "ts1": 0.011, # "ts2": 0.022, # "ts3": 0.03, # "ts4": 0.038, # "ts5": 0.045, # "tsl_target0": 0.029, # "tsl_target1": 0.058, # "tsl_target2": 0.091, # "tsl_target3": 0.143, # "tsl_target4": 0.183, # "tsl_target5": 0.2, # } # # Protection hyperspace params: # protection_params = { # "cooldown_lookback": 38, # value loaded from strategy # "stop_duration": 47, # value loaded from strategy # "use_stop_protection": False, # value loaded from strategy # } # # ROI table: # minimal_roi = { # "0": 0.364, # "45": 0.145, # "160": 0.048, # "520": 0 # } # # Stoploss: # stoploss = -0.286 # # Trailing stop: # trailing_stop = True # value loaded from strategy # trailing_stop_positive = 0.269 # value loaded from strategy # trailing_stop_positive_offset = 0.317 # value loaded from strategy # trailing_only_offset_is_reached = True # value loaded from strategy # # Max Open Trades: # max_open_trades = 12 # value loaded from strategy # Best result: # 53/250: 886 trades. 608/262/16 Wins/Draws/Losses. Avg profit 0.90%. Median profit 0.23%. Total profit 5306.73713390 USDT ( 53.07%). Avg duration 12:10:00 min. Objective: -5306.73713 # # Buy hyperspace params: # entry_params = { # "entry_rsi": 22, # "entry_rsi_bear": 40, # "entry_rsi_bull": 59, # "entry_wt_bear": 48, # "entry_wt_bull": 56, # "dca1": 0.01, # "dca2": 0.14, # "dca3": 0.21, # "max_epa": 1, # "wavelength": 9, # } # # Sell hyperspace params: # exit_params = { # "exit_rsi": 68, # "ts0": 0.009, # "ts1": 0.013, # "ts2": 0.025, # "ts3": 0.028, # "ts4": 0.04, # "ts5": 0.047, # "tsl_target0": 0.047, # "tsl_target1": 0.053, # "tsl_target2": 0.099, # "tsl_target3": 0.135, # "tsl_target4": 0.25, # "tsl_target5": 0.4, # } # # Protection hyperspace params: # protection_params = { # "cooldown_lookback": 26, # "stop_duration": 83, # "use_stop_protection": False, # } # # ROI table: # minimal_roi = { # "0": 0.154, # "120": 0.09, # "300": 0.044, # "554": 0 # } # # Stoploss: # stoploss = -0.205 # # Trailing stop: # trailing_stop = True # trailing_stop_positive = 0.342 # trailing_stop_positive_offset = 0.437 # trailing_only_offset_is_reached = True # # Max Open Trades: # max_open_trades = 12 # value loaded from strategy # (.env) core@core:~/freqtrade$ freqtrade hyperopt --hyperopt-loss OnlyProfitHyperOptLoss --strategy thetank2 --config ho.json -e 250 --timerange 20230101-20230215 --spaces entry exit roi stoploss trailing protection