From 96d6d4b67943b78de02eb5933bf176d1de4c991e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Delacotte?= Date: Tue, 7 Oct 2025 20:07:36 +0200 Subject: [PATCH] Zeus_8_1d 20250101-20250714 348.501 --- Zeus_8_1d.py | 1539 ++++++++++++++++++++++++++++++++++++ Zeus_8_3_2_B_4_2_Bilan.txt | 372 ++++----- 2 files changed, 1710 insertions(+), 201 deletions(-) create mode 100644 Zeus_8_1d.py diff --git a/Zeus_8_1d.py b/Zeus_8_1d.py new file mode 100644 index 0000000..31399d0 --- /dev/null +++ b/Zeus_8_1d.py @@ -0,0 +1,1539 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from freqtrade.persistence import Trade +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, stoploss_from_open, + IntParameter, IStrategy, merge_informative_pair, informative, stoploss_from_absolute) +import pandas as pd +import numpy as np +from pandas import DataFrame +from typing import Optional, Union, Tuple +from typing import List + +import logging +import configparser +from technical import pivots_points +# -------------------------------- + +# Add your lib to import here test git +import ta +import talib.abstract as talib +import freqtrade.vendor.qtpylib.indicators as qtpylib +import requests +from datetime import timezone, timedelta +from scipy.signal import savgol_filter +from ta.trend import SMAIndicator, EMAIndicator, MACD, ADXIndicator +from collections import Counter + +logger = logging.getLogger(__name__) + +from tabulate import tabulate + +# Couleurs ANSI de base +RED = "\033[31m" +GREEN = "\033[32m" +YELLOW = "\033[33m" +BLUE = "\033[34m" +MAGENTA = "\033[35m" +CYAN = "\033[36m" +RESET = "\033[0m" + + +def pprint_df(dframe): + print(tabulate(dframe, headers='keys', tablefmt='psql', showindex=False)) + + +def normalize(df): + df = (df - df.min()) / (df.max() - df.min()) + return df + + +class Zeus_8_1d(IStrategy): + levels = [1, 2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] + startup_candle_count = 12 * 24 * 2 + + # ROI table: + minimal_roi = { + "0": 10 + } + stakes = 40 + + # Stoploss: + stoploss = -1 # 0.256 + # Custom stoploss + use_custom_stoploss = True + + # Buy hypers + timeframe = '5m' + + max_open_trades = 5 + max_amount = 40 + + # DCA config + position_adjustment_enable = True + + plot_config = { + "main_plot": { + "mid_smooth_5": { + "color": "blue" + }, + "mid_smooth_12": { + "color": "green" + }, + "mid_smooth_24": { + "color": "yellow" + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + } + }, + "subplots": { + "Rsi": { + "max_rsi_12": { + "color": "blue" + }, + }, + "Deriv1": { + "mid_smooth_5_deriv1": { + "color": "blue" + }, + "mid_smooth_12_deriv1": { + "color": "green" + }, + "mid_smooth_24_deriv1": { + "color": "yellow" + }, + }, + "Deriv2": { + "mid_smooth_5_deriv2": { + "color": "blue" + }, + "mid_smooth_12_deriv2": { + "color": "green" + }, + "mid_smooth_24_deriv2": { + "color": "yellow" + }, + }, + "Down": { + "percage_upperband": { + "color": "green" + }, + "percage_up": { + "color": "blue" + } + } + # "Diff": { + # "sma10_deriv1": { + # "color": "#74effc" + # } + # }, + } + } + columns_logged = False + pairs = { + pair: { + "first_buy": 0, + "last_buy": 0.0, + "last_min": 999999999999999.5, + "last_max": 0, + "trade_info": {}, + "max_touch": 0.0, + "last_sell": 0.0, + 'count_of_buys': 0, + 'current_profit': 0, + 'expected_profit': 0, + "last_candle": {}, + "last_trade": None, + "last_count_of_buys": 0, + 'base_stake_amount': 0, + 'stop_buy': False, + 'last_date': 0, + 'stop': False, + 'max_profit': 0, + 'last_palier_index': -1, + 'total_amount': 0, + 'has_gain': 0, + 'force_sell': False, + 'force_buy': False + } + for pair in ["BTC/USDC", "ETH/USDC", "DOGE/USDC", "XRP/USDC", "SOL/USDC", + "BTC/USDT", "ETH/USDT", "DOGE/USDT", "XRP/USDT", "SOL/USDT"] + } + # 20 20 40 60 100 160 260 420 + # 50 50 100 300 500 + # fibo = [1, 1, 2, 3, 5, 8, 13, 21] + # my fibo + # 50 50 50 100 100 150 200 250 350 450 600 1050 + fibo = [1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21] + baisse = [1, 2, 3, 5, 7, 10, 14, 19, 26, 35, 47, 63, 84] + # Ma suite 1 1 1 2 2 3 4 5 7 9 12 16 21 + # Mise 50 50 50 100 100 150 200 250 350 450 600 800 1050 + # Somme Mises 50 100 150 250 350 500 700 950 1300 1750 2350 3150 4200 + # baisse 1 2 3 5 7 10 14 19 26 35 47 63 84 + + # factors = [1, 1.1, 1.25, 1.5, 2.0, 3] + # thresholds = [2, 5, 10, 20, 30, 50] + + factors = [0.5, 0.75, 1, 1.25, 1.5, 2] + thresholds = [0, 2, 5, 10, 30, 45] + + trades = list() + max_profit_pairs = {} + + # ========================================================================= + # Parameters hyperopt + + mise_factor_buy = DecimalParameter(0.01, 0.2, default=0.05, decimals=2, space='buy', optimize=True, load=True) + + # Récupération des labels ordonnés + # labels = ['B5', 'B4', 'B3', 'B2', 'B1', 'N0', 'H1', 'H2', 'H3', 'H4', 'H5'] + # index_labels = ['B5', 'B4', 'B3', 'B2', 'B1', 'N0', 'H1', 'H2', 'H3', 'H4', 'H5'] + # ordered_labels = ['B5', 'B4', 'B3', 'B2', 'B1', 'N0', 'H1', 'H2', 'H3', 'H4', 'H5'] + + labels = ['B3', 'B2', 'B1', 'N0', 'H1', 'H2', 'H3'] + index_labels = ['B3', 'B2', 'B1', 'N0', 'H1', 'H2', 'H3'] + ordered_labels = ['B3', 'B2', 'B1', 'N0', 'H1', 'H2', 'H3'] + + label_to_index = {label: i for i, label in enumerate(ordered_labels)} + + # ========================================================================= + # paliers dérivées jour sma5 + sma5_deriv1 = [-1.1726, -0.2131, -0.1012, -0.0330, 0.0169, 0.0815, 0.2000, 4.0335] + sma5_deriv2 = [-1.9190, -0.1388, -0.0644, -0.0202, 0.0209, 0.0646, 0.1377, 4.2987] + + sma5_derive1_2_matrice = { + 'B3': [8.6, 10.8, 34.6, 35.0, 58.8, 61.9, 91.2], + 'B2': [0.0, 12.5, 9.1, 57.1, 63.3, 79.3, 89.5], + 'B1': [6.1, 12.5, 22.0, 46.8, 61.5, 70.0, 100.0], + 'N0': [0.0, 10.7, 37.0, 43.5, 75.0, 75.9, 100.0], + 'H1': [0.0, 18.5, 32.4, 35.9, 76.8, 82.9, 92.0], + 'H2': [0.0, 21.9, 16.0, 39.5, 69.7, 83.3, 100.0], + 'H3': [9.5, 29.2, 41.2, 57.9, 53.8, 86.8, 92.3], + } + sma5_derive1_2_matrice_df = pd.DataFrame(sma5_derive1_2_matrice, index=index_labels) + # Extraction de la matrice numérique + sma5_derive1_2_numeric_matrice = sma5_derive1_2_matrice_df.reindex(index=ordered_labels, + columns=ordered_labels).values + + # paliers = {} + + indicateur_achat_vente = 'mid_smooth_12'; + + should_enter_trade_count = 0 + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + minutes = 0 + if self.pairs[pair]['last_date'] != 0: + minutes = round(int((current_time - self.pairs[pair]['last_date']).total_seconds() / 60)) + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + last_candle_2 = dataframe.iloc[-2].squeeze() + last_candle_3 = dataframe.iloc[-3].squeeze() + # val = self.getProbaHausse144(last_candle) + + # allow_to_buy = True #(not self.stop_all) #& (not self.all_down) + allow_to_buy = not self.pairs[pair]['stop'] # and val > self.buy_val.value #not last_candle['tendency'] in ('B-', 'B--') # (rate <= float(limit)) | (entry_tag == 'force_entry') + + force = self.pairs[pair]['force_buy'] + if self.pairs[pair]['force_buy']: + self.pairs[pair]['force_buy'] = False + allow_to_buy = True + else: + if not self.should_enter_trade(pair, last_candle, current_time): + allow_to_buy = False + + if allow_to_buy: + self.trades = list() + self.pairs[pair]['first_buy'] = rate + self.pairs[pair]['last_buy'] = rate + self.pairs[pair]['max_touch'] = last_candle['close'] + self.pairs[pair]['last_candle'] = last_candle + self.pairs[pair]['count_of_buys'] = 1 + self.pairs[pair]['current_profit'] = 0 + self.pairs[pair]['last_palier_index'] = -1 + self.pairs[pair]['last_max'] = max(last_candle['close'], self.pairs[pair]['last_max']) + self.pairs[pair]['last_min'] = min(last_candle['close'], self.pairs[pair]['last_min']) + + dispo = round(self.wallets.get_available_stake_amount()) + self.printLineLog() + + stake_amount = self.adjust_stake_amount(pair, last_candle) + + self.pairs[pair]['total_amount'] = stake_amount + + self.log_trade( + last_candle=last_candle, + date=current_time, + action=("🟩Buy" if allow_to_buy else "Canceled") + " " + str(minutes), + pair=pair, + rate=rate, + dispo=dispo, + profit=0, + trade_type=entry_tag, + buys=1, + stake=round(stake_amount, 2) + ) + + return allow_to_buy + + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, + time_in_force: str, + exit_reason: str, current_time, **kwargs, ) -> bool: + # allow_to_sell = (minutes > 30) + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + force = self.pairs[pair]['force_sell'] + allow_to_sell = (last_candle['percent'] < 0) #or force + + minutes = int(round((current_time - trade.date_last_filled_utc).total_seconds() / 60, 0)) + + if allow_to_sell: + self.trades = list() + self.pairs[pair]['last_count_of_buys'] = trade.nr_of_successful_entries # self.pairs[pair]['count_of_buys'] + self.pairs[pair]['last_sell'] = rate + self.pairs[pair]['last_trade'] = trade + self.pairs[pair]['last_candle'] = last_candle + self.pairs[pair]['max_profit'] = 0 + self.trades = list() + dispo = round(self.wallets.get_available_stake_amount()) + # print(f"Sell {pair} {current_time} {exit_reason} dispo={dispo} amount={amount} rate={rate} open_rate={trade.open_rate}") + self.log_trade( + last_candle=last_candle, + date=current_time, + action="🟥Sell " + str(minutes), + pair=pair, + trade_type=exit_reason, + rate=last_candle['close'], + dispo=dispo, + profit=round(trade.calc_profit(rate, amount), 2) + ) + self.pairs[pair]['force_sell'] = False + self.pairs[pair]['has_gain'] = 0 + self.pairs[pair]['current_profit'] = 0 + self.pairs[pair]['total_amount'] = 0 + self.pairs[pair]['count_of_buys'] = 0 + self.pairs[pair]['max_touch'] = 0 + self.pairs[pair]['last_buy'] = 0 + self.pairs[pair]['last_date'] = current_time + self.pairs[pair]['last_palier_index'] = -1 + self.pairs[pair]['last_trade'] = trade + self.pairs[pair]['current_trade'] = None + + return (allow_to_sell) | (exit_reason == 'force_exit') + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + adjusted_stake_amount = self.adjust_stake_amount(pair, current_candle) + + # print(f"{pair} adjusted_stake_amount{adjusted_stake_amount}") + + # Use default stake amount. + return adjusted_stake_amount + + def custom_exit(self, pair: str, trade: Trade, current_time, current_rate, current_profit, **kwargs): + + 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() + before_last_candle_2 = dataframe.iloc[-3].squeeze() + before_last_candle_12 = dataframe.iloc[-13].squeeze() + before_last_candle_24 = dataframe.iloc[-25].squeeze() + + expected_profit = self.expectedProfit(pair, last_candle) + # print(f"current_time={current_time} current_profit={current_profit} expected_profit={expected_profit}") + + max_touch_before = self.pairs[pair]['max_touch'] + self.pairs[pair]['last_max'] = max(last_candle['close'], self.pairs[pair]['last_max']) + self.pairs[pair]['last_min'] = min(last_candle['close'], self.pairs[pair]['last_min']) + self.pairs[pair]['current_trade'] = trade + + count_of_buys = trade.nr_of_successful_entries + + profit = round(current_profit * trade.stake_amount, 1) + 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 = 100 * abs(max_profit - profit) / max_profit + mx = max_profit / 5 + self.pairs[pair]['count_of_buys'] = count_of_buys + self.pairs[pair]['current_profit'] = profit + + dispo = round(self.wallets.get_available_stake_amount()) + hours_since_first_buy = (current_time - trade.open_date_utc).seconds / 3600.0 + days_since_first_buy = (current_time - trade.open_date_utc).days + hours = (current_time - trade.date_last_filled_utc).total_seconds() / 3600.0 + + if hours % 4 == 0: + self.log_trade( + last_candle=last_candle, + date=current_time, + action="🔴 CURRENT" if self.pairs[pair]['stop'] else "🟢 CURRENT", + dispo=dispo, + pair=pair, + rate=last_candle['close'], + trade_type='', + profit=profit, + buys='', + stake=0 + ) + + pair_name = self.getShortName(pair) + + # if baisse > 20 and before_last_candle[self.indicateur_achat_vente] > last_candle[self.indicateur_achat_vente] : + # self.pairs[pair]['force_sell'] = False + # self.pairs[pair]['force_buy'] = (self.pairs[pair]['count_of_buys'] - self.pairs[pair]['has_gain'] > 5) + # return 'Baisse_' + pair_name + '_' + str(count_of_buys) + '_' + str(self.pairs[pair]['has_gain']) + '_' + str(round(baisse, 2)) + + # if before_last_candle_2['mid_smooth_5'] < before_last_candle['mid_smooth_5'] > last_candle['mid_smooth_5'] \ + # and (profit > expected_profit or (profit > 0 and last_candle['max_rsi_12'] > 65)): + # self.pairs[pair]['force_sell'] = False + # self.pairs[pair]['force_buy'] = (self.pairs[pair]['count_of_buys'] - self.pairs[pair]['has_gain'] > 5) + # return 'RSI_' + pair_name + '_' + str(count_of_buys) + '_' + str(self.pairs[pair]['has_gain']) + + if before_last_candle_2[self.indicateur_achat_vente] < before_last_candle[self.indicateur_achat_vente] > last_candle[self.indicateur_achat_vente] \ + and last_candle['mid_smooth_5_deriv1'] < 0 and profit > expected_profit: + self.pairs[pair]['force_sell'] = False + self.pairs[pair]['force_buy'] = (self.pairs[pair]['count_of_buys'] - self.pairs[pair]['has_gain'] > 5) + return 'Drv3_' + pair_name + '_' + str(count_of_buys) + '_' + str(self.pairs[pair]['has_gain']) + '_' + str(round(baisse, 2)) + + self.pairs[pair]['max_touch'] = max(last_candle['close'], self.pairs[pair]['max_touch']) + + def getShortName(self, pair): + return pair.replace("/USDT", '').replace("/USDC", '') + + def multi_step_interpolate(self, pct: float, thresholds: List[float], factors: List[float]) -> float: + if pct <= thresholds[0]: + return factors[0] + if pct >= thresholds[-1]: + return factors[-1] + + for i in range(1, len(thresholds)): + if pct <= thresholds[i]: + # interpolation linéaire entre thresholds[i-1] et thresholds[i] + return factors[i - 1] + (pct - thresholds[i - 1]) * (factors[i] - factors[i - 1]) / ( + thresholds[i] - thresholds[i - 1]) + + # Juste au cas où (devrait jamais arriver) + return factors[-1] + + def log_trade(self, action, pair, date, trade_type=None, rate=None, dispo=None, profit=None, buys=None, stake=None, + last_candle=None): + # Afficher les colonnes une seule fois + if self.config.get('runmode') == 'hyperopt' or self.dp.runmode.value in ('hyperopt'): + return + if self.columns_logged % 10 == 0: + self.printLog( + 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}| {'last_max':>7}|{'Buys':>5}| {'Stake':>5} |" + f"Tdc|{'val':>6}| RSI |s201d|s5_1d|s5_2d|s51h|s52h" + ) + self.printLineLog() + df = pd.DataFrame.from_dict(self.pairs, orient='index') + colonnes_a_exclure = ['last_candle', 'last_trade', 'last_palier_index', 'current_trade', + 'trade_info', 'last_date', 'expected_profit', 'last_count_of_buys', 'base_stake_amount', 'stop_buy'] + df_filtered = df[df['count_of_buys'] > 0].drop(columns=colonnes_a_exclure) + # df_filtered = df_filtered["first_buy", "last_max", "max_touch", "last_sell","last_buy", 'count_of_buys', 'current_profit'] + + print(df_filtered) + + self.columns_logged += 1 + date = str(date)[:16] if date else "-" + limit = None + # if buys is not None: + # limit = round(last_rate * (1 - self.fibo[buys] / 100), 4) + + rsi = '' + rsi_pct = '' + sma5_1d = '' + sma5_1h = '' + + sma5 = str(sma5_1d) + ' ' + str(sma5_1h) + + last_lost = self.getLastLost(last_candle, pair) + + if buys is None: + buys = '' + + max_touch = '' # round(last_candle['max12'], 1) #round(self.pairs[pair]['max_touch'], 1) + pct_max = self.getPctFirstBuy(pair, last_candle) + + total_counts = str(buys) + '/' + str(sum(pair_data['count_of_buys'] for pair_data in self.pairs.values())) + + dist_max = self.getDistMax(last_candle, pair) + val = self.getProbaHausseSma5d(last_candle) + + pct60 = round(100 * self.getPct60D(pair, last_candle), 2) + + color = GREEN if profit > 0 else RED + color_sma20 = GREEN if last_candle['sma20_deriv1'] > 0 else RED + color_sma5 = GREEN if last_candle['mid_smooth_5_deriv1'] > 0 else RED + color_sma5_2 = GREEN if last_candle['mid_smooth_5_deriv2'] > 0 else RED + color_sma5_1h = GREEN if last_candle['sma60_deriv1'] > 0 else RED + color_sma5_2h = GREEN if last_candle['sma60_deriv2'] > 0 else RED + + last_max = int(self.pairs[pair]['last_max']) if self.pairs[pair]['last_max'] > 1 else round(self.pairs[pair]['last_max'],3) + 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)) + + # 🟢 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. + + # tdc last_candle['tendency_12'] + self.printLog( + f"| {date:<16} |{action:<10} | {pair[0:3]:<3} | {trade_type or '-':<18} |{rate or '-':>9}| {dispo or '-':>6} " + f"|{color}{profit or '-':>10}{RESET}| {pct_max or '-':>6} | {round(self.pairs[pair]['max_touch'], 2) or '-':>11} | {last_lost or '-':>12} " + f"| {last_max or '-':>7} | {last_min or '-':>7} |{total_counts or '-':>5}|{stake or '-':>7}" + f"|{ last_candle['tendency_12'] or '-':>3}|" + f"{round(val, 1) or '-' :>6}|" + f"{round(last_candle['rsi'], 0):>7}|{color_sma20}{round(last_candle['sma20_deriv1'], 2):>5}{RESET}" + f"|{color_sma5}{round(last_candle['mid_smooth_5_deriv1'], 2):>5}{RESET}|{color_sma5_2}{round(last_candle['mid_smooth_5_deriv2'], 2):>5}{RESET}" + f"|{color_sma5_1h}{round(last_candle['sma60_deriv1'], 2):>5}{RESET}|{color_sma5_2h}{round(last_candle['sma60_deriv2'], 2):>5}{RESET}" + # f"|{last_candle['min60']}|{last_candle['max60']}" + ) + + def getLastLost(self, last_candle, pair): + last_lost = round((last_candle['close'] - self.pairs[pair]['max_touch']) / self.pairs[pair]['max_touch'], 3) + return last_lost + + def getDistMax(self, last_candle, pair): + mx = last_candle['max12'] + dist_max = round(100 * (mx - last_candle['close']) / mx, 0) + return dist_max + + def printLineLog(self): + self.printLog( + f"+{'-' * 18}+{'-' * 12}+{'-' * 5}+{'-' * 20}+{'-' * 9}+{'-' * 8}+{'-' * 12}+{'-' * 8}+{'-' * 13}+{'-' * 14}+{'-' * 9}{'-' * 9}+{'-' * 5}+{'-' * 7}+" + f"{'-' * 3}" + # "+{'-' * 3}+{'-' * 3} + f"+{'-' * 6}+{'-' * 7}+{'-' * 5}+{'-' * 5}+{'-' * 5}+{'-' * 5}+{'-' * 5}+{'-' * 5}+" + ) + + def printLog(self, str): + if self.config.get('runmode') == 'hyperopt' or self.dp.runmode.value in ('hyperopt'): + return; + if not self.dp.runmode.value in ('backtest', 'hyperopt', 'lookahead-analysis'): + logger.info(str) + else: + if not self.dp.runmode.value in ('hyperopt'): + print(str) + + def add_tendency_column(self, dataframe: pd.DataFrame, name, suffixe='') -> pd.DataFrame: + def tag_by_derivatives(row): + d1 = row[f"{name}{suffixe}_deriv1"] + d2 = row[f"{name}{suffixe}_deriv2"] + d1_lim_inf = -0.01 + d1_lim_sup = 0.01 + if d1 >= d1_lim_inf and d1 <= d1_lim_sup: # and d2 >= d2_lim_inf and d2 <= d2_lim_sup: + return 'P' # Palier + if d1 == 0.0: + return 'DH' if d2 > 0 else 'DB' # Depart Hausse / Départ Baisse + if d1 > d1_lim_sup: + return 'H++' if d2 > 0 else 'H+' # Acceleration Hausse / Ralentissement Hausse + if d1 < d1_lim_inf: + return 'B--' if d2 < 0 else 'B-' # Accéleration Baisse / Ralentissement Baisse + return 'Mid' + + dataframe[f"tendency{suffixe}"] = dataframe.apply(tag_by_derivatives, axis=1) + return dataframe + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + pair = metadata['pair'] + + heikinashi = qtpylib.heikinashi(dataframe) + dataframe['haopen'] = heikinashi['open'] + dataframe['haclose'] = heikinashi['close'] + dataframe['hapercent'] = (dataframe['haclose'] - dataframe['haopen']) / dataframe['haclose'] + dataframe['hapercent3'] = (dataframe['haclose'] - dataframe['haopen'].shift(3)) / dataframe['haclose'].shift(3) + + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + self.calculeDerivees(dataframe, 'sma5', horizon=10) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + self.calculeDerivees(dataframe, 'sma10', horizon=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + self.calculeDerivees(dataframe, 'sma20', horizon=20) + dataframe['sma60'] = talib.SMA(dataframe, timeperiod=60) + self.calculeDerivees(dataframe, 'sma60', horizon=60) + dataframe['sma144'] = talib.SMA(dataframe, timeperiod=144) + self.calculeDerivees(dataframe, 'sma144') + + 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) + dataframe["percent12"] = (dataframe["close"] - dataframe["open"].shift(12)) / dataframe["open"].shift(12) + + dataframe = self.calculateDerivation(dataframe, window=3, suffixe="_3") + # dataframe = self.calculateDerivation(dataframe, window=3, suffixe="_6") + + dataframe["mid_re_smooth_3"] = self.conditional_smoothing(dataframe['mid_smooth_3'].dropna(), + threshold=0.0005).dropna() + self.calculeDerivees(dataframe, "mid_re_smooth_3", horizon=3) + dataframe = self.calculateDerivation(dataframe, window=12, suffixe="_12") + dataframe = self.calculateDerivation(dataframe, window=24, suffixe="_24", factor_1=1000, factor_2=10) + + # print(metadata['pair']) + dataframe['rsi'] = talib.RSI(dataframe['close'], timeperiod=14) + dataframe['max_rsi_12'] = talib.MAX(dataframe['rsi'], timeperiod=12) + self.calculeDerivees(dataframe, 'rsi', horizon=12) + + dataframe['max12'] = talib.MAX(dataframe['close'], timeperiod=12) + dataframe['min12'] = talib.MIN(dataframe['close'], timeperiod=12) + dataframe['max60'] = talib.MAX(dataframe['close'], timeperiod=60) + dataframe['min60'] = talib.MIN(dataframe['close'], timeperiod=60) + + # Bollinger Bands + 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ((dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_upperband"]) + # Normalization + + dataframe['bb_upperband5'] = dataframe['bb_upperband'].rolling(window=5).mean() + dataframe['bb_lowerband5'] = dataframe['bb_lowerband'].rolling(window=5).mean() + + dataframe = self.calculateDerivation(dataframe, window=5, suffixe="_5") + dataframe = self.calculateDerivation(dataframe, window=12, suffixe="_12") + + dataframe['percage_up'] = dataframe['high'] > dataframe['bb_upperband'] + + dataframe['percage_upperband'] = dataframe['percage_up'].astype(int) * ( + dataframe['percage_up'].groupby((dataframe['percage_up'] != dataframe['percage_up'].shift()).cumsum()).cumcount() + 1) + + # informative['futur_percent_3d'] = 100 * (informative['close'].shift(-3) - informative['close']) / informative['close'] + # + # self.calculateProbabilite2Index(informative, ['futur_percent'], 'rsi_deriv1', 'rsi') + # # self.calculateProbabilite2Index(dataframe, ['futur_percent_3d'], 'rsi_deriv1', 'sma5') + + + # informative['close_smooth'] = self.conditional_smoothing(informative['mid'].dropna(), threshold=0.0015).dropna() + # informative['smooth'], informative['deriv1'], informative['deriv2'] = self.smooth_and_derivatives(informative['close_smooth']) + # informative['deriv1'] = 100 * informative['deriv1'] / informative['mid'] + # informative['deriv2'] = 1000 * informative['deriv2'] / informative['mid'] + + # poly_func, x_future, y_future, count = self.polynomial_forecast(informative['sma5_deriv1'], window=24, degree=4) + + # informative['futur_percent_3'] = 100 * ((informative['sma5'].shift(-1) - informative['sma5']) / informative['sma5']) + # futur_cols = ['futur_percent_3'] + # indic_1 = 'sma5_deriv1' + # indic_2 = 'sma5_deriv2' + + # self.calculateProbabilite2Index(informative, futur_cols, indic_1, indic_2) + + # self.calculePlateaux(informative, 14, 0.01) + dataframe['last_price'] = dataframe['close'] + dataframe['first_price'] = dataframe['close'] + # dataframe['mid_price'] = (dataframe['last_price'] + dataframe['first_price']) / 2 + # dataframe['close01'] = dataframe.iloc[-1]['close'] * 1.01 + # dataframe['limit'] = dataframe['close'] + count_buys = 0 + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + self.getOpenTrades() + + for trade in self.trades: + if trade.pair != pair: + continue + filled_buys = trade.select_filled_orders('buy') + count = 0 + amount = 0 + for buy in filled_buys: + if count == 0: + dataframe['first_price'] = buy.price + self.pairs[pair]['first_buy'] = buy.price + # dataframe['close01'] = buy.price * 1.01 + + # Order(id=2396, trade=1019, order_id=29870026652, side=buy, filled=0.00078, price=63921.01, + # status=closed, date=2024-08-26 02:20:11) + dataframe['last_price'] = buy.price + self.pairs[pair]['last_buy'] = buy.price + count = count + 1 + amount += buy.price * buy.filled + # dataframe['mid_price'] = (dataframe['last_price'] + dataframe['first_price']) / 2 + count_buys = count + # dataframe['limit'] = dataframe['last_price'] * (1 - self.baisse[count] / 100) + # dataframe['amount'] = amount + + # Compter les baisses / hausses consécutives + self.calculateDownAndUp(dataframe, limit=0.0001) + + horizon_h = 12 + + dataframe['close_smooth'] = self.conditional_smoothing(dataframe['mid'].rolling(3).mean().dropna(), + threshold=0.001) + dataframe['smooth'], dataframe['deriv1'], dataframe['deriv2'] = self.smooth_and_derivatives( + dataframe['close_smooth']) + dataframe['deriv1'] = 100 * dataframe['deriv1'] / dataframe['mid'] + dataframe['deriv2'] = 100 * dataframe['deriv2'] / dataframe['mid'] + + # =============================== + # Lissage des valeurs Journalières + horizon_d = 12 * 5 * 24 + dataframe['ema_volume'] = 20 * (dataframe['volume'] * dataframe['percent']) / ( + abs(dataframe['volume'].shift(1)) + abs(dataframe['volume'].shift(2))) + + self.calculeDerivees(dataframe, 'ema_volume', factor_1=10, factor_2=1, horizon=14) + + return dataframe + + def calculeDerivees(self, dataframe, indic, factor_1=100, factor_2=10, horizon=5): + dataframe[f"{indic}_deriv1"] = (factor_1 * dataframe[f"{indic}"].diff() / dataframe[f"{indic}"]).rolling(horizon).mean() + dataframe[f"{indic}_deriv2"] = (factor_2 * dataframe[f"{indic}_deriv1"].diff()).rolling(horizon).mean() + + def calculateDownAndUp(self, dataframe, limit=0.0001): + dataframe['down'] = dataframe['mid_smooth_12_deriv1'] < limit # dataframe['hapercent'] <= limit + dataframe['up'] = dataframe['mid_smooth_12_deriv1'] > limit # dataframe['hapercent'] >= limit + dataframe['down_count'] = - dataframe['down'].astype(int) * ( + dataframe['down'].groupby((dataframe['down'] != dataframe['down'].shift()).cumsum()).cumcount() + 1) + dataframe['up_count'] = dataframe['up'].astype(int) * ( + dataframe['up'].groupby((dataframe['up'] != dataframe['up'].shift()).cumsum()).cumcount() + 1) + # Créer une colonne vide + dataframe['down_pct'] = self.calculateUpDownPct(dataframe, 'down_count') + dataframe['up_pct'] = self.calculateUpDownPct(dataframe, 'up_count') + + def calculateDerivation(self, dataframe, window=12, suffixe='', factor_1=100, factor_2=10): + dataframe['mid'] = dataframe['haopen'] + (dataframe['haclose'] - dataframe['haopen']) / 2 + # 1. Calcul du lissage par moyenne mobile médiane + dataframe[f"mid_smooth{suffixe}"] = dataframe['haclose'].rolling(window=window).mean() + # 2. Dérivée première = différence entre deux bougies successives + dataframe[f"mid_smooth{suffixe}_deriv1"] = round( + factor_1 * dataframe[f"mid_smooth{suffixe}"].rolling(window=3).mean().diff() / dataframe[ + f"mid_smooth{suffixe}"], 4) + + # 3. Dérivée seconde = différence de la dérivée première + dataframe[f"mid_smooth{suffixe}_deriv2"] = round( + factor_2 * dataframe[f"mid_smooth{suffixe}_deriv1"].rolling(window=3).mean().diff(), 4) + dataframe = self.add_tendency_column(dataframe, "mid_smooth", suffixe) + return dataframe + + def getOpenTrades(self): + # if len(self.trades) == 0: + self.trades = Trade.get_open_trades() + return self.trades + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + pair = metadata['pair'] + + # self.getOpenTrades() + # expected_profit = self.expectedProfit(pair, dataframe.iloc[-1]) + # self.getBinanceOrderBook(pair, dataframe) + + # Calcul de la pente (différence entre deux bougies consécutives) + dataframe['bb_ub_slope'] = dataframe['bb_upperband'].diff() + + # Détection d'une inversion (changement de signe de la pente) + # inversion = ( + # (dataframe['bb_ub_slope'].shift(1).rolling(5).apply( + # lambda x: any(x[i] > 0 and x[i + 1] < 0 for i in range(len(x) - 1)))) == 1 + # ) + # On regarde si la bande supérieure a atteint un maximum il y a k bougies + # lookback = 5 + # inversion = (dataframe['bb_upperband'] == dataframe['bb_upperband'].rolling(lookback).max()) + + # pente de la bb_upperband + dataframe['bb_ub_slope'] = dataframe['bb_upperband5'].diff() + + # évènement "inversion vers le bas" (pente passe de >0 à <=0) sur chaque bougie + cross_down = (dataframe['bb_ub_slope'].shift(1) > 0) & (dataframe['bb_ub_slope'] <= 0) + dataframe['bb_cross_down'] = 10000 * cross_down * dataframe['bb_width'] \ + * (dataframe['bb_lowerband'] - dataframe['bb_lowerband'].shift(1)) / dataframe[ + 'bb_lowerband'] + + # vrai si AU MOINS une inversion a eu lieu dans les 5 bougies *précédentes* (on exclut l'actuelle) + inversion_last5 = cross_down.shift(1).rolling(5, min_periods=1).max().astype(bool) + dataframe['inversion_last5'] = inversion_last5 + + N = 24 # nombre minimum de bougies avant inversion + rise_threshold = 1.0 # % de hausse à ne pas dépasser + + # Calcul de la hausse minimale avant inversion + def compute_rise(idx): + if idx < N: + return 0 + low_before = dataframe['close'].iloc[idx - N:idx].min() # min des N bougies avant inversion + return (dataframe['close'].iloc[idx] / low_before - 1) * 100 + + rise = [compute_rise(i) for i in range(len(dataframe))] + dataframe['rise_before_inversion'] = rise + + # Filtre : inversion sans forte hausse avant + valid_inversion = inversion_last5 & (dataframe['rise_before_inversion'] <= rise_threshold) + + # dataframe.loc[ + # ( + # (dataframe['percent'] > 0) + # & (dataframe['mid_smooth_12_deriv1'] >= dataframe['mid_smooth_12_deriv1'].shift(1)) + # ), ['enter_long', 'enter_tag']] = (1, 'down') + factor = 1.01 + if pair == "BTC/USDT" or pair == "BTC/USDC": + factor = factor / 2 + dataframe.loc[ + ( + # (valid_inversion & inversion_last5 ) + (dataframe['mid_smooth_12'].shift(2) > dataframe['mid_smooth_12'].shift(1)) + & (dataframe['mid_smooth_12'].shift(1) < dataframe['mid_smooth_12']) + # & (dataframe['hapercent'] > 0) + #& (dataframe['max_rsi_12'] < 50) + # & (dataframe['open'] <= dataframe['bb_middleband']) + + ), ['enter_long', 'enter_tag']] = (1, 'smth_12') + + dataframe['test'] = np.where(dataframe['enter_long'] == 1, dataframe['close'] * 1.01, np.nan) + + return dataframe + + def calculateProbabilite2Index(self, df, futur_cols, indic_1, indic_2): + # # Définition des tranches pour les dérivées + # bins_deriv = [-np.inf, -0.05, -0.01, 0.01, 0.05, np.inf] + # labels = ['forte baisse', 'légère baisse', 'neutre', 'légère hausse', 'forte hausse'] + # + # # Ajout des colonnes bin (catégorisation) + # df[f"{indic_1}_bin"] = pd.cut(df['mid_smooth_1h_deriv1'], bins=bins_deriv, labels=labels) + # df[f"{indic_2}_bin"] = pd.cut(df['mid_smooth_12_deriv1'], bins=bins_deriv, labels=labels) + # + # # Colonnes de prix futur à analyser + # futur_cols = ['futur_percent_1h', 'futur_percent_2h', 'futur_percent_3h', 'futur_percent_4h', 'futur_percent_5h'] + # + # # Calcul des moyennes et des effectifs + # grouped = df.groupby([f"{indic_2}_bin", f"{indic_1}_bin"])[futur_cols].agg(['mean', 'count']) + # + # pd.set_option('display.width', 200) # largeur max affichage + # pd.set_option('display.max_columns', None) + pd.set_option('display.max_columns', None) + pd.set_option('display.width', 300) # largeur max affichage + + # nettoyage + # series = df[f"{indic_2}"].dropna() + # unique_vals = df[f"{indic_2}"].nunique() + # print(unique_vals) + # print(df[f"{indic_2}"]) + n = len(self.labels) + + df[f"{indic_1}_bin"], bins_1h = pd.qcut(df[f"{indic_1}"], q=n, labels=self.labels, retbins=True, + duplicates='drop') + df[f"{indic_2}_bin"], bins_1d = pd.qcut(df[f"{indic_2}"], q=n, labels=self.labels, retbins=True, + duplicates='drop') + # Affichage formaté pour code Python + print(f"Bornes des quantiles pour {indic_1} : [{', '.join([f'{b:.4f}' for b in bins_1h])}]") + print(f"Bornes des quantiles pour {indic_2} : [{', '.join([f'{b:.4f}' for b in bins_1d])}]") + # Agrégation + grouped = df.groupby([f"{indic_2}_bin", f"{indic_1}_bin"], observed=True)[futur_cols].agg(['mean', 'count']) + # Affichage + with pd.option_context('display.max_rows', None, 'display.max_columns', None): + print(grouped.round(4)) + # Ajout des probabilités de hausse + for col in futur_cols: + df[f"{col}_is_up"] = df[col] > 0 + + # Calcul de la proba de hausse + proba_up = df.groupby([f"{indic_2}_bin", f"{indic_1}_bin"], observed=True)[f"{col}_is_up"].mean().unstack() + + print(f"\nProbabilité de hausse pour {col} (en %):") + with pd.option_context('display.max_rows', None, 'display.max_columns', None): + print((proba_up * 100).round(1)) + + # Affichage formaté des valeurs comme tableau Python + with pd.option_context('display.max_rows', None, 'display.max_columns', None): + df_formatted = (proba_up * 100).round(1) + + print("data = {") + for index, row in df_formatted.iterrows(): + row_values = ", ".join([f"{val:.1f}" for val in row]) + print(f"'{index}': [{row_values}], ") + print("}") + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['mid_smooth_12_deriv1'] == 0) + # & (dataframe['mid_smooth_12_deriv1'].shift(1) > 0) + # ), ['sell', 'exit_long']] = (1, 'sell_sma5_pct_1h') + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + # ne rien faire si ordre deja en cours + if trade.has_open_orders: + # print("skip open orders") + return None + if (self.wallets.get_available_stake_amount() < 0): # or trade.stake_amount >= max_stake: + return 0 + + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + before_last_candle_12 = dataframe.iloc[-13].squeeze() + before_last_candle_24 = dataframe.iloc[-25].squeeze() + last_candle_3 = dataframe.iloc[-4].squeeze() + last_candle_previous_1h = dataframe.iloc[-13].squeeze() + # prépare les données + current_time = current_time.astimezone(timezone.utc) + open_date = trade.open_date.astimezone(timezone.utc) + dispo = round(self.wallets.get_available_stake_amount()) + hours_since_first_buy = (current_time - trade.open_date_utc).seconds / 3600.0 + days_since_first_buy = (current_time - trade.open_date_utc).days + hours = (current_time - trade.date_last_filled_utc).total_seconds() / 3600.0 + count_of_buys = trade.nr_of_successful_entries + current_time_utc = current_time.astimezone(timezone.utc) + open_date = trade.open_date.astimezone(timezone.utc) + days_since_open = (current_time_utc - open_date).days + pair = trade.pair + + pct_first = 0 + + total_counts = sum( + pair_data['count_of_buys'] for pair_data in self.pairs.values() if not pair in ('BTC/USDT', 'BTC/USDC')) + + if self.pairs[pair]['first_buy']: + pct_first = self.getPctFirstBuy(pair, last_candle) + + pct = 0.012 + if count_of_buys == 1: + pct_max = current_profit + else: + if self.pairs[trade.pair]['last_buy']: + pct_max = self.getPctLastBuy(pair, last_candle) + else: + pct_max = - pct + + if pair in ('BTC/USDT', 'BTC/USDC') or count_of_buys <= 2: + lim = - pct - (count_of_buys * 0.001) + # lim = self.getLimitBuy(pair, last_candle, pct) + # lim = - (0.012 * (1 + round(count_of_buys / 5)) + 0.001 * (count_of_buys - 1)) + # lim = - (0.012 + 0.001 * (count_of_buys - 1) + (0.002 * count_of_buys if count_of_buys > 10 else 0.001 * count_of_buys if count_of_buys > 5 else 0)) + + else: + pct = 0.05 + lim = - pct - (count_of_buys * 0.0025) + # lim = self.getLimitBuy(pair, last_candle, pct) + + if (len(dataframe) < 1): + # print("skip dataframe") + return None + + if not self.should_enter_trade(pair, last_candle, current_time): + return None + + condition = (last_candle['sma5_deriv1'] > 0) # and \ + + limit_buy = 40 + if (count_of_buys < limit_buy) and condition and (pct_max < lim): + try: + + if self.pairs[pair]['has_gain']: + self.pairs[pair]['force_sell'] = True + + last_lost = self.getLastLost(last_candle, pair) + + max_amount = self.config.get('stake_amount') * 2.5 + stake_amount = min(min(max_amount, self.wallets.get_available_stake_amount()), + self.adjust_stake_amount(pair, last_candle) - 10 * pct_first / self.mise_factor_buy.value) # min(200, self.adjust_stake_amount(pair, last_candle) * self.fibo[count_of_buys]) + + if self.wallets.get_available_stake_amount() > stake_amount: + trade_type = last_candle['enter_tag'] if last_candle['enter_long'] == 1 else 'pct48' + self.pairs[trade.pair]['count_of_buys'] += 1 + self.pairs[pair]['total_amount'] += stake_amount + self.log_trade( + last_candle=last_candle, + date=current_time, + action="🟧 Loss -", + dispo=dispo, + pair=trade.pair, + rate=current_rate, + trade_type=trade_type, + profit=round(current_profit * trade.stake_amount, 1), + buys=trade.nr_of_successful_entries + 1, + stake=round(stake_amount, 2) + ) + + self.pairs[trade.pair]['last_buy'] = current_rate + self.pairs[trade.pair]['max_touch'] = last_candle['close'] + self.pairs[trade.pair]['last_candle'] = last_candle + + # df = pd.DataFrame.from_dict(self.pairs, orient='index') + # colonnes_a_exclure = ['last_candle', 'last_trade', 'last_palier_index', 'stop', + # 'trade_info', 'last_date', 'expected_profit', 'last_count_of_buys', 'base_stake_amount', 'stop_buy'] + # df_filtered = df[df['count_of_buys'] > 0].drop(columns=colonnes_a_exclure) + # # df_filtered = df_filtered["first_buy", "last_max", "max_touch", "last_sell","last_buy", 'count_of_buys', 'current_profit'] + # + # print(df_filtered) + + return stake_amount + return None + except Exception as exception: + # print(exception) + return None + + last_lost = self.getLastLost(last_candle, pair) + + if (False and hours > 6 and last_candle['mid_smooth_5_deriv1'] > 0): + try: + stake_amount = self.pairs[pair]['first_amount'] / 4 + if self.wallets.get_available_stake_amount() > stake_amount: + self.pairs[pair]['has_gain'] += 1 + + trade_type = 'Gain +' + self.pairs[trade.pair]['count_of_buys'] += 1 + self.pairs[pair]['total_amount'] += stake_amount + self.log_trade( + last_candle=last_candle, + date=current_time, + action="🟡 Gain +", + dispo=dispo, + pair=trade.pair, + rate=current_rate, + trade_type=str(round(pct_max, 4)), + profit=round(current_profit * trade.stake_amount, 1), + buys=trade.nr_of_successful_entries + 1, + stake=round(stake_amount, 2) + ) + self.pairs[trade.pair]['last_buy'] = current_rate + self.pairs[trade.pair]['max_touch'] = last_candle['close'] + self.pairs[trade.pair]['last_candle'] = last_candle + return stake_amount + return None + except Exception as exception: + # print(exception) + return None + + return None + + def getPctFirstBuy(self, pair, last_candle): + return round((last_candle['close'] - self.pairs[pair]['first_buy']) / self.pairs[pair]['first_buy'], 3) + + def getPctLastBuy(self, pair, last_candle): + return round((last_candle['close'] - self.pairs[pair]['last_buy']) / self.pairs[pair]['last_buy'], 4) + + def getPct60D(self, pair, last_candle): + return round((last_candle['max60'] - last_candle['min60']) / last_candle['max60'], 4) + + def getPctClose60D(self, pair, last_candle): + if last_candle['close'] > last_candle['max12']: + return 1 + if last_candle['close'] < last_candle['min12']: + return 0 + return round( + (last_candle['close'] - last_candle['min12']) / (last_candle['max12'] - last_candle['min12']), 4) + + def getLimitBuy(self, pair, last_candle, first_pct): + count_of_buys = self.pairs[pair]['count_of_buys'] + pct60 = self.getPct60D(pair, last_candle) # exemple 0.3 pour 30% + if (pct60 < 0.05): + lim = - first_pct - (count_of_buys * 0.001 * 0.05 / 0.05) + else: + # 0.1 + # 0.4 + lim = - first_pct - (count_of_buys * 0.001 * pct60 / 0.05) + + return lim + + def getProbaHausseSma5d(self, last_candle): + value_1 = self.getValuesFromTable(self.sma5_deriv1, last_candle['sma5_deriv1']) + value_2 = self.getValuesFromTable(self.sma5_deriv2, last_candle['sma5_deriv2']) + + val = self.approx_val_from_bins( + matrice=self.sma5_derive1_2_matrice_df, + numeric_matrice=self.sma5_derive1_2_numeric_matrice, + row_label=value_2, + col_label=value_1 + ) + return val + + def adjust_stake_amount(self, pair: str, last_candle: DataFrame): + # Calculer le minimum des 14 derniers jours + base_stake_amount = self.config.get('stake_amount') # Montant de base configuré + # pct60 = round(100 * self.getPctClose60D(pair, last_candle), 2) + + if True: # not pair in ('BTC/USDT', 'BTC/USDC'): + # factors = [1, 1.2, 1.3, 1.4] + if self.pairs[pair]['count_of_buys'] == 0: + # pctClose60 = self.getPctClose60D(pair, last_candle) + dist_max = self.getDistMax(last_candle, pair) + factor = self.multi_step_interpolate(dist_max, self.thresholds, self.factors) + + adjusted_stake_amount = max(base_stake_amount / 5, base_stake_amount * factor) + else: + adjusted_stake_amount = self.pairs[pair]['first_amount'] + else: + first_price = self.pairs[pair]['first_buy'] + if (first_price == 0): + first_price = last_candle['close'] + + last_max = last_candle['max12'] + pct = 5 + if last_max > 0: + pct = 100 * (last_max - first_price) / last_max + + factor = self.multi_step_interpolate(pct, self.thresholds, self.factors) + adjusted_stake_amount = base_stake_amount * factor # max(base_stake_amount, min(100, base_stake_amount * percent_4)) + + # pct = 100 * abs(self.getPctFirstBuy(pair, last_candle)) + # + # factor = self.multi_step_interpolate(pct, self.thresholds, self.factors) + + if self.pairs[pair]['count_of_buys'] == 0: + self.pairs[pair]['first_amount'] = adjusted_stake_amount + + return adjusted_stake_amount + + def expectedProfit(self, pair: str, last_candle: DataFrame): + lim = 0.01 + pct = 0.002 + if pair == "BTC/USDT" or pair == "BTC/USDC": + lim = 0.005 + pct = 0.001 + pct_to_max = lim + pct * self.pairs[pair]['count_of_buys'] + # if self.pairs[pair]['count_of_buys'] > 6: + # pct_to_max = 0.006 * self.pairs[pair]['count_of_buys'] + # pctClose60 = self.getPctClose60D(pair, last_candle) + + # max_60 = last_candle['max60'] + # if last_candle['close'] < max_60: + # pct_to_max = 0.25 * (max_60 - last_candle['close']) / max_60 + # pct_to_max = pct_to_max * (2 - pctClose60) + expected_profit = lim * self.pairs[pair]['total_amount'] #min(3 * lim, max(lim, pct_to_max)) # 0.004 + 0.002 * self.pairs[pair]['count_of_buys'] #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}") + return expected_profit + + def calculateUpDownPct(self, dataframe, key): + down_pct_values = np.full(len(dataframe), np.nan) + # Remplir la colonne avec les bons calculs + for i in range(len(dataframe)): + shift_value = abs(int(dataframe[key].iloc[i])) # Récupérer le shift actuel + if i - shift_value > 1: # Vérifier que le shift ne dépasse pas l'index + down_pct_values[i] = 100 * (dataframe['close'].iloc[i] - dataframe['close'].iloc[i - shift_value]) / \ + dataframe['close'].iloc[i - shift_value] + return down_pct_values + + # ✅ 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 calculateRegression(self, + dataframe: DataFrame, + column='close', + window=50, + degree=3, + future_offset: int = 10 # projection à n bougies après + ) -> DataFrame: + df = dataframe.copy() + + regression_fit = [] + regression_future_fit = [] + + regression_fit = [] + regression_future_fit = [] + + for i in range(len(df)): + if i < window: + regression_fit.append(np.nan) + regression_future_fit.append(np.nan) + continue + + # Fin de la fenêtre d’apprentissage + end_index = i + start_index = i - window + y = df[column].iloc[start_index:end_index].values + + # Si les données sont insuffisantes (juste par précaution) + if len(y) < window: + regression_fit.append(np.nan) + regression_future_fit.append(np.nan) + continue + + # x centré pour meilleure stabilité numérique + x = np.linspace(-1, 1, window) + coeffs = np.polyfit(x, y, degree) + poly = np.poly1d(coeffs) + + # Calcul point présent (dernier de la fenêtre) + x_now = x[-1] + regression_fit.append(poly(x_now)) + + # Calcul point futur, en ajustant si on dépasse la fin + remaining = len(df) - i - 1 + effective_offset = min(future_offset, remaining) + x_future = x_now + (effective_offset / window) * 2 # respect du même pas + regression_future_fit.append(poly(x_future)) + + df[f"{column}_regression"] = regression_fit + # 2. Dérivée première = différence entre deux bougies successives + df[f"{column}_regression_deriv1"] = round(100 * df[f"{column}_regression"].diff() / df[f"{column}_regression"], + 4) + + # 3. Dérivée seconde = différence de la dérivée première + df[f"{column}_regression_deriv2"] = round( + 10 * df[f"{column}_regression_deriv1"].rolling(int(window / 4)).mean().diff(), 4) + + df[f"{column}_future_{future_offset}"] = regression_future_fit + + # # 2. Dérivée première = différence entre deux bougies successives + # df[f"{column}_future_{future_offset}_deriv1"] = round(100 * df[f"{column}_future_{future_offset}"].diff() / df[f"{column}_future_{future_offset}"], 4) + # + # # 3. Dérivée seconde = différence de la dérivée première + # df[f"{column}_future_{future_offset}_deriv2"] = round(10 * df[f"{column}_future_{future_offset}_deriv1"].rolling(int(window / 4)).mean().diff(), 4) + + return df + + def getValuesFromTable(self, values, value): + for i in range(len(values) - 1): + if values[i] <= value < values[i + 1]: + return self.labels[i] + return self.labels[-1] # cas limite pour la borne max + + def approx_val_from_bins(self, matrice, numeric_matrice, row_label, col_label): + """ + Renvoie une approximation de la valeur à partir des labels binaires (e.g. B5, H1) + en utilisant une interpolation simple basée sur les indices. + + Parameters: + matrix_df (pd.DataFrame): Matrice avec les labels binaires en index et colonnes. + row_label (str): Label de la ligne (ex: 'B3'). + col_label (str): Label de la colonne (ex: 'H2'). + + Returns: + float: Valeur approchée si possible, sinon NaN. + """ + + # Vérification des labels + if row_label not in matrice.index or col_label not in matrice.columns: + return np.nan + + # Index correspondant + row_idx = self.label_to_index.get(row_label) + col_idx = self.label_to_index.get(col_label) + + # Approximation directe (aucune interpolation complexe ici, juste une lecture) + return numeric_matrice[row_idx, col_idx] + + @property + def protections(self): + return [ + # { + # "method": "CooldownPeriod", + # "stop_duration_candles": 12 + # } + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # }, + # { + # "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 conditional_smoothing(self, series, threshold=0.002): + smoothed = [series.iloc[0]] + for val in series.iloc[1:]: + last = smoothed[-1] + if abs(val - last) / last >= threshold: + smoothed.append(val) + else: + smoothed.append(last) + return pd.Series(smoothed, index=series.index) + + def smooth_and_derivatives(self, series, window=25, polyorder=3): + series = series.copy() + if series.isna().sum() > 0: + series = series.ffill().bfill() # Si tu veux éviter toute NaN + + smooth = self.causal_savgol(series, window=window, polyorder=polyorder) + deriv1 = np.diff(smooth, prepend=smooth[0]) + deriv2 = np.diff(deriv1, prepend=deriv1[0]) + + return pd.Series(smooth, index=series.index), pd.Series(deriv1, index=series.index), pd.Series(deriv2, + index=series.index) + + def causal_savgol(self, series, window=25, polyorder=3): + result = [] + half_window = window # Fenêtre complète dans le passé + for i in range(len(series)): + if i < half_window: + result.append(np.nan) + continue + window_series = series[i - half_window:i] + if window_series.isna().any(): + result.append(np.nan) + continue + coeffs = np.polyfit(range(window), window_series, polyorder) + poly = np.poly1d(coeffs) + result.append(poly(window - 1)) + return pd.Series(result, index=series.index) + + def polynomial_forecast(self, series: pd.Series, window: int = 20, degree: int = 2, steps=[12, 24, 36]): + """ + Calcule une régression polynomiale sur les `window` dernières valeurs de la série, + puis prédit les `n_future` prochaines valeurs. + + :param series: Série pandas (ex: dataframe['close']) + :param window: Nombre de valeurs récentes utilisées pour ajuster le polynôme + :param degree: Degré du polynôme (ex: 2 pour quadratique) + :param n_future: Nombre de valeurs futures à prédire + :return: tuple (poly_function, x_vals, y_pred), où y_pred contient les prédictions futures + """ + if len(series) < window: + raise ValueError("La série est trop courte pour la fenêtre spécifiée.") + + recent_y = series.iloc[-window:].values + x = np.arange(window) + + coeffs = np.polyfit(x, recent_y, degree) + poly = np.poly1d(coeffs) + + x_future = np.arange(window, window + len(steps)) + y_future = poly(x_future) + + # Affichage de la fonction + # print("Fonction polynomiale trouvée :") + # print(poly) + + current = series.iloc[-1] + count = 0 + for future_step in steps: # range(1, n_future + 1) + future_x = window - 1 + future_step + prediction = poly(future_x) + # series.loc[series.index[future_x], f'poly_pred_t+{future_step}'] = prediction + + # ➕ Afficher les prédictions + # print(f"{current} → t+{future_step}: x={future_x}, y={prediction:.2f}") + if prediction > 0: # current: + count += 1 + + return poly, x_future, y_future, count + + def calculateStats(self, df, index, target): + # Nombre de tranches (modifiable) + n_bins_indice = 11 + n_bins_valeur = 11 + + # Créer les tranches dynamiques + df['indice_tranche'] = pd.qcut(df[index], q=n_bins_indice, duplicates='drop') + df['valeur_tranche'] = pd.qcut(df[target], q=n_bins_valeur, duplicates='drop') + + # Créer un tableau croisé avec la moyenne des valeurs + pivot_mean = df.pivot_table( + index='indice_tranche', + columns='valeur_tranche', + values=target, # <-- c'est la colonne qu'on agrège + aggfunc='mean' # <-- on calcule la moyenne + ) + + # Résultat + # print("Moyenne des valeurs par double-tranche :") + # print(pivot_mean.round(2)) + + def should_enter_trade(self, pair: str, last_candle, current_time) -> bool: + + return True + limit = 3 + + # 🟢 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. + + # if not pair.startswith('BTC'): + dispo = round(self.wallets.get_available_stake_amount()) + + if self.pairs[pair]['stop'] and last_candle['mid_smooth_5_deriv1'] > -0.9 and last_candle['sma5_deriv1'] > 0 and last_candle['sma5_deriv2'] > 0: + self.pairs[pair]['stop'] = False + self.log_trade( + last_candle=last_candle, + date=current_time, + action="🟢RESTART", + dispo=dispo, + pair=pair, + rate=last_candle['close'], + trade_type='', + profit=0, + buys=self.pairs[pair]['count_of_buys'], + stake=0 + ) + else: + if self.pairs[pair]['stop'] == False and (last_candle['sma5_deriv1'] < -0.2 or last_candle['sma5_deriv2'] < -3): + self.pairs[pair]['stop'] = True + # if self.pairs[pair]['current_profit'] > 0: + # self.pairs[pair]['force_sell'] = True + self.log_trade( + last_candle=last_candle, + date=current_time, + action="🔴STOP", + dispo=dispo, + pair=pair, + rate=last_candle['close'], + trade_type='', + profit=self.pairs[pair]['current_profit'], + buys=self.pairs[pair]['count_of_buys'], + stake=0 + ) + return False + if self.pairs[pair]['stop']: + return False + + # if pair.startswith('BTC'): + # return True # BTC toujours autorisé + + #return True + + # Filtrer les paires non-BTC + non_btc_pairs = [p for p in self.pairs if not p.startswith('BTC')] + + # Compter les positions actives sur les paires non-BTC + max_nb_trades = 0 + total_non_btc = 0 + max_pair = '' + limit_amount = 250 + max_amount = 0 + for p in non_btc_pairs: + max_nb_trades = max(max_nb_trades, self.pairs[p]['count_of_buys']) + max_amount = max(max_amount, self.pairs[p]['total_amount']) + + for p in non_btc_pairs: + if (max_nb_trades == self.pairs[p]['count_of_buys'] and max_nb_trades > limit): + # if (max_amount == self.pairs[p]['total_amount'] and max_amount > limit_amount): + max_pair = p + total_non_btc += self.pairs[p]['count_of_buys'] + + pct_max = self.getPctFirstBuy(pair, last_candle) # self.getPctLastBuy(pair, last_candle) + + val = self.getProbaHausseSma5d(last_candle) + if val < 15: + return False + + return True + self.should_enter_trade_count = 0 + + # if max_pair != pair and self.pairs[pair]['total_amount'] > 300: + # return False + + if (max_pair != '') & (self.pairs[pair]['count_of_buys'] >= limit): + trade = self.pairs[max_pair]['current_trade'] + current_time = current_time.astimezone(timezone.utc) + open_date = trade.open_date.astimezone(timezone.utc) + current_time_utc = current_time.astimezone(timezone.utc) + days_since_open = (current_time_utc - open_date).days + pct_max_max = self.getPctFirstBuy(max_pair, last_candle) + # print(f"days_since_open {days_since_open} max_pair={max_pair} pair={pair}") + return max_pair == pair or pct_max < - 0.25 or ( + pct_max_max < - 0.15 and max_pair != pair and days_since_open > 30) + else: + return True + + def calculePlateaux(self, informative: pd.DataFrame, plateau_duration, plateau_tolerance) -> pd.DataFrame: + + # 1. Détection plateau + informative['rolling_min'] = informative['close'].rolling(plateau_duration).min() + informative['rolling_max'] = informative['close'].rolling(plateau_duration).max() + informative['plateau_amplitude'] = (informative['rolling_max'] - informative['rolling_min']) / informative[ + 'rolling_min'] + informative['plateau'] = informative['plateau_amplitude'] < plateau_tolerance + + # 2. Détection "fin de plateau" + # informative['plateau_end'] = (informative['plateau'] & ~informative['plateau'].shift(-1).fillna(False).astype(bool)) + next_plateau = informative['plateau'].shift(-1) + next_plateau = next_plateau.fillna(False).astype(bool) + informative['plateau_end'] = informative['plateau'] & ~next_plateau + + # 3. Enregistrer dernier plateau (min/max) + last_min = None + last_max = None + last_status = [] + + for i, row in informative.iterrows(): + if row['plateau_end']: + last_min = row['rolling_min'] + last_max = row['rolling_max'] + + if last_min is not None and last_max is not None: + if row['close'] > last_max: + breakout = "up" + distance = (row['close'] - last_max) / last_max + elif row['close'] < last_min: + breakout = "down" + distance = (last_min - row['close']) / last_min + else: + breakout = "inside" + distance = 0 + else: + breakout = None + distance = None + + last_status.append((breakout, distance)) + + informative['breakout_status'] = [s[0] for s in last_status] + informative['breakout_distance'] = [s[1] for s in last_status] + + return informative diff --git a/Zeus_8_3_2_B_4_2_Bilan.txt b/Zeus_8_3_2_B_4_2_Bilan.txt index 128d988..8a6d5e6 100644 --- a/Zeus_8_3_2_B_4_2_Bilan.txt +++ b/Zeus_8_3_2_B_4_2_Bilan.txt @@ -1,168 +1,138 @@ -Result for strategy Zeus_8_3_2_B_4_2 BACKTESTING REPORT -┏━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ -┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ -┡━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ -│ DOGE/USDT │ 28 │ 2.74 │ 126.247 │ 4.21 │ 6 days, 4:49:00 │ 28 0 0 100 │ -│ XRP/USDT │ 41 │ 1.76 │ 112.308 │ 3.74 │ 4 days, 6:33:00 │ 41 0 0 100 │ -│ SOL/USDT │ 27 │ 2.01 │ 109.775 │ 3.66 │ 6 days, 18:14:00 │ 26 0 1 96.3 │ -│ ETH/USDT │ 25 │ 1.74 │ 74.742 │ 2.49 │ 7 days, 13:21:00 │ 24 0 1 96.0 │ -│ BTC/USDT │ 17 │ 1.5 │ 41.111 │ 1.37 │ 11 days, 4:26:00 │ 17 0 0 100 │ -│ TOTAL │ 138 │ 1.97 │ 464.183 │ 15.47 │ 6 days, 14:20:00 │ 136 0 2 98.6 │ -└───────────┴────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┘ - LEFT OPEN TRADES REPORT -┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ -┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ -┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ -│ BTC/USDT │ 1 │ 0.86 │ 0.852 │ 0.03 │ 2 days, 16:20:00 │ 1 0 0 100 │ -│ XRP/USDT │ 1 │ 0.49 │ 0.245 │ 0.01 │ 1:20:00 │ 1 0 0 100 │ -│ ETH/USDT │ 1 │ -0.45 │ -0.454 │ -0.02 │ 4:50:00 │ 0 0 1 0 │ -│ SOL/USDT │ 1 │ -0.68 │ -1.084 │ -0.04 │ 2 days, 7:00:00 │ 0 0 1 0 │ -│ TOTAL │ 4 │ 0.06 │ -0.440 │ -0.01 │ 1 day, 7:22:00 │ 2 0 2 50.0 │ -└──────────┴────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┘ +┏━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ +┡━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ +│ XRP/USDT │ 15 │ 7.15 │ 178.814 │ 5.96 │ 12 days, 6:08:00 │ 15 0 0 100 │ +│ ETH/USDT │ 9 │ 5.13 │ 99.378 │ 3.31 │ 21 days, 1:27:00 │ 9 0 0 100 │ +│ DOGE/USDT │ 7 │ 5.22 │ 89.752 │ 2.99 │ 27 days, 7:43:00 │ 6 0 1 85.7 │ +│ BTC/USDT │ 15 │ 3.35 │ 54.723 │ 1.82 │ 12 days, 8:52:00 │ 15 0 0 100 │ +│ SOL/USDT │ 5 │ 5.95 │ -74.167 │ -2.47 │ 38 days, 12:00:00 │ 4 0 1 80.0 │ +│ TOTAL │ 51 │ 5.29 │ 348.501 │ 11.62 │ 18 days, 11:36:00 │ 49 0 2 96.1 │ +└───────────┴────────┴──────────────┴─────────────────┴──────────────┴───────────────────┴────────────────────────┘ + LEFT OPEN TRADES REPORT +┏━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ +┡━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ +│ ETH/USDT │ 1 │ 5.62 │ 2.812 │ 0.09 │ 3 days, 7:00:00 │ 1 0 0 100 │ +│ XRP/USDT │ 1 │ 2.24 │ 1.118 │ 0.04 │ 20:00:00 │ 1 0 0 100 │ +│ BTC/USDT │ 1 │ 1.04 │ 0.517 │ 0.02 │ 21:00:00 │ 1 0 0 100 │ +│ DOGE/USDT │ 1 │ -0.09 │ -0.047 │ -0.0 │ 22:00:00 │ 0 0 1 0 │ +│ SOL/USDT │ 1 │ -10.67 │ -114.313 │ -3.81 │ 175 days, 3:00:00 │ 0 0 1 0 │ +│ TOTAL │ 5 │ -0.37 │ -109.914 │ -3.66 │ 36 days, 5:00:00 │ 3 0 2 60.0 │ +└───────────┴────────┴──────────────┴─────────────────┴──────────────┴───────────────────┴────────────────────────┘ ENTER TAG STATS -┏━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ -┃ Enter Tag ┃ Entries ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ -┡━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ -│ smth │ 138 │ 1.97 │ 464.183 │ 15.47 │ 6 days, 14:20:00 │ 136 0 2 98.6 │ -│ TOTAL │ 138 │ 1.97 │ 464.183 │ 15.47 │ 6 days, 14:20:00 │ 136 0 2 98.6 │ -└───────────┴─────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┘ - EXIT REASON STATS -┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ -┃ Exit Reason ┃ Exits ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ -┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ -│ Baisse_SOL_36_28 │ 1 │ 2.95 │ 42.485 │ 1.42 │ 104 days, 22:20:00 │ 1 0 0 100 │ -│ Baisse_DOGE_3_1 │ 5 │ 3.34 │ 25.903 │ 0.86 │ 20:16:00 │ 5 0 0 100 │ -│ Baisse_XRP_3_2 │ 4 │ 5.85 │ 25.098 │ 0.84 │ 1 day, 18:08:00 │ 4 0 0 100 │ -│ Baisse_ETH_5_2 │ 2 │ 4.37 │ 24.899 │ 0.83 │ 1 day, 10:08:00 │ 2 0 0 100 │ -│ Baisse_SOL_3_1 │ 7 │ 1.9 │ 22.001 │ 0.73 │ 2 days, 23:57:00 │ 7 0 0 100 │ -│ Baisse_XRP_1_0 │ 10 │ 2.43 │ 19.997 │ 0.67 │ 8:32:00 │ 10 0 0 100 │ -│ Baisse_ETH_3_0 │ 5 │ 1.72 │ 19.657 │ 0.66 │ 8 days, 14:04:00 │ 5 0 0 100 │ -│ Baisse_SOL_1_0 │ 6 │ 3.46 │ 17.915 │ 0.6 │ 17:56:00 │ 6 0 0 100 │ -│ Baisse_DOGE_1_0 │ 7 │ 2.92 │ 15.755 │ 0.53 │ 8:17:00 │ 7 0 0 100 │ -│ Baisse_DOGE_2_0 │ 4 │ 1.67 │ 15.404 │ 0.51 │ 1 day, 3:48:00 │ 4 0 0 100 │ -│ Baisse_DOGE_12_8 │ 1 │ 2.08 │ 14.792 │ 0.49 │ 39 days, 4:20:00 │ 1 0 0 100 │ -│ Baisse_DOGE_3_2 │ 2 │ 5.38 │ 13.595 │ 0.45 │ 1 day, 4:30:00 │ 2 0 0 100 │ -│ Baisse_XRP_5_1 │ 1 │ 2.12 │ 13.134 │ 0.44 │ 8 days, 6:10:00 │ 1 0 0 100 │ -│ Baisse_BTC_3_2 │ 4 │ 1.9 │ 11.925 │ 0.4 │ 2 days, 6:35:00 │ 4 0 0 100 │ -│ Baisse_DOGE_3_0 │ 3 │ 1.15 │ 11.775 │ 0.39 │ 5 days, 13:33:00 │ 3 0 0 100 │ -│ Baisse_XRP_4_0 │ 1 │ 2.51 │ 10.429 │ 0.35 │ 32 days, 22:15:00 │ 1 0 0 100 │ -│ Baisse_DOGE_2_1 │ 2 │ 5.13 │ 10.297 │ 0.34 │ 1 day, 1:42:00 │ 2 0 0 100 │ -│ Baisse_BTC_5_4 │ 1 │ 4.43 │ 9.877 │ 0.33 │ 44 days, 14:30:00 │ 1 0 0 100 │ -│ Baisse_DOGE_5_2 │ 1 │ 2.82 │ 8.908 │ 0.3 │ 9 days, 5:45:00 │ 1 0 0 100 │ -│ Baisse_SOL_5_3 │ 1 │ 2.79 │ 7.369 │ 0.25 │ 1 day, 6:00:00 │ 1 0 0 100 │ -│ Baisse_ETH_3_1 │ 4 │ 1.39 │ 7.322 │ 0.24 │ 1 day, 21:44:00 │ 4 0 0 100 │ -│ Baisse_SOL_2_1 │ 4 │ 1.95 │ 7.253 │ 0.24 │ 13:42:00 │ 4 0 0 100 │ -│ Baisse_XRP_27_22 │ 1 │ 0.77 │ 6.578 │ 0.22 │ 47 days, 10:45:00 │ 1 0 0 100 │ -│ Baisse_SOL_4_0 │ 1 │ 1.88 │ 6.016 │ 0.2 │ 33 days, 19:30:00 │ 1 0 0 100 │ -│ Baisse_XRP_2_1 │ 3 │ 1.06 │ 5.576 │ 0.19 │ 10:20:00 │ 3 0 0 100 │ -│ Baisse_ETH_3_2 │ 2 │ 1.66 │ 5.426 │ 0.18 │ 4 days, 15:40:00 │ 2 0 0 100 │ -│ Baisse_SOL_3_2 │ 3 │ 1.41 │ 4.970 │ 0.17 │ 1 day, 14:13:00 │ 3 0 0 100 │ -│ Baisse_DOGE_4_1 │ 1 │ 2.12 │ 4.963 │ 0.17 │ 1 day, 19:10:00 │ 1 0 0 100 │ -│ Baisse_ETH_2_1 │ 3 │ 1.75 │ 4.814 │ 0.16 │ 1 day, 3:55:00 │ 3 0 0 100 │ -│ Baisse_XRP_2_0 │ 3 │ 0.82 │ 4.478 │ 0.15 │ 1 day, 2:17:00 │ 3 0 0 100 │ -│ Baisse_XRP_6_5 │ 1 │ 2.55 │ 4.450 │ 0.15 │ 1 day, 21:35:00 │ 1 0 0 100 │ -│ Baisse_ETH_1_0 │ 2 │ 3.75 │ 4.356 │ 0.15 │ 22:42:00 │ 2 0 0 100 │ -│ Baisse_DOGE_9_6 │ 1 │ 1.28 │ 3.949 │ 0.13 │ 43 days, 8:55:00 │ 1 0 0 100 │ -│ Baisse_XRP_3_1 │ 3 │ 0.6 │ 3.936 │ 0.13 │ 18:00:00 │ 3 0 0 100 │ -│ Baisse_XRP_4_1 │ 1 │ 1.73 │ 3.930 │ 0.13 │ 1 day, 7:15:00 │ 1 0 0 100 │ -│ Baisse_BTC_3_0 │ 1 │ 1.23 │ 3.787 │ 0.13 │ 6 days, 8:15:00 │ 1 0 0 100 │ -│ Baisse_XRP_3_0 │ 3 │ 0.51 │ 3.455 │ 0.12 │ 1 day, 16:42:00 │ 3 0 0 100 │ -│ Baisse_XRP_4_2 │ 3 │ 0.41 │ 3.377 │ 0.11 │ 1 day, 3:53:00 │ 3 0 0 100 │ -│ Drv3_XRP_1_0 │ 1 │ 2.61 │ 3.300 │ 0.11 │ 4:25:00 │ 1 0 0 100 │ -│ Drv3_BTC_1_0 │ 2 │ 1.63 │ 3.253 │ 0.11 │ 1 day, 6:22:00 │ 2 0 0 100 │ -│ Baisse_BTC_4_3 │ 1 │ 2.38 │ 2.914 │ 0.1 │ 2 days, 7:20:00 │ 1 0 0 100 │ -│ Baisse_ETH_9_6 │ 1 │ 0.93 │ 2.849 │ 0.09 │ 8 days, 6:40:00 │ 1 0 0 100 │ -│ Baisse_XRP_12_10 │ 1 │ 0.75 │ 2.639 │ 0.09 │ 4 days, 21:10:00 │ 1 0 0 100 │ -│ Drv3_ETH_1_0 │ 1 │ 2.64 │ 2.419 │ 0.08 │ 2 days, 5:10:00 │ 1 0 0 100 │ -│ Baisse_BTC_7_5 │ 1 │ 1.03 │ 2.321 │ 0.08 │ 6 days, 9:05:00 │ 1 0 0 100 │ -│ Baisse_BTC_1_0 │ 1 │ 2.03 │ 2.118 │ 0.07 │ 1 day, 21:00:00 │ 1 0 0 100 │ -│ Baisse_SOL_3_0 │ 2 │ 0.46 │ 1.769 │ 0.06 │ 3 days, 4:42:00 │ 2 0 0 100 │ -│ Baisse_ETH_6_4 │ 1 │ 0.81 │ 1.730 │ 0.06 │ 10 days, 18:00:00 │ 1 0 0 100 │ -│ Baisse_XRP_5_2 │ 2 │ 0.21 │ 1.294 │ 0.04 │ 23 days, 21:32:00 │ 2 0 0 100 │ -│ Baisse_BTC_5_3 │ 1 │ 0.54 │ 1.179 │ 0.04 │ 1 day, 2:10:00 │ 1 0 0 100 │ -│ Baisse_SOL_5_2 │ 1 │ 0.42 │ 1.081 │ 0.04 │ 1 day, 7:15:00 │ 1 0 0 100 │ -│ Drv3_BTC_3_1 │ 1 │ 0.85 │ 1.074 │ 0.04 │ 21:25:00 │ 1 0 0 100 │ -│ Baisse_ETH_25_19 │ 1 │ 0.09 │ 0.924 │ 0.03 │ 97 days, 9:25:00 │ 1 0 0 100 │ -│ Baisse_DOGE_33_29 │ 1 │ 0.09 │ 0.905 │ 0.03 │ 47 days, 13:00:00 │ 1 0 0 100 │ -│ Baisse_ETH_4_3 │ 1 │ 0.47 │ 0.730 │ 0.02 │ 1 day, 3:00:00 │ 1 0 0 100 │ -│ Baisse_BTC_8_6 │ 1 │ 0.2 │ 0.627 │ 0.02 │ 107 days, 10:20:00 │ 1 0 0 100 │ -│ Baisse_BTC_6_2 │ 1 │ 0.23 │ 0.618 │ 0.02 │ 3 days, 4:25:00 │ 1 0 0 100 │ -│ Drv3_BTC_2_1 │ 1 │ 0.77 │ 0.565 │ 0.02 │ 1 day, 17:20:00 │ 1 0 0 100 │ -│ Baisse_XRP_5_3 │ 1 │ 0.13 │ 0.221 │ 0.01 │ 1 day, 16:15:00 │ 1 0 0 100 │ -│ Baisse_XRP_8_6 │ 1 │ 0.07 │ 0.169 │ 0.01 │ 2 days, 19:15:00 │ 1 0 0 100 │ -│ Baisse_ETH_2_0 │ 1 │ 0.07 │ 0.071 │ 0.0 │ 20:45:00 │ 1 0 0 100 │ -│ force_exit │ 4 │ 0.06 │ -0.440 │ -0.01 │ 1 day, 7:22:00 │ 2 0 2 50.0 │ -│ TOTAL │ 138 │ 1.97 │ 464.183 │ 15.47 │ 6 days, 14:20:00 │ 136 0 2 98.6 │ -└───────────────────┴───────┴──────────────┴─────────────────┴──────────────┴────────────────────┴────────────────────────┘ - MIXED TAG STATS -┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ -┃ Enter Tag ┃ Exit Reason ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ -┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ -│ ('smth', 'Baisse_SOL_36_28') │ │ 1 │ 2.95 │ 42.485 │ 1.42 │ 104 days, 22:20:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_DOGE_3_1') │ │ 5 │ 3.34 │ 25.903 │ 0.86 │ 20:16:00 │ 5 0 0 100 │ -│ ('smth', 'Baisse_XRP_3_2') │ │ 4 │ 5.85 │ 25.098 │ 0.84 │ 1 day, 18:08:00 │ 4 0 0 100 │ -│ ('smth', 'Baisse_ETH_5_2') │ │ 2 │ 4.37 │ 24.899 │ 0.83 │ 1 day, 10:08:00 │ 2 0 0 100 │ -│ ('smth', 'Baisse_SOL_3_1') │ │ 7 │ 1.9 │ 22.001 │ 0.73 │ 2 days, 23:57:00 │ 7 0 0 100 │ -│ ('smth', 'Baisse_XRP_1_0') │ │ 10 │ 2.43 │ 19.997 │ 0.67 │ 8:32:00 │ 10 0 0 100 │ -│ ('smth', 'Baisse_ETH_3_0') │ │ 5 │ 1.72 │ 19.657 │ 0.66 │ 8 days, 14:04:00 │ 5 0 0 100 │ -│ ('smth', 'Baisse_SOL_1_0') │ │ 6 │ 3.46 │ 17.915 │ 0.6 │ 17:56:00 │ 6 0 0 100 │ -│ ('smth', 'Baisse_DOGE_1_0') │ │ 7 │ 2.92 │ 15.755 │ 0.53 │ 8:17:00 │ 7 0 0 100 │ -│ ('smth', 'Baisse_DOGE_2_0') │ │ 4 │ 1.67 │ 15.404 │ 0.51 │ 1 day, 3:48:00 │ 4 0 0 100 │ -│ ('smth', 'Baisse_DOGE_12_8') │ │ 1 │ 2.08 │ 14.792 │ 0.49 │ 39 days, 4:20:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_DOGE_3_2') │ │ 2 │ 5.38 │ 13.595 │ 0.45 │ 1 day, 4:30:00 │ 2 0 0 100 │ -│ ('smth', 'Baisse_XRP_5_1') │ │ 1 │ 2.12 │ 13.134 │ 0.44 │ 8 days, 6:10:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_BTC_3_2') │ │ 4 │ 1.9 │ 11.925 │ 0.4 │ 2 days, 6:35:00 │ 4 0 0 100 │ -│ ('smth', 'Baisse_DOGE_3_0') │ │ 3 │ 1.15 │ 11.775 │ 0.39 │ 5 days, 13:33:00 │ 3 0 0 100 │ -│ ('smth', 'Baisse_XRP_4_0') │ │ 1 │ 2.51 │ 10.429 │ 0.35 │ 32 days, 22:15:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_DOGE_2_1') │ │ 2 │ 5.13 │ 10.297 │ 0.34 │ 1 day, 1:42:00 │ 2 0 0 100 │ -│ ('smth', 'Baisse_BTC_5_4') │ │ 1 │ 4.43 │ 9.877 │ 0.33 │ 44 days, 14:30:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_DOGE_5_2') │ │ 1 │ 2.82 │ 8.908 │ 0.3 │ 9 days, 5:45:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_SOL_5_3') │ │ 1 │ 2.79 │ 7.369 │ 0.25 │ 1 day, 6:00:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_ETH_3_1') │ │ 4 │ 1.39 │ 7.322 │ 0.24 │ 1 day, 21:44:00 │ 4 0 0 100 │ -│ ('smth', 'Baisse_SOL_2_1') │ │ 4 │ 1.95 │ 7.253 │ 0.24 │ 13:42:00 │ 4 0 0 100 │ -│ ('smth', 'Baisse_XRP_27_22') │ │ 1 │ 0.77 │ 6.578 │ 0.22 │ 47 days, 10:45:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_SOL_4_0') │ │ 1 │ 1.88 │ 6.016 │ 0.2 │ 33 days, 19:30:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_XRP_2_1') │ │ 3 │ 1.06 │ 5.576 │ 0.19 │ 10:20:00 │ 3 0 0 100 │ -│ ('smth', 'Baisse_ETH_3_2') │ │ 2 │ 1.66 │ 5.426 │ 0.18 │ 4 days, 15:40:00 │ 2 0 0 100 │ -│ ('smth', 'Baisse_SOL_3_2') │ │ 3 │ 1.41 │ 4.970 │ 0.17 │ 1 day, 14:13:00 │ 3 0 0 100 │ -│ ('smth', 'Baisse_DOGE_4_1') │ │ 1 │ 2.12 │ 4.963 │ 0.17 │ 1 day, 19:10:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_ETH_2_1') │ │ 3 │ 1.75 │ 4.814 │ 0.16 │ 1 day, 3:55:00 │ 3 0 0 100 │ -│ ('smth', 'Baisse_XRP_2_0') │ │ 3 │ 0.82 │ 4.478 │ 0.15 │ 1 day, 2:17:00 │ 3 0 0 100 │ -│ ('smth', 'Baisse_XRP_6_5') │ │ 1 │ 2.55 │ 4.450 │ 0.15 │ 1 day, 21:35:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_ETH_1_0') │ │ 2 │ 3.75 │ 4.356 │ 0.15 │ 22:42:00 │ 2 0 0 100 │ -│ ('smth', 'Baisse_DOGE_9_6') │ │ 1 │ 1.28 │ 3.949 │ 0.13 │ 43 days, 8:55:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_XRP_3_1') │ │ 3 │ 0.6 │ 3.936 │ 0.13 │ 18:00:00 │ 3 0 0 100 │ -│ ('smth', 'Baisse_XRP_4_1') │ │ 1 │ 1.73 │ 3.930 │ 0.13 │ 1 day, 7:15:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_BTC_3_0') │ │ 1 │ 1.23 │ 3.787 │ 0.13 │ 6 days, 8:15:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_XRP_3_0') │ │ 3 │ 0.51 │ 3.455 │ 0.12 │ 1 day, 16:42:00 │ 3 0 0 100 │ -│ ('smth', 'Baisse_XRP_4_2') │ │ 3 │ 0.41 │ 3.377 │ 0.11 │ 1 day, 3:53:00 │ 3 0 0 100 │ -│ ('smth', 'Drv3_XRP_1_0') │ │ 1 │ 2.61 │ 3.300 │ 0.11 │ 4:25:00 │ 1 0 0 100 │ -│ ('smth', 'Drv3_BTC_1_0') │ │ 2 │ 1.63 │ 3.253 │ 0.11 │ 1 day, 6:22:00 │ 2 0 0 100 │ -│ ('smth', 'Baisse_BTC_4_3') │ │ 1 │ 2.38 │ 2.914 │ 0.1 │ 2 days, 7:20:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_ETH_9_6') │ │ 1 │ 0.93 │ 2.849 │ 0.09 │ 8 days, 6:40:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_XRP_12_10') │ │ 1 │ 0.75 │ 2.639 │ 0.09 │ 4 days, 21:10:00 │ 1 0 0 100 │ -│ ('smth', 'Drv3_ETH_1_0') │ │ 1 │ 2.64 │ 2.419 │ 0.08 │ 2 days, 5:10:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_BTC_7_5') │ │ 1 │ 1.03 │ 2.321 │ 0.08 │ 6 days, 9:05:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_BTC_1_0') │ │ 1 │ 2.03 │ 2.118 │ 0.07 │ 1 day, 21:00:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_SOL_3_0') │ │ 2 │ 0.46 │ 1.769 │ 0.06 │ 3 days, 4:42:00 │ 2 0 0 100 │ -│ ('smth', 'Baisse_ETH_6_4') │ │ 1 │ 0.81 │ 1.730 │ 0.06 │ 10 days, 18:00:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_XRP_5_2') │ │ 2 │ 0.21 │ 1.294 │ 0.04 │ 23 days, 21:32:00 │ 2 0 0 100 │ -│ ('smth', 'Baisse_BTC_5_3') │ │ 1 │ 0.54 │ 1.179 │ 0.04 │ 1 day, 2:10:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_SOL_5_2') │ │ 1 │ 0.42 │ 1.081 │ 0.04 │ 1 day, 7:15:00 │ 1 0 0 100 │ -│ ('smth', 'Drv3_BTC_3_1') │ │ 1 │ 0.85 │ 1.074 │ 0.04 │ 21:25:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_ETH_25_19') │ │ 1 │ 0.09 │ 0.924 │ 0.03 │ 97 days, 9:25:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_DOGE_33_29') │ │ 1 │ 0.09 │ 0.905 │ 0.03 │ 47 days, 13:00:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_ETH_4_3') │ │ 1 │ 0.47 │ 0.730 │ 0.02 │ 1 day, 3:00:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_BTC_8_6') │ │ 1 │ 0.2 │ 0.627 │ 0.02 │ 107 days, 10:20:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_BTC_6_2') │ │ 1 │ 0.23 │ 0.618 │ 0.02 │ 3 days, 4:25:00 │ 1 0 0 100 │ -│ ('smth', 'Drv3_BTC_2_1') │ │ 1 │ 0.77 │ 0.565 │ 0.02 │ 1 day, 17:20:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_XRP_5_3') │ │ 1 │ 0.13 │ 0.221 │ 0.01 │ 1 day, 16:15:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_XRP_8_6') │ │ 1 │ 0.07 │ 0.169 │ 0.01 │ 2 days, 19:15:00 │ 1 0 0 100 │ -│ ('smth', 'Baisse_ETH_2_0') │ │ 1 │ 0.07 │ 0.071 │ 0.0 │ 20:45:00 │ 1 0 0 100 │ -│ ('smth', 'force_exit') │ │ 4 │ 0.06 │ -0.440 │ -0.01 │ 1 day, 7:22:00 │ 2 0 2 50.0 │ -│ TOTAL │ │ 138 │ 1.97 │ 464.183 │ 15.47 │ 6 days, 14:20:00 │ 136 0 2 98.6 │ -└───────────────────────────────┴─────────────┴────────┴──────────────┴─────────────────┴──────────────┴────────────────────┴────────────────────────┘ +┏━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ Enter Tag ┃ Entries ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ +┡━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ +│ smth_12 │ 51 │ 5.29 │ 348.501 │ 11.62 │ 18 days, 11:36:00 │ 49 0 2 96.1 │ +│ TOTAL │ 51 │ 5.29 │ 348.501 │ 11.62 │ 18 days, 11:36:00 │ 49 0 2 96.1 │ +└───────────┴─────────┴──────────────┴─────────────────┴──────────────┴───────────────────┴────────────────────────┘ + EXIT REASON STATS +┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ Exit Reason ┃ Exits ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ +┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ +│ Drv3_XRP_5_0_16.31 │ 1 │ 23.4 │ 73.520 │ 2.45 │ 57 days, 4:00:00 │ 1 0 0 100 │ +│ Drv3_ETH_8_0_38.18 │ 1 │ 7.76 │ 65.196 │ 2.17 │ 126 days, 19:00:00 │ 1 0 0 100 │ +│ Drv3_DOGE_7_0_50.8 │ 1 │ 7.99 │ 48.996 │ 1.63 │ 50 days, 19:00:00 │ 1 0 0 100 │ +│ Drv3_SOL_2_0_22.44 │ 1 │ 27.31 │ 28.009 │ 0.93 │ 3 days, 8:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_2_0_20.51 │ 1 │ 26.94 │ 27.916 │ 0.93 │ 5 days, 10:00:00 │ 1 0 0 100 │ +│ Drv3_DOGE_8_0_62.66 │ 1 │ 2.81 │ 23.933 │ 0.8 │ 122 days, 21:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_2_0_10.47 │ 1 │ 15.01 │ 15.373 │ 0.51 │ 3 days, 2:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_3_0_25.38 │ 1 │ 8.72 │ 14.706 │ 0.49 │ 6 days, 12:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_3_0_33.33 │ 1 │ 6.89 │ 13.860 │ 0.46 │ 50 days, 22:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_9_0_36.61 │ 1 │ 1.75 │ 11.596 │ 0.39 │ 93 days, 18:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_2_0_29.32 │ 1 │ 6.7 │ 9.383 │ 0.31 │ 7 days, 7:00:00 │ 1 0 0 100 │ +│ Drv3_ETH_3_0_27.56 │ 1 │ 5.68 │ 9.199 │ 0.31 │ 13 days, 21:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_2_0_8.99 │ 1 │ 7.84 │ 8.075 │ 0.27 │ 5 days, 16:00:00 │ 1 0 0 100 │ +│ Drv3_DOGE_1_0_24.51 │ 1 │ 12.4 │ 7.724 │ 0.26 │ 2 days, 0:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_4_0_21.52 │ 1 │ 2.86 │ 6.208 │ 0.21 │ 17 days, 21:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_4_0_17.57 │ 1 │ 2.6 │ 6.105 │ 0.2 │ 14 days, 19:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_3_0_43.52 │ 1 │ 3.04 │ 6.100 │ 0.2 │ 17 days, 15:00:00 │ 1 0 0 100 │ +│ Drv3_ETH_4_0_50.43 │ 1 │ 2.2 │ 5.726 │ 0.19 │ 22 days, 16:00:00 │ 1 0 0 100 │ +│ Drv3_SOL_4_0_59.29 │ 1 │ 2.18 │ 5.714 │ 0.19 │ 10 days, 10:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_2_0_32.91 │ 1 │ 5.13 │ 5.261 │ 0.18 │ 11 days, 17:00:00 │ 1 0 0 100 │ +│ Drv3_SOL_1_0_12.73 │ 1 │ 7.72 │ 4.819 │ 0.16 │ 1 day, 10:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_2_0_65.15 │ 1 │ 2.99 │ 4.605 │ 0.15 │ 1 day, 21:00:00 │ 1 0 0 100 │ +│ Drv3_ETH_3_0_59.82 │ 1 │ 2.33 │ 4.554 │ 0.15 │ 13 days, 4:00:00 │ 1 0 0 100 │ +│ Drv3_ETH_1_0_6.25 │ 1 │ 8.94 │ 4.460 │ 0.15 │ 2 days, 10:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_1_0_8.7 │ 1 │ 8.37 │ 4.169 │ 0.14 │ 4 days, 8:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_5_0_95.13 │ 1 │ 1.08 │ 3.727 │ 0.12 │ 18 days, 18:00:00 │ 1 0 0 100 │ +│ Drv3_ETH_1_0_10.0 │ 1 │ 7.12 │ 3.554 │ 0.12 │ 2 days, 13:00:00 │ 1 0 0 100 │ +│ Drv3_DOGE_1_0_61.18 │ 1 │ 6.58 │ 3.290 │ 0.11 │ 3 days, 18:00:00 │ 1 0 0 100 │ +│ Drv3_DOGE_3_0_64.44 │ 1 │ 1.62 │ 3.241 │ 0.11 │ 9 days, 7:00:00 │ 1 0 0 100 │ +│ Drv3_ETH_1_0_30.43 │ 1 │ 5.08 │ 3.177 │ 0.11 │ 2 days, 12:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_1_0_26.32 │ 1 │ 4.54 │ 2.831 │ 0.09 │ 7 days, 3:00:00 │ 1 0 0 100 │ +│ Drv3_DOGE_1_0_31.58 │ 1 │ 5.24 │ 2.615 │ 0.09 │ 1 day, 15:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_2_0_31.43 │ 1 │ 2.35 │ 2.370 │ 0.08 │ 6 days, 0:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_2_0_71.95 │ 1 │ 1.7 │ 2.341 │ 0.08 │ 4 days, 2:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_2_0_84.09 │ 1 │ 1.21 │ 2.085 │ 0.07 │ 2 days, 2:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_1_0_13.04 │ 1 │ 4.04 │ 2.013 │ 0.07 │ 3 days, 13:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_1_0_45.16 │ 1 │ 3.37 │ 1.686 │ 0.06 │ 3 days, 5:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_1_0_54.29 │ 1 │ 4.51 │ 1.651 │ 0.06 │ 3 days, 15:00:00 │ 1 0 0 100 │ +│ Drv3_SOL_1_0_33.33 │ 1 │ 3.21 │ 1.605 │ 0.05 │ 2 days, 5:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_1_0_64.86 │ 1 │ 1.79 │ 1.346 │ 0.04 │ 1 day, 9:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_1_0_18.75 │ 1 │ 2.54 │ 1.271 │ 0.04 │ 1 day, 15:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_1_0_21.43 │ 1 │ 1.84 │ 1.146 │ 0.04 │ 2 days, 0:00:00 │ 1 0 0 100 │ +│ Drv3_XRP_2_0_57.69 │ 1 │ 1.06 │ 1.082 │ 0.04 │ 3 days, 5:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_1_0_50.0 │ 1 │ 1.22 │ 0.758 │ 0.03 │ 10 days, 22:00:00 │ 1 0 0 100 │ +│ Drv3_BTC_2_0_53.33 │ 1 │ 0.71 │ 0.717 │ 0.02 │ 2 days, 3:00:00 │ 1 0 0 100 │ +│ Drv3_ETH_1_0_46.15 │ 1 │ 1.4 │ 0.700 │ 0.02 │ 2 days, 7:00:00 │ 1 0 0 100 │ +│ force_exit │ 5 │ -0.37 │ -109.914 │ -3.66 │ 36 days, 5:00:00 │ 3 0 2 60.0 │ +│ TOTAL │ 51 │ 5.29 │ 348.501 │ 11.62 │ 18 days, 11:36:00 │ 49 0 2 96.1 │ +└─────────────────────┴───────┴──────────────┴─────────────────┴──────────────┴────────────────────┴────────────────────────┘ + MIXED TAG STATS +┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ Enter Tag ┃ Exit Reason ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ +┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ +│ ('smth_12', 'Drv3_XRP_5_0_16.31') │ │ 1 │ 23.4 │ 73.520 │ 2.45 │ 57 days, 4:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_ETH_8_0_38.18') │ │ 1 │ 7.76 │ 65.196 │ 2.17 │ 126 days, 19:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_DOGE_7_0_50.8') │ │ 1 │ 7.99 │ 48.996 │ 1.63 │ 50 days, 19:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_SOL_2_0_22.44') │ │ 1 │ 27.31 │ 28.009 │ 0.93 │ 3 days, 8:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_2_0_20.51') │ │ 1 │ 26.94 │ 27.916 │ 0.93 │ 5 days, 10:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_DOGE_8_0_62.66') │ │ 1 │ 2.81 │ 23.933 │ 0.8 │ 122 days, 21:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_2_0_10.47') │ │ 1 │ 15.01 │ 15.373 │ 0.51 │ 3 days, 2:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_3_0_25.38') │ │ 1 │ 8.72 │ 14.706 │ 0.49 │ 6 days, 12:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_3_0_33.33') │ │ 1 │ 6.89 │ 13.860 │ 0.46 │ 50 days, 22:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_9_0_36.61') │ │ 1 │ 1.75 │ 11.596 │ 0.39 │ 93 days, 18:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_2_0_29.32') │ │ 1 │ 6.7 │ 9.383 │ 0.31 │ 7 days, 7:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_ETH_3_0_27.56') │ │ 1 │ 5.68 │ 9.199 │ 0.31 │ 13 days, 21:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_2_0_8.99') │ │ 1 │ 7.84 │ 8.075 │ 0.27 │ 5 days, 16:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_DOGE_1_0_24.51') │ │ 1 │ 12.4 │ 7.724 │ 0.26 │ 2 days, 0:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_4_0_21.52') │ │ 1 │ 2.86 │ 6.208 │ 0.21 │ 17 days, 21:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_4_0_17.57') │ │ 1 │ 2.6 │ 6.105 │ 0.2 │ 14 days, 19:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_3_0_43.52') │ │ 1 │ 3.04 │ 6.100 │ 0.2 │ 17 days, 15:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_ETH_4_0_50.43') │ │ 1 │ 2.2 │ 5.726 │ 0.19 │ 22 days, 16:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_SOL_4_0_59.29') │ │ 1 │ 2.18 │ 5.714 │ 0.19 │ 10 days, 10:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_2_0_32.91') │ │ 1 │ 5.13 │ 5.261 │ 0.18 │ 11 days, 17:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_SOL_1_0_12.73') │ │ 1 │ 7.72 │ 4.819 │ 0.16 │ 1 day, 10:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_2_0_65.15') │ │ 1 │ 2.99 │ 4.605 │ 0.15 │ 1 day, 21:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_ETH_3_0_59.82') │ │ 1 │ 2.33 │ 4.554 │ 0.15 │ 13 days, 4:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_ETH_1_0_6.25') │ │ 1 │ 8.94 │ 4.460 │ 0.15 │ 2 days, 10:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_1_0_8.7') │ │ 1 │ 8.37 │ 4.169 │ 0.14 │ 4 days, 8:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_5_0_95.13') │ │ 1 │ 1.08 │ 3.727 │ 0.12 │ 18 days, 18:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_ETH_1_0_10.0') │ │ 1 │ 7.12 │ 3.554 │ 0.12 │ 2 days, 13:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_DOGE_1_0_61.18') │ │ 1 │ 6.58 │ 3.290 │ 0.11 │ 3 days, 18:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_DOGE_3_0_64.44') │ │ 1 │ 1.62 │ 3.241 │ 0.11 │ 9 days, 7:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_ETH_1_0_30.43') │ │ 1 │ 5.08 │ 3.177 │ 0.11 │ 2 days, 12:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_1_0_26.32') │ │ 1 │ 4.54 │ 2.831 │ 0.09 │ 7 days, 3:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_DOGE_1_0_31.58') │ │ 1 │ 5.24 │ 2.615 │ 0.09 │ 1 day, 15:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_2_0_31.43') │ │ 1 │ 2.35 │ 2.370 │ 0.08 │ 6 days, 0:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_2_0_71.95') │ │ 1 │ 1.7 │ 2.341 │ 0.08 │ 4 days, 2:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_2_0_84.09') │ │ 1 │ 1.21 │ 2.085 │ 0.07 │ 2 days, 2:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_1_0_13.04') │ │ 1 │ 4.04 │ 2.013 │ 0.07 │ 3 days, 13:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_1_0_45.16') │ │ 1 │ 3.37 │ 1.686 │ 0.06 │ 3 days, 5:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_1_0_54.29') │ │ 1 │ 4.51 │ 1.651 │ 0.06 │ 3 days, 15:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_SOL_1_0_33.33') │ │ 1 │ 3.21 │ 1.605 │ 0.05 │ 2 days, 5:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_1_0_64.86') │ │ 1 │ 1.79 │ 1.346 │ 0.04 │ 1 day, 9:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_1_0_18.75') │ │ 1 │ 2.54 │ 1.271 │ 0.04 │ 1 day, 15:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_1_0_21.43') │ │ 1 │ 1.84 │ 1.146 │ 0.04 │ 2 days, 0:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_XRP_2_0_57.69') │ │ 1 │ 1.06 │ 1.082 │ 0.04 │ 3 days, 5:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_1_0_50.0') │ │ 1 │ 1.22 │ 0.758 │ 0.03 │ 10 days, 22:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_BTC_2_0_53.33') │ │ 1 │ 0.71 │ 0.717 │ 0.02 │ 2 days, 3:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'Drv3_ETH_1_0_46.15') │ │ 1 │ 1.4 │ 0.700 │ 0.02 │ 2 days, 7:00:00 │ 1 0 0 100 │ +│ ('smth_12', 'force_exit') │ │ 5 │ -0.37 │ -109.914 │ -3.66 │ 36 days, 5:00:00 │ 3 0 2 60.0 │ +│ TOTAL │ │ 51 │ 5.29 │ 348.501 │ 11.62 │ 18 days, 11:36:00 │ 49 0 2 96.1 │ +└────────────────────────────────────┴─────────────┴────────┴──────────────┴─────────────────┴──────────────┴────────────────────┴────────────────────────┘ SUMMARY METRICS ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓ ┃ Metric ┃ Value ┃ @@ -172,51 +142,51 @@ Result for strategy Zeus_8_3_2_B_4_2 │ Trading Mode │ Spot │ │ Max open trades │ 5 │ │ │ │ -│ Total/Daily Avg Trades │ 138 / 0.71 │ +│ Total/Daily Avg Trades │ 51 / 0.26 │ │ Starting balance │ 3000 USDT │ -│ Final balance │ 3464.183 USDT │ -│ Absolute profit │ 464.183 USDT │ -│ Total profit % │ 15.47% │ -│ CAGR % │ 31.08% │ -│ Sortino │ 145.12 │ -│ Sharpe │ 9.29 │ -│ Calmar │ 3433.37 │ -│ SQN │ 8.00 │ -│ Profit factor │ 302.81 │ -│ Expectancy (Ratio) │ 3.36 (4.37) │ -│ Avg. daily profit % │ 0.08% │ -│ Avg. stake amount │ 202.763 USDT │ -│ Total trade volume │ 56539.807 USDT │ +│ Final balance │ 3348.501 USDT │ +│ Absolute profit │ 348.501 USDT │ +│ Total profit % │ 11.62% │ +│ CAGR % │ 22.97% │ +│ Sortino │ 0.60 │ +│ Sharpe │ 1.51 │ +│ Calmar │ 34.64 │ +│ SQN │ 2.12 │ +│ Profit factor │ 4.05 │ +│ Expectancy (Ratio) │ 6.83 (0.12) │ +│ Avg. daily profit % │ 0.06% │ +│ Avg. stake amount │ 184.308 USDT │ +│ Total trade volume │ 19186.241 USDT │ │ │ │ -│ Best Pair │ DOGE/USDT 4.21% │ -│ Worst Pair │ BTC/USDT 1.37% │ -│ Best trade │ XRP/USDT 19.20% │ -│ Worst trade │ SOL/USDT -0.68% │ -│ Best day │ 47.239 USDT │ -│ Worst day │ -0.44 USDT │ -│ Days win/draw/lose │ 67 / 127 / 1 │ -│ Avg. Duration Winners │ 6 days, 16:13:00 │ -│ Avg. Duration Loser │ 1 day, 5:55:00 │ -│ Max Consecutive Wins / Loss │ 135 / 2 │ +│ Best Pair │ XRP/USDT 5.96% │ +│ Worst Pair │ SOL/USDT -2.47% │ +│ Best trade │ SOL/USDT 27.31% │ +│ Worst trade │ SOL/USDT -10.67% │ +│ Best day │ 126.686 USDT │ +│ Worst day │ -109.914 USDT │ +│ Days win/draw/lose │ 33 / 160 / 1 │ +│ Avg. Duration Winners │ 15 days, 15:29:00 │ +│ Avg. Duration Loser │ 88 days, 0:30:00 │ +│ Max Consecutive Wins / Loss │ 48 / 1 │ │ Rejected Entry signals │ 0 │ │ Entry/Exit Timeouts │ 0 / 0 │ │ │ │ -│ Min balance │ 3007.222 USDT │ -│ Max balance │ 3465.476 USDT │ -│ Max % of account underwater │ 0.04% │ -│ Absolute Drawdown (Account) │ 0.04% │ -│ Absolute Drawdown │ 1.538 USDT │ -│ Drawdown high │ 465.476 USDT │ -│ Drawdown low │ 463.938 USDT │ +│ Min balance │ 3004.819 USDT │ +│ Max balance │ 3461.743 USDT │ +│ Max % of account underwater │ 3.30% │ +│ Absolute Drawdown (Account) │ 3.30% │ +│ Absolute Drawdown │ 114.313 USDT │ +│ Drawdown high │ 461.743 USDT │ +│ Drawdown low │ 347.43 USDT │ │ Drawdown Start │ 2025-07-14 00:00:00 │ │ Drawdown End │ 2025-07-14 00:00:00 │ -│ Market change │ -0.05% │ +│ Market change │ -17.01% │ └─────────────────────────────┴─────────────────────┘ Backtested 2025-01-01 00:00:00 -> 2025-07-14 00:00:00 | Max open trades : 5 - STRATEGY SUMMARY -┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓ -┃ Strategy ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ Drawdown ┃ -┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩ -│ Zeus_8_3_2_B_4_2 │ 138 │ 1.97 │ 464.183 │ 15.47 │ 6 days, 14:20:00 │ 136 0 2 98.6 │ 1.538 USDT 0.04% │ -└──────────────────┴────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┴───────────────────┘ + STRATEGY SUMMARY +┏━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓ +┃ Strategy ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ Drawdown ┃ +┡━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩ +│ Zeus_8_1d │ 51 │ 5.29 │ 348.501 │ 11.62 │ 18 days, 11:36:00 │ 49 0 2 96.1 │ 114.313 USDT 3.30% │ +└───────────┴────────┴──────────────┴─────────────────┴──────────────┴───────────────────┴────────────────────────┴─────────────────────┘