import datetime import json import logging import math import os from datetime import datetime from datetime import timedelta, timezone from functools import reduce from typing import Optional import numpy as np import pandas as pd import talib.abstract as ta from pandas import DataFrame import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.constants import Config from freqtrade.persistence import Trade from freqtrade.strategy import merge_informative_pair, DecimalParameter, IntParameter from freqtrade.strategy.interface import IStrategy def pct_change(a, b): return (b - a) / a def load_sell_value_info(sell_value_info_file): logging.info("Loading sell value info") try: user_data_directory = os.path.join('user_data') with open(os.path.join(user_data_directory, sell_value_info_file), 'r') as file: return json.load(file) except FileNotFoundError: return {} def save_sell_value_info(sell_value_info_file, sell_value_info): logging.info("Saving sell value info") user_data_directory = os.path.join('user_data') with open(os.path.join(user_data_directory, sell_value_info_file), 'w') as file: json.dump(sell_value_info, file) def EWO(dataframe, ema_length=5, ema2_length=3): df = dataframe.copy() ema1 = ta.EMA(df, timeperiod=ema_length) ema2 = ta.EMA(df, timeperiod=ema2_length) return (ema1 - ema2) / df['close'] * 100 class HPStrategy(IStrategy): INTERFACE_VERSION = 2 max_safety_orders = 3 lowest_prices = {} highest_prices = {} price_drop_percentage = {} pairs_close_to_high = [] locked = [] stoploss = -0.99 is_optimize_cofi = False use_sell_signal = True sell_profit_only = True sell_profit_offset = 0.005 ignore_roi_if_buy_signal = False position_adjustment_enable = True order_time_in_force = { 'buy': 'gtc', 'sell': 'gtc' } timeframe = '1m' inf_1h = '1h' process_only_new_candles = True startup_candle_count = 400 plot_config = { 'main_plot': { 'ma_buy': {'color': 'orange'}, 'ma_sell': {'color': 'orange'}, }, } buy_params = { "base_nb_candles_buy": 12, "rsi_buy": 58, "ewo_high": 3.001, "ewo_low": -10.289, "low_offset": 0.987, "lambo2_ema_14_factor": 0.981, "lambo2_enabled": True, "lambo2_rsi_14_limit": 39, "lambo2_rsi_4_limit": 44, "buy_adx": 20, "buy_fastd": 20, "buy_fastk": 22, "buy_ema_cofi": 0.98, "buy_ewo_high": 4.179 } buy_ema_cofi = DecimalParameter(0.96, 0.98, default=0.97, optimize=is_optimize_cofi) buy_fastk = IntParameter(20, 30, default=20, optimize=is_optimize_cofi) buy_fastd = IntParameter(20, 30, default=20, optimize=is_optimize_cofi) buy_adx = IntParameter(20, 30, default=30, optimize=is_optimize_cofi) buy_ewo_high = DecimalParameter(2, 12, default=3.553, optimize=is_optimize_cofi) base_nb_candles_buy = IntParameter(8, 20, default=buy_params['base_nb_candles_buy'], space='buy', optimize=False) low_offset = DecimalParameter(0.975, 0.995, default=buy_params['low_offset'], space='buy', optimize=True) rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=False) lambo2_ema_14_factor = DecimalParameter(0.8, 1.2, decimals=3, default=buy_params['lambo2_ema_14_factor'], space='buy', optimize=True) lambo2_rsi_4_limit = IntParameter(10, 60, default=buy_params['lambo2_rsi_4_limit'], space='buy', optimize=True) lambo2_rsi_14_limit = IntParameter(10, 60, default=buy_params['lambo2_rsi_14_limit'], space='buy', optimize=True) fast_ewo = 50 slow_ewo = 200 ewo_low = DecimalParameter(-20.0, -7.0, default=buy_params['ewo_low'], space='buy', optimize=True) ewo_high = DecimalParameter(3.0, 5, default=buy_params['ewo_high'], space='buy', optimize=True) trailing_stop = True trailing_stop_positive = 0.001 trailing_stop_positive_offset = 0.03 trailing_only_offset_is_reached = True minimal_roi = { "0": 0.15, "30": 0.10, "60": 0.05, "90": 0.03, "120": 0.01, "240": 0 } order_types = { 'entry': 'market', 'exit': 'market', 'stoploss': 'market', 'stoploss_on_exchange': False } sell_params = { "base_nb_candles_sell": 22, "high_offset": 1.014, "high_offset_2": 1.01 } base_nb_candles_sell = IntParameter(8, 20, default=sell_params['base_nb_candles_sell'], space='sell', optimize=False) high_offset = DecimalParameter(1.000, 1.010, default=sell_params['high_offset'], space='sell', optimize=True) high_offset_2 = DecimalParameter(1.000, 1.010, default=sell_params['high_offset_2'], space='sell', optimize=True) @property def protections(self): return [ { "method": "CooldownPeriod", "stop_duration_candles": 3 }, { "method": "MaxDrawdown", "lookback_period_candles": 48, "trade_limit": 20, "stop_duration_candles": 4, "max_allowed_drawdown": 0.2 }, { "method": "StoplossGuard", "lookback_period_candles": 24, "trade_limit": 4, "stop_duration_candles": 2, "only_per_pair": False }, { "method": "LowProfitPairs", "lookback_period_candles": 6, "trade_limit": 2, "stop_duration_candles": 60, "required_profit": 0.02 }, { "method": "LowProfitPairs", "lookback_period_candles": 24, "trade_limit": 4, "stop_duration_candles": 2, "required_profit": 0.01 } ] def version(self) -> str: return "HPStrategy v1.1.0 " def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs): if current_profit < -0.05 and (current_time - trade.open_date_utc).days >= 7: return 'unclog' 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: min_trade_size = 3 if proposed_stake < min_trade_size: return 0 max_stake_for_safety_orders = max_stake / self.max_safety_orders if max_stake_for_safety_orders < min_trade_size: return 0 return min(proposed_stake, max_stake_for_safety_orders) def informative_pairs(self): pairs = self.dp.current_whitelist() informative_pairs = [(pair, '1h') for pair in pairs] if self.config['stake_currency'] in ['USDT', 'BUSD', 'USDC', 'DAI', 'TUSD', 'PAX', 'USD', 'EUR', 'GBP']: btc_info_pair = f"BTC/{self.config['stake_currency']}" else: btc_info_pair = "BTC/USDT" informative_pairs.extend( ((btc_info_pair, self.timeframe), (btc_info_pair, self.inf_1h)) ) return informative_pairs def analyze_price_movements(self, dataframe, metadata, window=50): pair = metadata['pair'] low = dataframe['low'].rolling(window=window).min() high = dataframe['high'].rolling(window=window).max() current_price = dataframe['close'].iloc[-1] mid_price = (low + high) / 2 price_to_mid_ratio = ((current_price - mid_price) / (high - mid_price)).iloc[-1] self.pairs_close_to_high = list(set(self.pairs_close_to_high)) if price_to_mid_ratio > 0.5: if pair not in self.pairs_close_to_high: self.pairs_close_to_high.append(pair) if pair in self.locked: self.locked.remove(pair) elif pair in self.pairs_close_to_high: self.pairs_close_to_high.remove(pair) if pair not in self.locked: logging.info(f"Locking {pair}") self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(minutes=5)) self.locked.append(pair) user_data_directory = os.path.join('user_data') if not os.path.exists(user_data_directory): os.makedirs(user_data_directory) with open(os.path.join(user_data_directory, 'high_moving_pairs.json'), 'w') as f: json.dump(self.pairs_close_to_high, f, indent=4) def pump_dump_protection(self, dataframe: DataFrame, metadata: dict) -> DataFrame: df36h = dataframe.copy().shift(432) df24h = dataframe.copy().shift(288) dataframe['volume_mean_short'] = dataframe['volume'].rolling(4).mean() dataframe['volume_mean_long'] = df24h['volume'].rolling(48).mean() dataframe['volume_mean_base'] = df36h['volume'].rolling(288).mean() dataframe['volume_change_percentage'] = (dataframe['volume_mean_long'] / dataframe['volume_mean_base']) dataframe['rsi_mean'] = dataframe['rsi'].rolling(48).mean() dataframe['pnd_volume_warn'] = np.where((dataframe['volume_mean_short'] / dataframe['volume_mean_long'] > 5.0), -1, 0) return dataframe def base_tf_btc_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['price_trend_long'] = ( dataframe['close'].rolling(8).mean() / dataframe['close'].shift(8).rolling(144).mean()) ignore_columns = ['date', 'open', 'high', 'low', 'close', 'volume'] dataframe.rename(columns=lambda s: f"btc_{s}" if s not in ignore_columns else s, inplace=True) return dataframe def info_tf_btc_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['rsi_8'] = ta.RSI(dataframe, timeperiod=8) ignore_columns = ['date', 'open', 'high', 'low', 'close', 'volume'] dataframe.rename(columns=lambda s: f"btc_{s}" if s not in ignore_columns else s, inplace=True) return dataframe def save_dictionaries_to_disk(self): try: user_data_directory = os.path.join('user_data') if not os.path.exists(user_data_directory): os.makedirs(user_data_directory) with open(os.path.join(user_data_directory, 'lowest_prices.json'), 'w') as file: json.dump(self.lowest_prices, file, indent=4) with open(os.path.join(user_data_directory, 'highest_prices.json'), 'w') as file: json.dump(self.highest_prices, file, indent=4) with open(os.path.join(user_data_directory, 'price_drop_percentage.json'), 'w') as file: json.dump(self.price_drop_percentage, file, indent=4) except Exception as ex: logging.error(str(ex)) def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: logging.info("Populating indicators") dataframe['price_history'] = dataframe['close'].shift(1) data_last_bbars = dataframe[-30:].copy() low_min = dataframe['low'].rolling(window=14).min() high_max = dataframe['high'].rolling(window=14).max() dataframe['stoch_k'] = 100 * (dataframe['close'] - low_min) / (high_max - low_min) dataframe['stoch_d'] = dataframe['stoch_k'].rolling(window=3).mean() cnum = 64 price_range = np.linspace(data_last_bbars['low'].min(), data_last_bbars['high'].max(), num=cnum) vol_profile = pd.cut(data_last_bbars['close'], bins=price_range, include_lowest=True, labels=range(cnum - 1)) vol_by_price = data_last_bbars.groupby(vol_profile)['volume'].sum() poc_index = vol_by_price.idxmax() dataframe['poc'] = price_range[poc_index] if poc_index >= 0 else np.nan percent = 70 va_threshold = vol_by_price.sum() * (percent / 100) cum_vol = vol_by_price.sort_values(ascending=False).cumsum() value_area = cum_vol[cum_vol <= va_threshold].index dataframe['va_high'] = np.nan if value_area.empty else price_range[value_area.max()] dataframe['va_low'] = np.nan if value_area.empty else price_range[value_area.min()] pair = metadata['pair'] if self.config['stake_currency'] in ['USDT', 'BUSD']: btc_info_pair = f"BTC/{self.config['stake_currency']}" else: btc_info_pair = "BTC/USDT" btc_info_tf = self.dp.get_pair_dataframe(btc_info_pair, self.inf_1h) btc_info_tf = self.info_tf_btc_indicators(btc_info_tf, metadata) dataframe = merge_informative_pair(dataframe, btc_info_tf, self.timeframe, self.inf_1h, ffill=True) drop_columns = [f"{s}_{self.inf_1h}" for s in ['date', 'open', 'high', 'low', 'close', 'volume']] dataframe.drop(columns=dataframe.columns.intersection(drop_columns), inplace=True) btc_base_tf = self.dp.get_pair_dataframe(btc_info_pair, self.timeframe) btc_base_tf = self.base_tf_btc_indicators(btc_base_tf, metadata) dataframe = merge_informative_pair(dataframe, btc_base_tf, self.timeframe, self.timeframe, ffill=True) drop_columns = [f"{s}_{self.timeframe}" for s in ['date', 'open', 'high', 'low', 'close', 'volume']] dataframe.drop(columns=dataframe.columns.intersection(drop_columns), inplace=True) for val in self.base_nb_candles_buy.range: dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) for val in self.base_nb_candles_sell.range: dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) # Elliot dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) # RSI dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) # lambo2 dataframe['ema_14'] = ta.EMA(dataframe, timeperiod=14) dataframe['rsi_4'] = ta.RSI(dataframe, timeperiod=4) dataframe['rsi_14'] = ta.RSI(dataframe, timeperiod=14) # # Pump strength # dataframe['zema_30'] = ftt.zema(dataframe, period=30) # dataframe['zema_200'] = ftt.zema(dataframe, period=200) # dataframe['pump_strength'] = (dataframe['zema_30'] - dataframe['zema_200']) / dataframe['zema_30'] # Cofi stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0) dataframe['fastd'] = stoch_fast['fastd'] dataframe['fastk'] = stoch_fast['fastk'] dataframe['adx'] = ta.ADX(dataframe) dataframe['ema_8'] = ta.EMA(dataframe, timeperiod=8) dataframe = self.pump_dump_protection(dataframe, metadata) low_min = dataframe['low'].rolling(window=14, center=True).apply(lambda x: np.argmin(x) == 7, raw=True) rsi_min = dataframe['rsi'].rolling(window=14, center=True).apply(lambda x: np.argmin(x) == 7, raw=True) bullish_div = (low_min.notna()) & (rsi_min.shift() > rsi_min) dataframe['bullish_divergence'] = bullish_div.astype(int) # Fractals dataframe['fractal_top'] = (dataframe['high'] > dataframe['high'].shift(2)) & \ (dataframe['high'] > dataframe['high'].shift(1)) & \ (dataframe['high'] > dataframe['high'].shift(-1)) & \ (dataframe['high'] > dataframe['high'].shift(-2)) dataframe['fractal_bottom'] = (dataframe['low'] < dataframe['low'].shift(2)) & \ (dataframe['low'] < dataframe['low'].shift(1)) & \ (dataframe['low'] < dataframe['low'].shift(-1)) & \ (dataframe['low'] < dataframe['low'].shift(-2)) dataframe['turnaround_signal'] = (bullish_div) & (dataframe['fractal_bottom']) dataframe['rolling_max'] = dataframe['high'].cummax() dataframe['drawdown'] = (dataframe['rolling_max'] - dataframe['low']) / dataframe['rolling_max'] dataframe['below_90_percent_drawdown'] = dataframe['drawdown'] >= 0.90 # MACD výpočet macd = ta.MACD(dataframe) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] # Výpočet volatility pomocí ATR nebo standardní odchylky dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) dataframe['volatility'] = dataframe['close'].rolling(window=14).std() # Normalizace volatility do rozsahu, který bude použit pro úpravu citlivosti MACD # Můžete například použít z-score nebo jinou metodu pro normalizaci dataframe['volatility_factor'] = (dataframe['volatility'] - dataframe['volatility'].min()) / \ (dataframe['volatility'].max() - dataframe['volatility'].min()) # Zvolte koeficienty pro citlivost na základě volatility dataframe['macd_adjusted'] = dataframe['macd'] * (1 - dataframe['volatility_factor']) dataframe['macdsignal_adjusted'] = dataframe['macdsignal'] * (1 + dataframe['volatility_factor']) # dataframe.drop_duplicates() return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: self.analyze_price_movements(dataframe=dataframe, metadata=metadata, window=200) better_pair = metadata['pair'] not in self.pairs_close_to_high conditions = [] dataframe.loc[:, 'buy_tag'] = '' lambo2 = ( # bool(self.lambo2_enabled.value) & # (dataframe['pump_warning'] == 0) & (dataframe['close'] < (dataframe['ema_14'] * self.lambo2_ema_14_factor.value)) & (dataframe['rsi_4'] < int(self.lambo2_rsi_4_limit.value)) & (dataframe['rsi_14'] < int(self.lambo2_rsi_14_limit.value)) ) dataframe.loc[lambo2, 'buy_tag'] += 'lambo2_' conditions.append(lambo2) buy1ewo = ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['EWO'] > self.ewo_high.value) & (dataframe['rsi'] < self.rsi_buy.value) & (dataframe['volume'] > 0) & (dataframe['close'] < ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) ) dataframe.loc[buy1ewo, 'buy_tag'] += 'buy1eworsi_' conditions.append(buy1ewo) buy2ewo = ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['EWO'] < self.ewo_low.value) & (dataframe['volume'] > 0) & (dataframe['close'] < ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) ) dataframe.loc[buy2ewo, 'buy_tag'] += 'buy2ewo_' conditions.append(buy2ewo) is_cofi = ( (dataframe['open'] < dataframe['ema_8'] * self.buy_ema_cofi.value) & (qtpylib.crossed_above(dataframe['fastk'], dataframe['fastd'])) & (dataframe['fastk'] < self.buy_fastk.value) & (dataframe['fastd'] < self.buy_fastd.value) & (dataframe['adx'] > self.buy_adx.value) & (dataframe['EWO'] > self.buy_ewo_high.value) ) dataframe.loc[is_cofi, 'buy_tag'] += 'cofi_' conditions.append(is_cofi) if conditions: dataframe.loc[ reduce(lambda x, y: x | y, conditions) & better_pair, 'buy' ] = 1 dont_buy_conditions = [ dataframe['pnd_volume_warn'] < 0.0, dataframe['btc_rsi_8_1h'] < 35.0, ] poc_condition = ( (dataframe['close'] < dataframe['poc']) & (dataframe['close'] < dataframe['va_low']) ) if conditions: combined_conditions = [poc_condition & condition for condition in conditions] # combined_conditions = [condition for condition in conditions] final_condition = reduce(lambda x, y: x | y, combined_conditions) dataframe.loc[final_condition, 'buy'] = 1 if dont_buy_conditions: for condition in dont_buy_conditions: dataframe.loc[condition, 'buy'] = 0 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if conditions := [ (dataframe['close'] > dataframe['hma_50']) & ( dataframe['close'] > ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value ) ) & (dataframe['rsi'] > 50) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow']) | (dataframe['close'] < dataframe['hma_50']) & ( dataframe['close'] > ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value ) ) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow']) ]: dataframe.loc[ reduce(lambda x, y: x | y, conditions), 'sell' ] = 1 return dataframe def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, current_time: datetime, **kwargs) -> bool: try: sell_reason = f"{sell_reason}_" + trade.buy_tag except: pass current_profit = trade.calc_profit_ratio(rate) return ( current_profit >= self.sell_profit_offset or 'unclog' in sell_reason or 'force' in sell_reason ) class HPStrategyDCA(HPStrategy): initial_safety_order_trigger = -0.018 safety_order_step_scale = 1.2 safety_order_volume_scale = 1.4 drawdown_limit = -2 buy_params = { "dca_min_rsi": 35, } buy_params.update(HPStrategy.buy_params) dca_min_rsi = IntParameter(35, 75, default=buy_params['dca_min_rsi'], space='buy', optimize=True) def version(self) -> str: return f"{super().version()} DCA " def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = super().populate_indicators(dataframe, metadata) dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) return dataframe def calculate_volatility(self, dataframe: DataFrame, pair: str, timeframe: str) -> float: logging.info("Calculating volatility") timeframes_in_minutes = { '1m': 1, '5m': 5, '15m': 15, '30m': 30, '1h': 60, '4h': 240, '1d': 1440 } interval_in_minutes = timeframes_in_minutes.get(timeframe) if interval_in_minutes is None: raise ValueError("Neplatný timeframe. Prosím, zadejte jeden z podporovaných timeframe.") periods = int(24 * 60 / interval_in_minutes) dataframe['pct_change'] = dataframe['close'].pct_change() return dataframe['pct_change'].tail(periods).abs().mean() * 100 def dynamic_stake_adjustment(self, stake, volatility): logging.info("Adjusting stake dynamically") return stake * 0.8 if volatility > 0.05 else stake def calculate_drawdown(self, current_price, last_order_price): logging.info("Calculating drawdown") return (current_price - last_order_price) / last_order_price * 100 def calculate_dca_amount(self, current_price, target_profit, average_buy_price, total_investment): logging.info("Calculating DCA amount") target_sell_price = average_buy_price * (1 + target_profit) required_price_rise = target_sell_price / current_price return total_investment * (required_price_rise - 1) def check_buy_conditions(self, lambo2_ema_14_factor, lambo2_rsi_4_limit, lambo2_rsi_14_limit, base_nb_candles_buy, low_offset, ewo_high, rsi_buy, base_nb_candles_sell, high_offset, ewo_low, buy_ema_cofi, buy_fastk, buy_fastd, buy_adx, buy_ewo_high, last_candle, previous_candle): logging.info("Checking buy conditions") lambo2 = ( (last_candle['close'] < (last_candle['ema_14'] * lambo2_ema_14_factor.value)) & (last_candle['rsi_4'] < int(lambo2_rsi_4_limit.value)) & (last_candle['rsi_14'] < int(lambo2_rsi_14_limit.value)) ) conditions = [lambo2] buy1ewo = ( (last_candle['rsi_fast'] < 35) & (last_candle['close'] < ( last_candle[f'ma_buy_{base_nb_candles_buy.value}'] * low_offset.value)) & (last_candle['EWO'] > ewo_high.value) & (last_candle['rsi'] < rsi_buy.value) & (last_candle['volume'] > 0) & (last_candle['close'] < ( last_candle[f'ma_sell_{base_nb_candles_sell.value}'] * high_offset.value)) ) conditions.append(buy1ewo) buy2ewo = ( (last_candle['rsi_fast'] < 35) & (last_candle['close'] < ( last_candle[f'ma_buy_{base_nb_candles_buy.value}'] * low_offset.value)) & (last_candle['EWO'] < ewo_low.value) & (last_candle['volume'] > 0) & (last_candle['close'] < ( last_candle[f'ma_sell_{base_nb_candles_sell.value}'] * high_offset.value)) ) conditions.append(buy2ewo) crossed_above_fastk_fastd = (previous_candle['fastk'] < previous_candle['fastd']) and ( last_candle['fastk'] > last_candle['fastd']) is_cofi = ( (last_candle['open'] < last_candle['ema_8'] * buy_ema_cofi.value) & crossed_above_fastk_fastd & (last_candle['fastk'] < buy_fastk.value) & (last_candle['fastd'] < buy_fastd.value) & (last_candle['adx'] > buy_adx.value) & (last_candle['EWO'] > buy_ewo_high.value) ) conditions.append(is_cofi) return any(conditions) def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: float, max_stake: float, **kwargs): logging.info("Adjusting trade position") try: dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) df = dataframe.copy() except Exception as e: logging.error(f"Error getting analyzed dataframe: {e}") return None volatility = self.calculate_volatility(df, trade.pair, self.timeframe) adjusted_min_stake = self.dynamic_stake_adjustment(min_stake, volatility) adjusted_max_stake = self.dynamic_stake_adjustment(max_stake, volatility) last_candle = df.iloc[-1].squeeze() previous_candle = df.iloc[-2].squeeze() if last_candle['close'] < previous_candle['close']: return None current_candle_index = df.index[-1] if last_buy_order := next( ( order for order in sorted( trade.orders, key=lambda x: x.order_date, reverse=True ) if order.ft_order_side == 'buy' and order.status == 'closed' ), None, ): last_buy_candle = dataframe.loc[dataframe['date'] == last_buy_order.order_date] if not last_buy_candle.empty: last_buy_candle_index = last_buy_candle.index[0] if current_candle_index == last_buy_candle_index: return None if not self.check_buy_conditions(self.lambo2_ema_14_factor, self.lambo2_rsi_4_limit, self.lambo2_rsi_14_limit, self.base_nb_candles_buy, self.low_offset, self.ewo_high, self.rsi_buy, self.base_nb_candles_sell, self.high_offset, self.ewo_low, self.buy_ema_cofi, self.buy_fastk, self.buy_fastd, self.buy_adx, self.buy_ewo_high, last_candle, previous_candle): return None count_of_buys = sum(order.ft_order_side == 'buy' and order.status == 'closed' for order in trade.orders) if self.max_safety_orders >= count_of_buys >= 1: last_order_price = trade.open_rate if last_buy_order := next( ( order for order in sorted( trade.orders, key=lambda x: x.order_date, reverse=True ) if order.ft_order_side == 'buy' ), None, ): last_order_price = last_buy_order.price or last_buy_order.average drawdown = self.calculate_drawdown(current_rate, last_order_price) if last_order_price else 0 if drawdown <= self.drawdown_limit: try: stake_amount = self.wallets.get_trade_stake_amount(trade.pair, None) stake_amount = min(stake_amount * math.pow(self.safety_order_volume_scale, (count_of_buys - 1)), adjusted_max_stake) if stake_amount < adjusted_min_stake: return None try: price_change_rate = (last_candle['close'] - previous_candle['close']) / previous_candle['close'] if price_change_rate < -0.02: adjusted_stake = stake_amount * 1.5 elif price_change_rate > 0.02: adjusted_stake = stake_amount * 0.75 else: adjusted_stake = stake_amount except: adjusted_stake = stake_amount return adjusted_stake except Exception as exception: logging.error(f"Error adjusting trade position: {exception}") return None return None class HPStrategyTF(HPStrategyDCA): def version(self) -> str: return f"{super().version()} TF " def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = super().populate_indicators(dataframe, metadata) resampled_frame = dataframe.resample('5T', on='date').agg({ 'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum' }) resampled_frame['higher_tf_trend'] = (resampled_frame['close'] > resampled_frame['open']).astype(int) resampled_frame['higher_tf_trend'] = resampled_frame['higher_tf_trend'].replace({1: 1, 0: -1}) dataframe['higher_tf_trend'] = dataframe['date'].map(resampled_frame['higher_tf_trend']) return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = super().populate_buy_trend(dataframe, metadata) down_trend = ( (dataframe['higher_tf_trend'] > 1) ) dataframe.loc[down_trend, 'buy'] = 1 return dataframe class HPStrategyFLRSI(HPStrategyTF): def version(self) -> str: return f"{super().version()} FLRSI " def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs) -> float: return -0.025 def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = super().populate_buy_trend(dataframe, metadata) adjusted_rsi_slow = dataframe['rsi_slow'] * 0.9 adjusted_rsi = dataframe['rsi'] * 1.15 rsi_crossover = ( (qtpylib.crossed_above(adjusted_rsi, adjusted_rsi_slow)) ) dataframe.loc[rsi_crossover, 'buy_tag'] += 'rsi_crossover_' dataframe.loc[rsi_crossover, 'buy'] = 1 dataframe.loc[ ( (dataframe['macd_adjusted'] <= dataframe[ 'macdsignal_adjusted']) | # Upřednostnění křížení směrem dolů s větší citlivostí po pádu (dataframe['macd'] <= 0) # MACD je pod 0 ), 'buy'] = 0 # Zrušení nákupního signálu dataframe.loc[dataframe['atr'] > 0.002727272727272727, 'buy'] = 0 return dataframe class HPStrategyRsiVolAtr(HPStrategyDCA): force_buy_list = [] def version(self) -> str: return f"{super().version()} VOLATR " @property def protections(self): return [] def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = super().populate_buy_trend(dataframe, metadata) rsi_signal = ( (dataframe['rsi'] >= 15) & (dataframe['rsi'] <= 50) & (dataframe['volume'] > 0) ) dataframe.loc[rsi_signal, 'buy_tag'] += 'rsi_volume_' dataframe.loc[rsi_signal, 'buy'] = 1 return dataframe def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs) -> str: dataframe = self.dp.get_analyzed_dataframe(pair, self.timeframe) if dataframe.iloc[-1]['date'].to_pydatetime() < current_time: return None rsi_signal = ( (dataframe.iloc[-1]['rsi'] >= 15) & (dataframe.iloc[-1]['rsi'] <= 50) ) if current_profit > 0.005 and rsi_signal: self.force_buy_list.append(pair) return 'sell_signal_based_on_rsi_volume' return None class HPStrategyBlockDowntrend(HPStrategyDCA): # Jméno strategie INTERFACE_VERSION = 2 minimal_roi = { "0": 0.10, "15": 0.05, "40": 0.03, "60": 0.02, "90": 0.01, "120": 0.00 } trailing_stop = True trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.03 trailing_only_offset_is_reached = True def version(self) -> str: return f"{super().version()} BlockDowntrend " def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = super().populate_indicators(dataframe, metadata) # Detekce sestupného trendu dataframe['is_downtrend'] = (dataframe.shift(-12)['close'] < dataframe['open']) # dataframe['is_downtrend'] = (dataframe.shift(-10)['close'] < dataframe['open']) # dataframe['is_downtrend'] = (dataframe.shift(-8)['close'] < dataframe['open']) # dataframe['is_downtrend'] = (dataframe.shift(-6)['close'] < dataframe['open']) # dataframe['is_downtrend'] = (dataframe.shift(-4)['close'] < dataframe['open']) # dataframe['is_downtrend'] = (dataframe.shift(-2)['close'] < dataframe['open']) # dataframe['is_downtrend'] = (dataframe.shift(-1)['close'] < dataframe['open']) dataframe.loc[(dataframe['is_downtrend'] == False) & (dataframe['bullish_divergence'] > 0), 'our'] = 1 dataframe['buy'] = 1 dataframe['buy_tag'] = '' return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: current_pair = metadata['pair'] open_trades = Trade.get_open_trades() if any(trade.pair == current_pair for trade in open_trades): return dataframe dataframe.loc[(dataframe['rsi'] <= 40), 'buy'] = 1 dataframe.loc[(dataframe['rsi'] > 65), 'buy'] = 0 dataframe.loc[(dataframe['is_downtrend'] == True), 'buy'] = 0 dataframe.loc[(dataframe['our'] > 0), 'buy'] = 1 dataframe.loc[(dataframe['our'] > 0), 'buy_tag'] += 'our_signal_' return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = super().populate_sell_trend(dataframe, metadata) return dataframe def custom_stop_loss(self, pair, bought_price, current_price, current_time): roi = (current_price / bought_price) - 1 return current_price if roi <= -0.035 else bought_price def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: float, max_stake: float, **kwargs): dataframe_tuple = self.dp.get_analyzed_dataframe(pair=trade.pair, timeframe=self.timeframe) dataframe = dataframe_tuple[0] last_candle = dataframe.iloc[-1] if last_candle['is_downtrend']: return None return super().adjust_trade_position(trade, current_time, current_rate, current_profit, min_stake, max_stake)