From 63b425aaf61e8aec414a22706a85f8c46888ae63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Delacotte?= Date: Wed, 25 Feb 2026 11:21:57 +0100 Subject: [PATCH] =?UTF-8?q?Backtested=202023-01-01=2000:00:00=20->=202026-?= =?UTF-8?q?02-20=2000:00:00=20|=20Max=20open=20trades=20:=201=20=E2=94=8F?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=B3=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=B3=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=B3?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=B3=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=B3=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=B3=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=B3?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=93=20=E2=94=83=20Strategy=20=E2=94=83=20Trades=20?= =?UTF-8?q?=E2=94=83=20Avg=20Profit=20%=20=E2=94=83=20Tot=20Profit=20USDT?= =?UTF-8?q?=20=E2=94=83=20Tot=20Profit=20%=20=E2=94=83=20=20=20=20=20Avg?= =?UTF-8?q?=20Duration=20=E2=94=83=20=20Win=20=20Draw=20=20Loss=20=20Win%?= =?UTF-8?q?=20=E2=94=83=20=20=20=20=20=20=20=20=20=20=20=20Drawdown=20?= =?UTF-8?q?=E2=94=83=20=E2=94=A1=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=95=87?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=95=87=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=95=87=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=95=87?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=95=87=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=95=87=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=95=87=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81=E2=94=81?= =?UTF-8?q?=E2=94=81=E2=94=81=E2=94=81=E2=94=A9=20=E2=94=82=20=20=20=20Emp?= =?UTF-8?q?ty=20=E2=94=82=20=20=20=20=2085=20=E2=94=82=20=20=20=20=20=20?= =?UTF-8?q?=20=20=201.06=20=E2=94=82=20=20=20=20=20=20=20=201239.222=20?= =?UTF-8?q?=E2=94=82=20=20=20=20=20=20=20123.92=20=E2=94=82=202=20days,=20?= =?UTF-8?q?12:39:00=20=E2=94=82=20=20=2054=20=20=20=20=200=20=20=20=2031?= =?UTF-8?q?=20=2063.5=20=E2=94=82=20206.862=20USDT=20=208.61%=20=E2=94=82?= =?UTF-8?q?=20=E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80?= =?UTF-8?q?=E2=94=80=E2=94=80=E2=94=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Empty.py | 78 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/Empty.py b/Empty.py index 8b98142..ae232fd 100644 --- a/Empty.py +++ b/Empty.py @@ -324,7 +324,8 @@ class Empty(IStrategy): 'force_buy': False, 'current_trade': None, 'last_trade': None, - 'force_stop': False + 'force_stop': False, + 'count_of_lost': 0 } for pair in ["BTC/USDC", "BTC/USDT"] } @@ -432,7 +433,7 @@ class Empty(IStrategy): sl_max = self.wallets.get_available_stake_amount() / 2 amount = sl_min + (1 - range_pos) * (sl_max - sl_min) - # amount = self.wallets.get_available_stake_amount() / 8 + amount = self.wallets.get_available_stake_amount() return min(amount, self.wallets.get_available_stake_amount()) @@ -535,7 +536,9 @@ class Empty(IStrategy): last_candle_2 = dataframe.iloc[-2].squeeze() last_candle_3 = dataframe.iloc[-3].squeeze() - condition = True + condition = False if self.pairs[pair]['count_of_lost'] >= 1 \ + and (last_candle['sma12_deriv1_1d'] < 0.001 \ + or last_candle['sma12_deriv2_1d'] < 0.001) else True if condition and last_candle['range_pos'] > 0.05:# and self.pairs[pair]['force_stop']: condition = last_candle['sma5'] > last_candle['sma60'] @@ -619,7 +622,6 @@ class Empty(IStrategy): if allow_to_sell: self.pairs[pair]['last_sell'] = rate self.pairs[pair]['last_candle'] = last_candle - self.pairs[pair]['max_profit'] = 0 profit = trade.calc_profit(rate) self.pairs[pair]['previous_profit'] = profit dispo = round(self.wallets.get_available_stake_amount()) @@ -645,6 +647,12 @@ class Empty(IStrategy): self.pairs[pair]['last_date'] = current_time self.pairs[pair]['last_trade'] = trade self.pairs[pair]['current_trade'] = None + self.pairs[pair]['max_profit'] = 0 + + if profit < 0: + self.pairs[pair]['count_of_lost'] += 1 + else: + self.pairs[pair]['count_of_lost'] = 0 else: print(f"{current_time} STOP triggered for {pair} ({exit_reason}) but condition blocked", "warning") return (allow_to_sell) | (exit_reason == 'force_exit') | (exit_reason == 'stop_loss') | force @@ -656,12 +664,14 @@ class Empty(IStrategy): before_last_candle = dataframe.iloc[-2] self.pairs[pair]['current_trade'] = trade momentum = last_candle[self.sell_score_indicator.value] + profit = trade.calc_profit(current_rate) #round(current_profit * trade.stake_amount, 1) count_of_buys = trade.nr_of_successful_entries expected_profit = self.expectedProfit(pair, last_candle) self.pairs[pair]['max_profit'] = max(self.pairs[pair]['max_profit'], profit) max_profit = self.pairs[pair]['max_profit'] + baisse = 0 if profit > 0: baisse = 1 - (profit / max_profit) @@ -669,22 +679,27 @@ class Empty(IStrategy): self.pairs[pair]['count_of_buys'] = count_of_buys self.pairs[pair]['current_profit'] = profit - dispo = round(self.wallets.get_available_stake_amount()) + self.pairs[pair]['total_amount'] + dispo = round(self.wallets.get_available_stake_amount() + self.pairs[pair]['total_amount']) - self.log_trade( - last_candle=last_candle, - date=current_time, - action=("🔴 NOW" if self.pairs[pair]['stop'] else "🟢 NOW "), - dispo=dispo, - pair=pair, - rate=last_candle['close'], - trade_type='momentum' + str(round(momentum, 2)), - profit=round(profit, 2), - buys=count_of_buys, - stake=0 - ) + # self.log_trade( + # last_candle=last_candle, + # date=current_time, + # action=("🔴 NOW" if self.pairs[pair]['stop'] else "🟢 NOW "), + # dispo=dispo, + # pair=pair, + # rate=last_candle['close'], + # trade_type='momentum' + str(round(momentum, 2)), + # profit=round(profit, 2), + # buys=count_of_buys, + # stake=0 + # ) - if profit > max(5, expected_profit) and baisse > 0.30: + if current_profit < - 0.02 and last_candle[f"close"] <= last_candle['sma60']: + self.pairs[pair]['force_sell'] = True + return 'sma60' + + if profit > max(5, expected_profit) and baisse > 0.30 and last_candle[f"close"] <= last_candle['sma5']\ + and last_candle['percent3'] < 0 and last_candle['percent5'] < 0: self.pairs[pair]['force_sell'] = True self.pairs[pair]['force_buy'] = (self.pairs[pair]['count_of_buys'] - self.pairs[pair]['has_gain'] > 3) return str(count_of_buys) + '_' + 'B30_' + pair + '_' + str(self.pairs[pair]['has_gain']) @@ -712,14 +727,15 @@ class Empty(IStrategy): # self.pairs[pair]['force_sell'] = True # return 'Stop' # Si momentum fort → on laisse courir + if momentum > 1: return None # Si momentum faiblit → on prend profit plus tôt - if momentum < 0 and current_profit > 0.02 and last_candle[self.sell_score_indicator.value] < before_last_candle[self.sell_score_indicator.value]\ - and last_candle['close'] < last_candle['sma5']: - self.pairs[pair]['last_profit'] = current_profit - return "momentum_drop" + # if momentum < 0 and current_profit > 0.02 and last_candle[self.sell_score_indicator.value] < before_last_candle[self.sell_score_indicator.value]\ + # and last_candle['close'] < last_candle['sma5']: + # self.pairs[pair]['last_profit'] = current_profit + # return "momentum_drop" return None @@ -801,6 +817,9 @@ class Empty(IStrategy): informative[f"sma{timeperiod}"] = informative['mid'].ewm(span=timeperiod, adjust=False).mean() self.calculeDerivees(informative, f"sma{timeperiod}", timeframe=self.timeframe, ema_period=timeperiod) + informative['rsi'] = talib.RSI(informative['close'], timeperiod=14) + informative['max_rsi_12'] = talib.MAX(informative['rsi'], timeperiod=12) + informative['max_rsi_24'] = talib.MAX(informative['rsi'], timeperiod=24) dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) # ###################################################################################################### @@ -809,9 +828,13 @@ class Empty(IStrategy): range_max = dataframe[f"max48"] dataframe[f"range_pos"] = ((dataframe['mid'] - range_min) / (range_max)).rolling(5).mean() - dataframe['cross_sma60'] = qtpylib.crossed_above(dataframe['sma5'], dataframe['sma60']) + dataframe['cross_sma60'] = qtpylib.crossed_below(dataframe['sma12'], dataframe['sma5_1d']) dataframe[f'stop_buying'] = qtpylib.crossed_below(dataframe[f"sma12"], dataframe['sma48']) + dataframe['rsi'] = talib.RSI(dataframe['close'], timeperiod=14) + dataframe['max_rsi_12'] = talib.MAX(dataframe['rsi'], timeperiod=12) + dataframe['max_rsi_24'] = talib.MAX(dataframe['rsi'], timeperiod=24) + # récupérer le dernier trade fermé trades = Trade.get_trades_proxy(pair=pair,is_open=False) if trades: @@ -883,7 +906,10 @@ class Empty(IStrategy): conditions.append(dataframe['sma5_deriv1_1d'] > self.buy_deriv_sma5d.value) conditions.append(dataframe['sma12_deriv1_1d'] > self.buy_deriv_sma12d.value) conditions.append(dataframe['hapercent'] > 0) - # # conditions.append(dataframe[f"range_pos"] <= 0.5) + conditions.append(dataframe['max_rsi_24'] < 80) + conditions.append(dataframe['max_rsi_12_1d'] < 65) + + conditions.append(dataframe[f"close"] > dataframe['sma60']) conditions.append(((dataframe[f"range_pos"] < 0.05) ) | ((dataframe['sma12_deriv1'] > 0) & (dataframe['sma12_deriv2'] > 0))) # print(f"BUY indicators tested \n" @@ -1076,7 +1102,7 @@ class Empty(IStrategy): self.printLog( f"| {'Date':<16} | {'Action':<10} |{'Pair':<5}| {'Trade Type':<18} |{'Rate':>8} | {'Dispo':>6} | {'Profit':>8} " f"| {'Pct':>6} | {'max_touch':>11} | {'last_lost':>12} | {'last_max':>7}| {'last_max':>7}|{'Buys':>5}| {'Stake':>5} |" - f"{'rsi':>6}|Distmax|s201d|s5_1d|s5_2d|s51h|s52h|smt1h|smt2h|tdc1d|tdc1h" + f"{'rsi':>6}|" ) self.printLineLog() df = pd.DataFrame.from_dict(self.pairs, orient='index') @@ -1150,7 +1176,7 @@ class Empty(IStrategy): last_min = int(self.pairs[pair]['last_min']) if self.pairs[pair]['last_min'] > 1 else round( self.pairs[pair]['last_min'], 3) - profit = str(profit) + '/' + str(round(self.pairs[pair]['max_profit'], 2)) + profit = str(round(profit, 2)) + '/' + str(round(self.pairs[pair]['max_profit'], 2)) # 🟢 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.