From 8d3f2b0bff554dd17e0e1619054b647684ade0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Delacotte?= Date: Mon, 5 May 2025 16:07:54 +0200 Subject: [PATCH] Zeus_8_3_2_B_4_2 optimisations --- Zeus_8_3_2_B_4_2.py | 212 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 44 deletions(-) diff --git a/Zeus_8_3_2_B_4_2.py b/Zeus_8_3_2_B_4_2.py index 6aeca3b..0a0c8bd 100644 --- a/Zeus_8_3_2_B_4_2.py +++ b/Zeus_8_3_2_B_4_2.py @@ -41,6 +41,7 @@ def normalize(df): class Zeus_8_3_2_B_4_2(IStrategy): levels = [1, 2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] + startup_candle_count = 24 # ROI table: minimal_roi = { @@ -66,15 +67,21 @@ class Zeus_8_3_2_B_4_2(IStrategy): plot_config = { "main_plot": { - "min200": { - "color": "#86c932" - }, - "max50": { + "sma5_1h": { "color": "white" }, - "max200": { + "sma20": { "color": "yellow" }, + "sma20_smooth": { + "color": "green" + }, + "sma20_smooth_2": { + "color": "red", + }, + "sma20_smooth_3": { + "color": "blue", + }, "bb_lowerband": { "color": "#da59a6"}, "bb_upperband": { @@ -82,22 +89,48 @@ class Zeus_8_3_2_B_4_2(IStrategy): } }, "subplots": { - "Rsi": { - "rsi": { - "color": "pink" + "Pct": { + "sma20_pct": { + 'color': "green" } }, - "Percent": { - "max_min": { - "color": "#74effc" + "Rsi": { + "rsi60": { + "color": "red" + }, + 'rsi60_diff': { + "color": "blue" + }, + 'rsi60_diff2': { + 'color': "green" + } + }, + "Rsi_diff": { + "rsi_diff_1h": { + "color": "red" + }, + "rsi_diff_2_1h": { + "color": "blue" } }, "smooth": { - 'mid_smooth_deriv1': { + 'sma5_diff_sum_1h': { + "color": "green" + }, + 'sma5_diff2_sum_1h': { "color": "blue" }, + # 'mid_smooth_deriv1': { + # "color": "blue" + # }, 'mid_smooth_deriv1_1h': { "color": "red" + }, + # 'mid_smooth_deriv2': { + # "color": "pink" + # }, + 'mid_smooth_deriv2_1h': { + "color": "#da59a6" } } } @@ -175,8 +208,9 @@ class Zeus_8_3_2_B_4_2(IStrategy): self.pairs[pair]['current_profit'] = 0 self.printLog( - f"|{'-' * 18}+{'-' * 12}+{'-' * 5}+{'-' * 20}+{'-' * 14}+{'-' * 8}+{'-' * 10}+{'-' * 8}+{'-' * 13}+" - f"{'-' * 14}+{'-' * 14}+{'-' * 4}+{'-' * 7}|" + f"|{'-' * 18}+{'-' * 12}+{'-' * 5}+{'-' * 20}+{'-' * 9}+{'-' * 8}+{'-' * 10}+{'-' * 8}+{'-' * 13}" + f"+{'-' * 14}+{'-' * 9}+{'-' * 4}+{'-' * 7}|" + ) stake_amount = self.adjust_stake_amount(pair, last_candle) @@ -184,7 +218,7 @@ class Zeus_8_3_2_B_4_2(IStrategy): self.log_trade( last_candle=last_candle, date=current_time, - action="START BUY" if allow_to_buy else "Canceled", + action="Buy" if allow_to_buy else "Canceled", pair=pair, rate=rate, dispo=dispo, @@ -246,6 +280,7 @@ class Zeus_8_3_2_B_4_2(IStrategy): dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() + last_candle_1h = dataframe.iloc[-13].squeeze() before_last_candle = dataframe.iloc[-2].squeeze() expected_profit = self.expectedProfit(pair, last_candle) @@ -260,7 +295,7 @@ class Zeus_8_3_2_B_4_2(IStrategy): self.pairs[pair]['current_profit'] = current_profit pct_first = round((last_candle['close'] - self.pairs[pair]['first_buy']) / self.pairs[pair]['first_buy'], 3) - if (last_candle['tendency'] in ('H++', 'H+')) : + if (last_candle['tendency'] in ('H++', 'H+')) and (last_candle['rsi'] < 80): # and (last_candle['tendency_1h'] in ('H++', 'H+')): # and (last_candle['tendency_1d'] in ('H++', 'H+')) : return None @@ -331,10 +366,10 @@ class Zeus_8_3_2_B_4_2(IStrategy): # f"|{'-' * 18}+{'-' * 12}+{'-' * 12}+{'-' * 20}+{'-' * 14}+{'-' * 8}+{'-' * 10}+{'-' * 7}+{'-' * 13}+{'-' * 14}+{'-' * 14}+{'-' * 7}+{'-' * 12}|" # ) self.printLog( - f"| {'Date':<16} | {'Action':<10} |{'Pair':<5}| {'Trade Type':<18} | {'Rate':>12} | {'Dispo':>6} | {'Profit':>8} | {'Pct':>6} | {'max_touch':>11} | {'last_lost':>12} | {'last_max':>12} |{'Buys':>4}| {'Stake':>5} |" + f"| {'Date':<16} | {'Action':<10} |{'Pair':<5}| {'Trade Type':<18} | {'Rate':>8} | {'Dispo':>6} | {'Profit':>8} | {'Pct':>6} | {'max_touch':>11} | {'last_lost':>12} | {'last_max':>7} |{'Buys':>4}| {'Stake':>5} |" ) self.printLog( - f"|{'-' * 18}+{'-' * 12}+{'-' * 5}+{'-' * 20}+{'-' * 14}+{'-' * 8}+{'-' * 10}+{'-' * 8}+{'-' * 13}+{'-' * 14}+{'-' * 14}+{'-' * 4}+{'-' * 7}|" + f"|{'-' * 18}+{'-' * 12}+{'-' * 5}+{'-' * 20}+{'-' * 9}+{'-' * 8}+{'-' * 10}+{'-' * 8}+{'-' * 13}+{'-' * 14}+{'-' * 9}+{'-' * 4}+{'-' * 7}|" ) self.columns_logged += 1 date = str(date)[:16] if date else "-" @@ -375,12 +410,12 @@ class Zeus_8_3_2_B_4_2(IStrategy): + " " + str(int(last_candle['rsi_diff_1h'])) self.printLog( - f"| {date:<16} | {action:<10} | {pair[0:3]:<3} | {trade_type or '-':<18} | {rate or '-':>12} | {dispo or '-':>6} " + f"| {date:<16} | {action:<10} | {pair[0:3]:<3} | {trade_type or '-':<18} |{rate or '-':>9}| {dispo or '-':>6} " f"| {profit or '-':>8} | {pct_max or '-':>6} | {round(self.pairs[pair]['max_touch'], 2) or '-':>11} | {last_lost or '-':>12} " - f"| {round(self.pairs[pair]['last_max'], 2) or '-':>12} |{buys or '-':>4}|{stake or '-':>7}" - f"|{round(last_candle['sma5_1d'], 2) or '-':>8}" + f"| {round(self.pairs[pair]['last_max'], 0) or '-':>7} |{buys or '-':>4}|{stake or '-':>7}" + f"|{round(last_candle['sma5_diff_sum_1h'], 2) or '-':>6}|{round(last_candle['sma5_diff_sum_1d'], 2) or '-':>6}" f"|{last_candle['tendency'] or '-':>3}|{last_candle['tendency_1h'] or '-':>3}|{last_candle['tendency_1d'] or '-':>3}" - f"|{round(last_candle['mid_smooth_deriv1']):>3}|{round(last_candle['mid_smooth_deriv1_1h']):>5}|" + f"|{round(last_candle['mid_smooth_deriv1']):>3}|{round(last_candle['mid_smooth_deriv1_1h']):>5}|{round(last_candle['mid_smooth_deriv1_1d']):>5}|" ) def printLog(self, str): @@ -440,6 +475,11 @@ class Zeus_8_3_2_B_4_2(IStrategy): dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma20_pct'] = 100 * dataframe['sma20'].diff() / dataframe['sma20'] + dataframe['sma20_smooth'] = dataframe['sma20'].ewm(span=5).mean() + dataframe['sma20_smooth_2'] = dataframe['sma20'].rolling(window=5, center=True).median() + dataframe['sma20_smooth_3'] = self.smooth_series(dataframe['sma20'], alpha_low=0.05, alpha_high=0.3, threshold=0.2) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] dataframe["percent3"] = (dataframe["close"] - dataframe["open"].shift(3)) / dataframe["open"].shift(3) dataframe["percent5"] = (dataframe["close"] - dataframe["open"].shift(5)) / dataframe["open"].shift(5) @@ -449,10 +489,14 @@ class Zeus_8_3_2_B_4_2(IStrategy): dataframe["percent_max_144"] = (dataframe["close"] - dataframe["max144"]) / dataframe["close"] # print(metadata['pair']) - dataframe['rsi'] = talib.RSI(dataframe['close'], length=14) + dataframe['rsi'] = talib.RSI(dataframe['close'], timeperiod=14) dataframe['rsi_diff'] = dataframe['rsi'].diff() dataframe['rsi_diff_2'] = dataframe['rsi_diff'].diff() + dataframe['rsi60'] = talib.RSI(dataframe['close'], timeperiod=60) + dataframe['rsi60_diff'] = dataframe['rsi60'].rolling(60).mean().diff() + dataframe['rsi60_diff2'] = dataframe['rsi60_diff'].rolling(60).mean().diff() + # Bollinger Bands bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] @@ -500,21 +544,26 @@ class Zeus_8_3_2_B_4_2(IStrategy): # Créer une colonne vide dataframe['down_pct'] = self.calculateUpDownPct(dataframe, 'down_count') dataframe['up_pct'] = self.calculateUpDownPct(dataframe, 'up_count') + dataframe = self.apply_regression_derivatives(dataframe, column='mid', window=24, degree=3) # Normaliser les données de 'close' # normalized_close = self.min_max_scaling(dataframe['close']) ################### INFORMATIVE 1h informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") informative = self.calculateTendency(informative, 3) - informative = self.apply_regression_derivatives(informative, column='mid', window=50, degree=3) + informative = self.apply_regression_derivatives(informative, column='mid', window=5, degree=3) informative['volatility'] = talib.STDDEV(informative['close'], timeperiod=14) / informative['close'] informative['atr'] = (talib.ATR(informative['high'], informative['low'], informative['close'], timeperiod=14)) / informative['close'] informative['rsi'] = talib.RSI(informative['close'], length=7) informative['rsi_diff'] = informative['rsi'].diff() + informative['rsi_sum'] = (informative['rsi'].rolling(7).sum() - 350) / 7 + informative['rsi_sum_diff'] = informative['rsi_sum'].diff() informative['rsi_diff_2'] = informative['rsi_diff'].diff() informative['sma5'] = talib.SMA(informative, timeperiod=5) informative['sma5_pct'] = 100 * (informative['sma5'] - informative['sma5'].shift(1)) / informative['sma5'] + informative['sma5_diff_sum'] = (informative['sma5_pct'].rolling(5).sum()) / 5 + informative['sma5_diff2_sum'] = informative['sma5_diff_sum'].diff() dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) @@ -522,13 +571,16 @@ class Zeus_8_3_2_B_4_2(IStrategy): informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") informative = self.calculateTendency(informative, 3) - informative = self.apply_regression_derivatives(informative, column='mid', window=50, degree=3) + informative = self.apply_regression_derivatives(informative, column='mid', window=5, degree=3) informative['rsi'] = talib.RSI(informative['close'], length=7) informative['rsi_diff'] = informative['rsi'].diff() + informative['rsi_sum'] = (informative['rsi'].rolling(7).sum() - 350) / 7 informative['rsi_diff_2'] = informative['rsi_diff'].diff() informative['sma5'] = talib.SMA(informative, timeperiod=5) informative['sma5_pct'] = 100 * (informative['sma5'] - informative['sma5'].shift(1)) / informative['sma5'] + informative['sma5_diff_sum'] = (informative['sma5_pct'].rolling(5).sum()) / 5 + informative['sma5_diff2_sum'] = informative['sma5_diff_sum'].diff() sorted_close_prices = informative['close'].tail(365).sort_values() lowest_4 = sorted_close_prices.head(4) @@ -550,7 +602,6 @@ class Zeus_8_3_2_B_4_2(IStrategy): informative['middle'] = informative['lowest_4'] + (informative['highest_4'] - informative['lowest_4']) / 2 informative['mid_min_max_0.98'] = informative['mid_min_max'] * 0.98 - dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) dataframe['count_buys'] = 0 @@ -769,7 +820,7 @@ class Zeus_8_3_2_B_4_2(IStrategy): # (dataframe['rsi_1h'] < 70) # & (dataframe['rsi_diff_1h'] > -5) # (dataframe["bb_width"] > 0.01) - (dataframe['down_count'].shift(1) < - 6) + (dataframe['down_count'].shift(1) < - 1) & (dataframe['down_count'] == 0) # & (dataframe['tendency'] != "B--") # & (dataframe['tendency'] != "B-") @@ -782,6 +833,13 @@ class Zeus_8_3_2_B_4_2(IStrategy): # & (dataframe['tendency'] != "B--") # & (dataframe['tendency'] != "B-") ), ['enter_long', 'enter_tag']] = (1, 'low') + # dataframe.loc[ + # ( + # (dataframe['mid_smooth_deriv1'] > 0) + # & (dataframe['rsi'] < 50) + # & (dataframe['mid_smooth_deriv1'] > dataframe['mid_smooth_deriv1'].shift(1) * 1.5) + # ), ['enter_long', 'enter_tag']] = (1, 'mid') + dataframe['test'] = np.where(dataframe['enter_long'] == 1, dataframe['close'] * 1.01, np.nan) return dataframe @@ -853,12 +911,16 @@ class Zeus_8_3_2_B_4_2(IStrategy): ) \ and (last_candle['rsi_diff_1h'] >= -5) \ and (last_candle['tendency'] in ('P', 'H++', 'DH', 'H+')) \ - and ((pct_max < lim)): + and (last_candle['sma5_diff_sum_1d'] > -1 or count_of_buys <= 9) \ + and (pct_max < lim): try: - # print(self.adjust_stake_amount(pair, last_candle)) - # print(pct_first) - # print(pct) - stake_amount = min(self.wallets.get_available_stake_amount(), + + # and (last_candle['mid_smooth_deriv1_1d'] > -1000 or last_candle['mid_smooth_deriv1_1h'] > 200) \ + # and (last_candle['mid_smooth_deriv1_1d'] > -1500) \ + # and not (last_candle['mid_smooth_deriv1_1d'] < - 500 and last_candle['mid_smooth_deriv1_1h'] < 0) \ + + max_amount = self.config.get('stake_amount', 100) * 2.5 + stake_amount = min(min(max_amount, self.wallets.get_available_stake_amount()), self.adjust_stake_amount(pair, last_candle) - 10 * pct_first / pct) # min(200, self.adjust_stake_amount(pair, last_candle) * self.fibo[count_of_buys]) trade_type = last_candle['enter_tag'] if last_candle['enter_long'] == 1 else 'pct48' @@ -975,7 +1037,7 @@ class Zeus_8_3_2_B_4_2(IStrategy): first_price = last_candle['first_price'] first_max = (last_candle['max200'] - first_price) / first_price - expected_profit = min(0.01, first_max * 0.5) + expected_profit = min(0.01, first_max) # print( # f"Expected profit price={current_price:.4f} min_max={min_max:.4f} min_14={min_14_days:.4f} max_14={max_14_days:.4f} percent={percent:.4f} expected_profit={expected_profit:.4f}") @@ -1350,6 +1412,25 @@ class Zeus_8_3_2_B_4_2(IStrategy): print(f"update all col {col} {value}") df[col] = value + def smooth_series(self, series, alpha_low=0.1, alpha_high=0.5, threshold=0.2): + """ + Applique un lissage adaptatif sur une série Pandas. + - alpha_low : lissage fort (forte inertie, pour petites variations) + - alpha_high : lissage faible (rapide réaction, pour grandes variations) + - threshold : variation (%) à partir de laquelle on considère le mouvement significatif + """ + smoothed = [series.iloc[0]] + for i in range(1, len(series)): + prev = smoothed[-1] + current = series.iloc[i] + variation = abs(current - prev) / prev * 100 if prev != 0 else 0 + + alpha = alpha_high if variation > threshold else alpha_low + new_val = prev + alpha * (current - prev) + smoothed.append(new_val) + + return pd.Series(smoothed, index=series.index) + # def update_last_record(self, dataframe: DataFrame, new_data): # # Vérifiez si de nouvelles données ont été reçues # if new_data is not None: @@ -1397,37 +1478,80 @@ class Zeus_8_3_2_B_4_2(IStrategy): dataframe['close'].iloc[i - shift_value] return down_pct_values - def apply_regression_derivatives(self, dataframe: DataFrame, column: str = 'close', window: int = 50, degree: int = 3): + # ✅ Première dérivée(variation ou pente) + # Positive: la courbe est croissante → tendance haussière. + # Négative: la courbe est décroissante → tendance baissière. + # Proche de 0: la courbe est plate → marché stable ou en transition. + # + # Applications: + # Détecter les points d’inflexion(changement de tendance) quand elle s’annule.\ + # Analyser la vitesse d’un mouvement(plus elle est forte, plus le mouvement est impulsif). + # + # ✅ Seconde dérivée(accélération ou concavité) + # Positive: la pente augmente → accélération de la hausse ou ralentissement de la baisse. + # Négative: la pente diminue → accélération de la baisse ou ralentissement de la hausse. + # Changement de signe: indique souvent un changement de courbure, utile pour prévoir des retournements. + # + # Exemples: + # 🟢 Dérivée 1 > 0 et dérivée 2 > 0: tendance haussière qui s’accélère. + # 🟡 Dérivée 1 > 0 et dérivée 2 < 0: tendance haussière qui ralentit → essoufflement potentiel. + # 🔴 Dérivée 1 < 0 et dérivée 2 < 0: tendance baissière qui s’accélère. + # 🟠 Dérivée 1 < 0 et dérivée 2 > 0: tendance baissière qui ralentit → possible bottom. + # + # Filtrer les signaux: ne prendre un signal haussier que si dérivée1 > 0 et dérivée2 > 0. + # Détecter les zones de retournement: quand dérivée1 ≈ 0 et que dérivée2 change de signe. + def apply_regression_derivatives(self, + dataframe: DataFrame, + column: str = 'close', + window: int = 50, + degree: int = 3, + future_offset: int = 10 # projection à n bougies après + ) -> DataFrame: df = dataframe.copy() + + regression_fit = [] deriv1 = [] deriv2 = [] + regression_future_fit = [] + regression_future_deriv1 = [] + regression_future_deriv2 = [] for i in range(len(df)): - if i < window: + if i < window or i + future_offset >= len(df): + regression_fit.append(np.nan) deriv1.append(np.nan) deriv2.append(np.nan) + regression_future_fit.append(np.nan) + regression_future_deriv1.append(np.nan) + regression_future_deriv2.append(np.nan) continue y = df[column].iloc[i - window:i].values x = np.arange(window) - # Régression polynomiale coeffs = np.polyfit(x, y, degree) poly = np.poly1d(coeffs) - # Dérivées - d1 = np.polyder(poly, 1) # première dérivée - d2 = np.polyder(poly, 2) # seconde dérivée + x_now = window - 1 + x_future = x_now + future_offset - # On calcule les valeurs de la dérivée à la dernière bougie - val_d1 = d1(window - 1) - val_d2 = d2(window - 1) + regression_fit.append(poly(x_now)) + deriv1.append(np.polyder(poly, 1)(x_now)) + deriv2.append(np.polyder(poly, 2)(x_now)) - deriv1.append(val_d1) - deriv2.append(val_d2) + regression_future_fit.append(poly(x_future)) + regression_future_deriv1.append(np.polyder(poly, 1)(x_future)) + regression_future_deriv2.append(np.polyder(poly, 2)(x_future)) + df['regression_fit'] = regression_fit df['regression_deriv1'] = deriv1 df['regression_deriv2'] = deriv2 + df['regression_future_fit'] = regression_future_fit + df['regression_future_deriv1'] = regression_future_deriv1 + df['regression_future_deriv2'] = regression_future_deriv2 + return df + +