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 scipy.signal import find_peaks import logging import configparser from technical import pivots_points # -------------------------------- # Add your lib to import here import ta import talib.abstract as talib import freqtrade.vendor.qtpylib.indicators as qtpylib import requests import joblib from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.ensemble import HistGradientBoostingClassifier from sklearn.ensemble import HistGradientBoostingRegressor from sklearn.metrics import accuracy_score from sklearn.metrics import mean_squared_error from sklearn.metrics import mean_absolute_error logger = logging.getLogger(__name__) # Configuration du logger logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', ) from tabulate import tabulate 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 def get_limit_from_config(section, pair): file_path = '/HOME/home/souti/freqtrade2/user_data/strategies/Thor_001.txt' # Créez un objet ConfigParser config = configparser.ConfigParser() try: # Lisez le fichier avec les valeurs config.read(file_path) # Vérifiez si la section existe if config.has_section(section): # Obtenez les valeurs à partir de la section et de la clé (pair) limit = config.get(section, pair) return limit # else: # raise ValueError(f"La section '{section}' n'existe pas dans le fichier de configuration.") except Exception as e: print(f"Erreur lors de la lecture du fichier de configuration : {e}") return None # Order( # id=123456, # pair='BTC/USDT', # order_type='limit', # side='buy', # price=45000.0, # amount=0.1, # filled=0.1, # status='closed', # order_date=datetime(2023, 5, 12, 14, 30, 0), # Date de création de l'ordre # exchange_order_id='abcdef123456', # fee=0.0005, # fee_currency='BTC' # ) class Thor_001(IStrategy): levels = [1, 2, 2, 4, 6, 8] # ROI table: minimal_roi = { "0": 10, # "567": 0.273, # "2814": 0.12, # "7675": 0.05 } min_max_buys = { "BTC/USDT": {"min": 1000000, "max": 0, "profit": 0.01, 'last_rsi_1d': 0, 'last_sell_price': 1000000}, "ETH/USDT": {"min": 1000000, "max": 0, "profit": 0.01, 'last_rsi_1d': 0, 'last_sell_price': 1000000}, "DOGE/USDT": {"min": 1000000, "max": 0, "profit": 0.02, 'last_rsi_1d': 0, 'last_sell_price': 1000000}, "XRP/USDT": {"min": 1000000, "max": 0, "profit": 0.02, 'last_rsi_1d': 0, 'last_sell_price': 1000000}, "SOL/USDT": {"min": 1000000, "max": 0, "profit": 0.02, 'last_rsi_1d': 0, 'last_sell_price': 1000000}, "DASH/USDT": {"min": 1000000, "max": 0, "profit": 0.02, 'last_rsi_1d': 0, 'last_sell_price': 1000000} } last_sell = { "BTC/USDT": 0, "ETH/USDT": 0, "DOGE/USDT": 0, "DASH/USDT": 0, "XRP/USDT": 0, "SOL/USDT": 0 } # Hyperparameters sell_max_threshold = 5.0 # % sous le max où vendre sell_min_threshold = 1.0 close = 'close' open = 'open' # Stoploss: stoploss = -0.10 # 0.256 # Custom stoploss use_custom_stoploss = False # Buy hypers timeframe = '5m' max_open_trades = 5 max_amount = 40 startup_candle_count = 26 # DCA config position_adjustment_enable = True plot_config = { "main_plot": { "min200": { "color": "#86c932" }, "max50": { "color": "white" }, "max200": { "color": "yellow" }, "max_previous_1h": { "color": "#da59a6"}, "min_previous_1h": { "color": "#da59a6", }, "sma5_1h": { "color": "red", }, "close_1d": { "color": "yellow" } }, "subplots": { "Rsi": { "rsi": { "color": "pink" }, "rsi_1h": { "color": "yellow" }, "rsi_sma_1h": { "color": "yellow" } }, "Pct": { "percent": { "color": "blue" }, "percent3": { "color": "green" }, "percent12": { "color": "yellow" }, "percent24": { "color": "pink" }, "sma5_pct_1h": { "color": "red", } } } } # 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 trades = list() max_profit_pairs = {} profit_b_no_change = BooleanParameter(default=True, space="sell") profit_b_quick_lost = BooleanParameter(default=True, space="sell") profit_b_sma5 = BooleanParameter(default=True, space="sell") profit_b_sma10 = BooleanParameter(default=True, space="sell") profit_b_sma20 = BooleanParameter(default=True, space="sell") profit_b_quick_gain = BooleanParameter(default=True, space="sell") profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") profit_b_old_sma10 = BooleanParameter(default=True, space="sell") profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") profit_b_over_rsi = BooleanParameter(default=True, space="sell") profit_b_short_loss = BooleanParameter(default=True, space="sell") sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') sell_b_candels = IntParameter(0, 48, default=12, space='sell') sell_b_too_old_day = IntParameter(0, 10, default=300, space='sell') sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') sell_b_profit_percent12 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') sell_b_RSI = IntParameter(70, 98, default=88, space='sell') sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') protection_percent_buy_lost = IntParameter(1, 10, default=5, space='protection') # protection_nb_buy_lost = IntParameter(1, 2, default=2, space='protection') protection_fibo = IntParameter(1, 10, default=2, space='protection') # trailing stoploss hyperopt parameters # hard stoploss profit pHSL = DecimalParameter(-0.200, -0.040, default=-0.08, decimals=3, space='sell', optimize=False, load=True) # profit threshold 1, trigger point, SL_1 is used pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', optimize=True, load=True) pSL_1 = DecimalParameter(0.008, 0.020, default=0.011, decimals=3, space='sell', optimize=True, load=True) # profit threshold 2, SL_2 is used pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', optimize=True, load=True) pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', optimize=True, load=True) columns_logged = False features = ['rsi_percent', 'rsi_percent3', 'rsi_percent5', 'bb_upperband', 'bb_lowerband', 'close', close, 'percent12', 'percent24', 'tag_min', 'tag_percent24', 'tag_stable', 'tag_buy'] # Charger les modèles def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.model = None def train_model(self, dataframe: DataFrame): # Sélection des colonnes de caractéristiques (features) features = self.features target = 'future_direction' # Supprimer les lignes avec des valeurs NaN # dataframe = dataframe.dropna() # dataframe = dataframe.fillna("Unknown") # Remplace NaN par "Unknown" # Création d'une nouvelle DataFrame contenant uniquement les colonnes spécifiées # dataframe = old_dataframe[self.features] # dataframe['future_direction'] = old_dataframe['future_direction'] # dataframe['future_change'] = old_dataframe['future_change'] # dataframe['rsi_pct_range'] = old_dataframe['rsi_pct_range'] print(f"Shape of dataset: {dataframe.shape}") print(dataframe.head()) dataframe = dataframe.dropna() #dataframe = dataframe.fillna(0) # Remplace NaN par 0 # Diviser les données en train (80%) et test (20%) X = dataframe[features] y = dataframe[target] print(f"Shape of dataset: {dataframe.shape}") print(dataframe.head()) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # Entraîner le modèle de régression logistique model = LogisticRegression(max_iter=5000) model.fit(X_train, y_train) # Évaluer le modèle predictions = model.predict(X_test) accuracy = accuracy_score(y_test, predictions) print(f"Accuracy du modèle : {accuracy * 100:.2f}%") # # HistGradientBoostingClassifier est conçu pour les problèmes de classification, pas pour la régression. # # Si votre problème est une classification (par exemple, prédire une catégorie ou une classe), # # vous pouvez utiliser ce modèle. # # Remplacer LinearRegression par HistGradientBoostingClassifier # # Diviser les données # X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # # model = HistGradientBoostingClassifier() # model.fit(X_train, y_train) # # # Prédictions # y_pred = model.predict(X_test) # accuracy = accuracy_score(y_test, y_pred) # print(f"Accuracy: {accuracy:.2f}") return model def add_future_direction(self, dataframe: DataFrame) -> DataFrame: dataframe['future_change'] = dataframe['close'].shift(-12) - dataframe['close'] dataframe['future_direction'] = dataframe['future_change'].apply(lambda x: 1 if x > 0 else 0) return dataframe def predict_with_model(self, dataframe: DataFrame, model): features = self.features # Supprimer les lignes avec des NaN # Prédire les probabilités #dataframe['prediction_prob'] = model.predict_proba(dataframe[features])[:, 1] #dataframe['predicted_direction'] = dataframe['prediction_prob'].apply(lambda x: 1 if x > 0.5 else 0) # Assurez-vous que dataframe est une copie complète dataframe = dataframe.copy() dataframe = dataframe.dropna() # Prédire les probabilités dataframe.loc[:, 'prediction_prob'] = model.predict_proba(dataframe[features])[:, 1] # Appliquer la direction prédite dataframe.loc[:, 'predicted_direction'] = (dataframe['prediction_prob'] > 0.5).astype(int) return dataframe def color_line(self, action, line): if "INC" in action: # Exemple pour un prix croissant return f"\033[92m{line}\033[0m" # Vert elif "DEC" in action: # Exemple pour un prix décroissant return f"\033[91m{line}\033[0m" # Rouge elif "Allow Sell" in action: # Exemple pour une vente return f"\033[93m{line}\033[0m" # Jaune elif "Allow To buy" in action: # Exemple pour un achat return f"\033[94m{line}\033[0m" # Bleu else: return line # Sans modification def log_trade(self, action, pair, date, trade_type=None, rate=None, dispo=None, profit=None, rsi=None, minutes=None, first_rate=None, last_rate=None, buys=None, stake=None): # Afficher les colonnes une seule fois if not self.columns_logged: print( f"| {'Date':<16} | {'Action':<20} | {'Pair':<10} | {'Trade Type':<18} | {'Rate':>12} | {'Dispo':>6} | {'Profit':>8} | {'RSI':>5} | {'Limit':>10} | {'First Rate':>12} | {'Last Rate':>12} | {'Buys':>5} | {'Stake':>10} |" ) print( f"|{'-' * 18}|{'-' * 22}|{'-' * 12}|{'-' * 20}|{'-' * 14}|{'-' * 8}|{'-' * 10}|{'-' * 7}|{'-' * 12}|{'-' * 14}|{'-' * 14}|{'-' * 7}|{'-' * 12}|" ) self.columns_logged = True 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 = (str(rsi) if rsi else '') + " " + str(rsi - self.min_max_buys[pair]['last_rsi_1d']) #action = self.color_line(action, action) print( f"| {date:<16} | {action:<20} | {pair:<10} | {trade_type or '-':<18} | {rate or '-':>12} | {dispo or '-':>6} | {profit or '-':>8} | {rsi or '-':>5} | {limit or '-':>10} | {first_rate or '-':>12} | {last_rate or '-':>12} | {buys or '-':>5} | {stake or '-':>10} |" ) 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: # count_buys = 0 # trade = self.getTrade(pair) # if trade: # filled_buys = trade.select_filled_orders('buy') # count_buys = len(filled_buys) # print('entry_tag' + str(entry_tag)) dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() # last_candle_288 = dataframe.iloc[-289].squeeze() # limit = get_limit_from_config('Achats', pair) # allow_to_buy = True #(not self.stop_all) #& (not self.all_down) #allow_to_buy = (last_candle['rsi_1d'] > 50) # | (last_candle['rsi_1d'] > last_candle_288['rsi_1d']) # (rate <= float(limit)) | (entry_tag == 'force_entry') allow_to_buy = \ ( ((last_candle['ha_red_1d'] == 0) | (entry_tag == "buy_crash")) # & (last_candle['close'] < self.min_max_buys[pair]['last_sell_price'] * 0.99) #& (last_candle['rsi_1d'] >= 45) ) self.trades = list() dispo = round(self.wallets.get_available_stake_amount()) stake_amount = self.calculate_stake(pair, rate, last_candle) if allow_to_buy: self.log_trade( date=current_time, action="Allow To buy", pair=pair, trade_type=entry_tag, rate=rate, dispo=dispo, profit=0, stake=round(stake_amount, 2), rsi=round(last_candle['rsi_1d']) ) self.min_max_buys[pair]['last_rsi_1d'] = round(last_candle['rsi_1d']) # logger.info(f"Allow_to_buy {allow_to_buy} {pair} {current_time} Buy={entry_tag} rate={rate} dispo={dispo} rsi_1d={rsi_1d}") 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) # limit = get_limit_from_config('Ventes', pair) dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() # & (last_candle['rsi_1d'] <= 50)# rate > float(limit) allow_to_sell = ( last_candle['percent'] < -0.00) | (last_candle['percent3'] < - 0.00) | (40 >= last_candle['rsi'] >= 80 ) #& (last_candle['rsi_1d'] < self.min_max_buys[pair]['last_rsi_1d']) string = "" # allow_to_sell = (last_candle['rsi_1d'] >= self.min_max_buys[pair]['last_rsi_1d']) dispo = round(self.wallets.get_available_stake_amount()) if allow_to_sell: self.trades = list() self.log_trade( date=current_time, action="Allow Sell trade", pair=pair, trade_type=exit_reason, first_rate=trade.open_rate, rate=last_candle['close'], dispo=dispo, profit=round(trade.calc_profit(rate, amount), 2), rsi=round(last_candle['rsi_1d']) ) self.min_max_buys[pair]['last_rsi_1d'] = round(last_candle['rsi_1d']) else: self.log_trade( date=current_time, action="Cancel Sell trade", pair=pair, trade_type=exit_reason, first_rate=trade.open_rate, rate=last_candle['close'], dispo=dispo, #profit=round(trade.calc_profit(rate, amount), 2), rsi=round(last_candle['rsi_1d']) ) ok = (allow_to_sell) | (exit_reason == 'force_exit') if ok: self.min_max_buys[pair]['last_sell_price'] = last_candle['close'] self.last_sell[pair] = rate return ok 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) last_candle = dataframe.iloc[-1].squeeze() # adjusted_stake_amount = self.adjust_stake_amount(pair, current_candle) # adjusted_stake_amount = self.calculate_stake(pair, current_rate) # print(f"Pour {pair}, avec une valeur actuelle de {current_rate}, la mise est de {adjusted_stake_amount:.2f}%") adjusted_stake_amount = self.calculate_stake(pair, current_rate, last_candle) # self.config['stake_amount'] # logger.info(f"{pair} adjusted_stake_amount{adjusted_stake_amount}") # Use default stake amount. return adjusted_stake_amount # def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, # current_profit: float, **kwargs) -> float: # # Récupérer les données pour le trade en cours # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) # last_candle = dataframe.iloc[-1] # # # Heikin-Ashi values # ha_close = last_candle['ha_close_1h'] # ha_open = last_candle['ha_open_1h'] # ha_low = last_candle['ha_low_1h'] # ha_high = last_candle['ha_high_1h'] # # # Calcul de la volatilité et du buffer dynamique # ha_range = abs(ha_close - ha_open) # ha_relative_diff = ha_range / ha_close if ha_close > 0 else 0 # stoploss_buffer = ha_relative_diff * 0.5 # Coefficient ajustable # # # Calcul du stoploss dynamique basé sur Heikin-Ashi # if ha_close > ha_open: # Tendance haussière # stoploss = ha_open * (1 - stoploss_buffer) # else: # Tendance baissière # stoploss = ha_low * (1 - stoploss_buffer) # # # Contrainte de la fourchette [95%, 98%] # stoploss_min = current_rate * 0.97 # stoploss_max = current_rate * 0.985 # stoploss_final = max(stoploss_min, min(stoploss, stoploss_max)) # # return stoploss_final # def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, # current_rate: float, current_profit: float, **kwargs) -> float: # # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) # # last_candle = dataframe.iloc[-1].squeeze() # # # # hard stoploss profit # HSL = self.pHSL.value # PF_1 = self.pPF_1.value # SL_1 = self.pSL_1.value # PF_2 = self.pPF_2.value # SL_2 = self.pSL_2.value # # # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated # # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value # # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. # # # if current_profit > PF_2: # # sl_profit = SL_2 + (current_profit - PF_2) # # elif current_profit > PF_1: # # sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) # # else: # # sl_profit = HSL # # filled_buys = trade.select_filled_orders('buy') # count_of_buys = len(filled_buys) # # first_price = filled_buys[0].price # # days = (current_time - trade.open_date_utc).days # # #print(f"entry_tag={trade.entry_tag} max={trade.max_rate} min={trade.min_rate} ") # if current_profit > (count_of_buys / 0.6) / 100: #0.0125: # sl_profit = 0.6 * current_profit # 75% du profit en cours # else: # sl_profit = self.pHSL.value # Hard stop-loss # # stoploss = stoploss_from_open(sl_profit, current_profit) # # # logger.info(f"{pair} {current_time} stoploss={stoploss:.4f} sl_profit={sl_profit:.4f} current_profit={current_profit:.4f} count_of_buys={count_of_buys}") # return stoploss def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs) -> 'Optional[Union[str, bool]]': dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() previous_last_candle = dataframe.iloc[-2].squeeze() fifth_candle = dataframe.iloc[-6].squeeze() if (last_candle['percent'] > 0) | (last_candle['percent3'] > 0.0): # | (last_candle['max200_diff'] > 0.02): return None # if not (pair in self.max_profit_pairs): # self.max_profit_pairs[pair] = current_profit # self.max_profit_pairs[pair] = max(self.max_profit_pairs[pair], current_profit) # # if (self.max_profit_pairs[pair] > 0.01) & (current_profit > 0): # allow_decrease = 0.2 * self.max_profit_pairs[pair] # if self.max_profit_pairs[pair] - current_profit > allow_decrease: # print("Max profit=" + str(self.max_profit_pairs[pair]) + ' ' + str(current_profit) + ' ' + str( # allow_decrease) + ' ' + str(self.max_profit_pairs[pair] - current_profit)) # return 'max_lost_profit' # hours = (current_time - trade.open_date_utc).seconds / 3600 # # if (hours >= 2) & (self.max_profit_pairs[pair] < 0.002) & (self.max_profit_pairs[pair] > - 0.002): # return "no_change" # if (current_profit < - 0.03 ): # print("Stop loss max profit=" + str(self.max_profit_pairs[pair]) + ' current=' + str(current_profit)) # return 'stop_loss' filled_buys = trade.select_filled_orders('buy') count_of_buys = len(filled_buys) expected_profit = max(self.min_max_buys[pair]['profit'], 0.01 * count_of_buys) # print(last_candle['buy_tag']) days = (current_time - trade.open_date_utc).days hours = (current_time - trade.open_date_utc).seconds / 3600 # fibo_fact = fibo[hours] ###### # if last_candle['percent5'] < -0.05: # return "stop_loss_005" max_percent = 0.005 # self.min_max_buys[pair]['profit'] / 5 # last_candle['bb_width'] / 3.5 # 0.005 max_profit = self.min_max_buys[pair]['profit'] # last_candle['bb_width'] * 3 / 4 # 0.015 # if (current_profit > expected_profit) & \ # (last_candle['open'] > last_candle['max_previous_1h']) & \ # (last_candle['percent'] < 0) \ # & (last_candle[self.close] > last_candle['sma5_1h']): # return 'b_max_previous' # if (current_profit < - 0.03) & \ # (last_candle['rsi_1d'] < 45) & \ # (last_candle['percent'] < 0): # return 'b_rsi_1d' if (current_profit > expected_profit) & \ (last_candle['percent12'] < -max_percent) \ & ((current_time - trade.open_date_utc).seconds >= 3600) \ & (last_candle[self.close] > last_candle['sma5_1h']) \ & (80 > last_candle['rsi_1h'] < 50): return 'b_percent12' # if (current_profit > max_profit) & \ # ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( # last_candle['percent5'] < -max_percent)): # return 'b_percent_quick' if self.profit_b_quick_lost.value and (current_profit >= max_profit) \ & (last_candle['percent3'] < -max_percent) \ & (last_candle[self.close] > last_candle['sma5_1h']) \ & (80 > last_candle['rsi_1h'] < 50): return "b_quick_lost" # if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ # & (last_candle['percent12'] < self.sell_b_profit_percent12.value) & (last_candle['percent5'] < 0) \ # & ((current_time - trade.open_date_utc).seconds >= 3600): # return "b_no_change" if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): return "b_quick_gain_param" if self.profit_b_sma5.value: if (current_profit > expected_profit) \ & ((fifth_candle['sma5'] > last_candle['sma5']) \ | (last_candle['percent3'] < -expected_profit) | ( last_candle['percent5'] < -expected_profit)) \ & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) return 'b_sma5' if self.profit_b_sma10.value: if (current_profit > expected_profit) \ & ((fifth_candle['sma10'] > last_candle['sma10']) \ | (last_candle['percent3'] < -expected_profit) | ( last_candle['percent5'] < -expected_profit)) \ & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) return 'b_sma10' if self.profit_b_sma20.value: if (current_profit > expected_profit) \ & (previous_last_candle['sma10'] > last_candle['sma10']) \ & ((current_time - trade.open_date_utc).seconds >= 3600) \ & ((previous_last_candle['sma20'] > last_candle['sma20']) & ((last_candle['percent5'] < 0) | (last_candle['percent12'] < 0) | ( last_candle['percent24'] < 0))): # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) return 'b_sma20' if self.profit_b_over_rsi.value: if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) return 'b_over_rsi' if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ (last_candle[ 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) return 'b_over_rsi_2' if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ (last_candle[self.close] >= last_candle['max200']) \ & (last_candle[ 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi2'] > 75 & last_candle['rsi'] < 70)): # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) return 'b_over_rsi_max' def informative_pairs(self): # get access to all pairs available in whitelist. pairs = self.dp.current_whitelist() informative_pairs = [(pair, '1d') for pair in pairs] informative_pairs += [(pair, '1h') for pair in pairs] return informative_pairs def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Add all ta features pair = metadata['pair'] # dataframe['achats'] = get_limit_from_config('Achats', pair) # dataframe['ventes'] = get_limit_from_config('Ventes', pair) heikinashi = qtpylib.heikinashi(dataframe) dataframe['enter_long'] = "" dataframe['enter_tag'] = "" dataframe['haopen'] = heikinashi['open'] dataframe['haclose'] = heikinashi['close'] dataframe['hapercent'] = (dataframe['haclose'] - dataframe['haopen']) / dataframe['haclose'] dataframe['mid'] = (dataframe['close'] + dataframe['open']) / 2 dataframe['pct_change'] = dataframe[self.close].pct_change(5) dataframe['min'] = talib.MIN(dataframe[self.close], timeperiod=200) dataframe['min12'] = talib.MIN(dataframe[self.close], timeperiod=12) dataframe['min50'] = talib.MIN(dataframe[self.close], timeperiod=50) dataframe['min200'] = talib.MIN(dataframe[self.close], timeperiod=200) dataframe['max50'] = talib.MAX(dataframe[self.close], timeperiod=50) dataframe['max144'] = talib.MAX(dataframe[self.close], timeperiod=144) dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] dataframe['max200'] = talib.MAX(dataframe[self.close], timeperiod=200) dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] dataframe['max200_diff'] = (dataframe['max200'] - dataframe[self.close]) / dataframe[self.close] dataframe['max50_diff'] = (dataframe['max50'] - dataframe[self.close]) / dataframe[self.close] dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) dataframe["percent"] = (dataframe[self.close] - dataframe[self.open]) / dataframe[self.open] dataframe["percent3"] = (dataframe[self.close] - dataframe[self.open].shift(3)) / dataframe[self.open].shift(3) dataframe["percent5"] = (dataframe[self.close] - dataframe[self.open].shift(5)) / dataframe[self.open].shift(5) dataframe["percent12"] = (dataframe[self.close] - dataframe[self.open].shift(12)) / dataframe[self.open].shift( 12) dataframe["percent24"] = (dataframe[self.close] - dataframe[self.open].shift(24)) / dataframe[self.open].shift( 24) dataframe["percent48"] = (dataframe[self.close] - dataframe[self.open].shift(48)) / dataframe[self.open].shift( 48) dataframe["percent_max_144"] = (dataframe[self.close] - dataframe["max144"]) / dataframe[self.close] # print(metadata['pair']) dataframe['rsi'] = talib.RSI(dataframe[self.close], timeperiod=12) dataframe['rsi24'] = talib.RSI(dataframe[self.close], timeperiod=24) dataframe['rsi48'] = talib.RSI(dataframe[self.close], timeperiod=48) dataframe['volume2'] = dataframe['volume'] dataframe.loc[dataframe['percent'] < 0, 'volume2'] *= -1 # ====================================================================================== # MACD pour identifier les croisements macd = talib.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] # Moyennes mobiles pour les tendances dataframe['ema_50'] = talib.EMA(dataframe, timeperiod=50) dataframe['ema_200'] = talib.EMA(dataframe, timeperiod=200) # ADX pour la force de tendance dataframe['adx'] = talib.ADX(dataframe) # Volume moyen pour filtrer les opportunités à faible volume dataframe['volume_mean_sma'] = talib.SMA(dataframe['volume'], timeperiod=20) # ====================================================================================== # ====================================================================================== # 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[self.close] - dataframe["bb_lowerband"]) / (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) ) # ====================================================================================== # Normalization dataframe['average_line'] = dataframe[self.close].mean() dataframe['average_line_50'] = talib.MIDPOINT(dataframe[self.close], timeperiod=50) dataframe['average_line_288'] = talib.MIDPOINT(dataframe[self.close], timeperiod=288) dataframe['average_line_288_098'] = dataframe['average_line_288'] * 0.98 dataframe['average_line_288_099'] = dataframe['average_line_288'] * 0.99 # Sort the close prices to find the 4 lowest values sorted_close_prices = dataframe[self.close].tail(576).sort_values() lowest_4 = sorted_close_prices.head(20) dataframe['lowest_4_average'] = dataframe['min200'] # lowest_4.mean() # Propagate this mean value across the entire dataframe # dataframe['lowest_4_average'] = dataframe['lowest_4_average'].iloc[0] # # Sort the close prices to find the 4 highest values sorted_close_prices = dataframe[self.close].tail(288).sort_values(ascending=False) highest_4 = sorted_close_prices.head(20) # # Calculate the mean of the 4 highest values dataframe['highest_4_average'] = dataframe['max200'] # highest_4.mean() # # Propagate this mean value across the entire dataframe # dataframe['highest_4_average'] = dataframe['highest_4_average'].iloc[0] dataframe['volatility'] = talib.STDDEV(dataframe[self.close], timeperiod=144) / dataframe[self.close] dataframe['atr'] = talib.ATR(dataframe['high'], dataframe['low'], dataframe[self.close], timeperiod=144) / \ dataframe[self.close] # ====================================================================================== ################### INFORMATIVE 1h informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") informative['sma5'] = talib.SMA(informative, timeperiod=10) informative['sma5_pct'] = (informative["sma5"] - informative["sma5"].shift(6)) / informative["sma5"] informative["max_previous"] = talib.MAX(informative['close'], timeperiod=48) informative["min_previous"] = talib.MIN(informative['close'], timeperiod=48) informative['volatility'] = talib.STDDEV(informative['close'], timeperiod=14) / informative['close'] informative['atr'] = (talib.ATR(informative['high'], informative['low'], informative['close'], timeperiod=14)) / \ informative['close'] informative['rsi'] = talib.RSI(informative['close'], timeperiod=12) informative['rsi_sma'] = talib.SMA(informative['rsi'], timeperiod=10) heikinashi = qtpylib.heikinashi(informative) informative['ha_open'] = heikinashi['open'] informative['ha_close'] = heikinashi['close'] informative['ha_high'] = heikinashi['high'] informative['ha_low'] = heikinashi['low'] dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) # Calcul des seuils dynamiques dataframe['sell_threshold_max'] = dataframe['max_previous_1h'] - ( dataframe['max_previous_1h'] * self.sell_max_threshold / 100) dataframe['sell_threshold_min'] = dataframe['min_previous_1h'] + ( dataframe['min_previous_1h'] * self.sell_min_threshold / 100) # ====================================================================================== ################### INFORMATIVE 1d informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") sorted_close_prices = informative['close'].tail(365).sort_values() lowest_4 = sorted_close_prices.head(4) informative['lowest_4'] = lowest_4.mean() sorted_close_prices = informative['close'].tail(365).sort_values(ascending=False) highest_4 = sorted_close_prices.head(4) informative['highest_4'] = highest_4.mean() last_14_days = informative.tail(14) # Récupérer le minimum et le maximum de la colonne 'close' des 14 derniers jours min_14_days = last_14_days['close'].min() max_14_days = last_14_days['close'].max() informative['lowest'] = min_14_days informative['highest'] = max_14_days informative['pct_min_max'] = (max_14_days - min_14_days) / min_14_days informative['mid_min_max'] = min_14_days + (max_14_days - min_14_days) / 2 informative['middle'] = informative['lowest_4'] + (informative['highest_4'] - informative['lowest_4']) / 2 informative['mid_min_max_0.98'] = informative['mid_min_max'] * 0.98 informative['rsi'] = talib.RSI(informative['close'], timeperiod=12) informative['rsi_sma'] = talib.SMA(informative['rsi'], timeperiod=12) informative["percent"] = (informative[self.close] - informative[self.open]) / informative[self.open] # Heikin Ashi (simplified) heikinashi = qtpylib.heikinashi(informative) informative['ha_open'] = heikinashi['open'] informative['ha_close'] = heikinashi['close'] informative['ha_high'] = heikinashi['high'] informative['ha_low'] = heikinashi['low'] # informative['ha_close'] = (informative['open'] + informative['high'] + informative['low'] + informative['close']) / 4 # informative['ha_open'] = informative['ha_close'].shift(1).combine_first(informative['open']) informative['ha_red'] = informative['ha_close'] < informative['ha_open'] dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) dataframe['count_buys'] = 0 dataframe['last_price'] = dataframe[self.close] dataframe['first_price'] = dataframe[self.close] dataframe['mid_price'] = (dataframe['last_price'] + dataframe['first_price']) / 2 dataframe['close01'] = dataframe.iloc[-1][self.close] * 1.01 dataframe['amount'] = 0 dataframe['limit'] = dataframe[self.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') dataframe['count_buys'] = len(filled_buys) count = 0 amount = 0 for buy in filled_buys: if count == 0: dataframe['first_price'] = 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 print(buy) 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 print(f"amount= {amount}") # trades = Trade.get_trades([Trade.is_open is False]).all() trades = Trade.get_trades_proxy(is_open=False, pair=metadata['pair']) if trades: trade = trades[-1] print('closed trade pair is : ') dataframe['expected_profit'] = (1 + self.expectedProfit(pair, dataframe.iloc[-1])) * dataframe[ 'last_price'] dataframe['buy_level'] = dataframe['lowest_4_average'] * (1 - self.levels[count_buys] / 100) # ====================================================================================================== dataframe['tag_min'] = (dataframe[self.close] <= dataframe['min200'] * 1.002) dataframe['tag_percent24'] = (dataframe['percent24'] <= -self.min_max_buys[pair]['profit']) dataframe['tag_stable'] = (dataframe['min50'].shift(6) == dataframe['min50']) dataframe['tag_buy'] = dataframe['tag_min'].shift(6) & dataframe['tag_percent24'].shift(6) & dataframe[ 'tag_stable'] self.getBinanceOrderBook(pair, dataframe) # ========================================================================= dataframe['rsi_range'] = pd.cut(dataframe['rsi'], bins=[0,10, 20, 30, 40, 50, 60, 70, 80, 90, 100], labels=['00', '10', '20', '30', '40', '50', '60', '70', '80', '90']) dataframe['rsi_percent'] = 100 * (dataframe['rsi'] - dataframe['rsi'].shift(1)) / dataframe['rsi'] dataframe['rsi_percent3'] = 100 * (dataframe['rsi'] - dataframe['rsi'].shift(3)) / dataframe['rsi'] dataframe['rsi_percent5'] = 100 * (dataframe['rsi'] - dataframe['rsi'].shift(5)) / dataframe['rsi'] dataframe['rsi_pct_range'] = pd.cut(dataframe['rsi_percent3'], bins=[-1000, -80, -60, -40, -20, 0, 20, 40, 60, 80, 1000], labels=['-100', '-80', '-60', '-40', '-20', '0', '20', '40', '60', '80']) # Calcul des changements futurs dataframe['future_change'] = dataframe['close'].shift(-12) - dataframe['close'] dataframe['future_direction'] = dataframe['future_change'].apply(lambda x: 1 if x > 0 else 0) rsi_probabilities = dataframe.groupby('rsi_pct_range')['future_direction'].mean() print(f"Probabilités de hausse selon rsi_pct_range :\n", rsi_probabilities) return dataframe def getOpenTrades(self): # if len(self.trades) == 0: print('search open trades') if self.dp: if self.dp.runmode.value in ('live', 'dry_run'): self.trades = Trade.get_open_trades() return self.trades def getTrade(self, pair): trades = self.getOpenTrades() trade_for_pair = None for trade in trades: if trade.pair == pair: trade_for_pair = trade break return trade_for_pair 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) last_candle = dataframe.iloc[-1].squeeze() # limit = last_candle['first_price'] * (1 - self.baisse[last_candle['count_buys']] / 100) # self.updateLastValue(dataframe, 'expected_profit', expected_profit) # print("---------------" + pair + "----------------") # print('adjust stake amount ' + str(self.adjust_stake_amount(pair, dataframe.iloc[-1]))) # print('adjust exit price ' + str(self.adjust_exit_price(dataframe.iloc[-1]))) # print('calcul expected_profit ' + str(expected_profit)) buy_level = dataframe[self.open] # dataframe['buy_level'] # self.get_buy_level(pair, dataframe) dataframe.loc[ ( (dataframe['max200_diff'] >= 0.018) & (dataframe[self.open] < dataframe['average_line_288']) & (dataframe[self.close] < dataframe['min12'] * 1.002) & (dataframe['min12'].shift(2) == dataframe['min12']) ), ['enter_long', 'enter_tag']] = (1, 'buy_min_max200') dataframe.loc[ ( ( (dataframe['percent12'].shift(3) < -0.015) | (dataframe['percent24'].shift(3) < -0.022) | (dataframe['percent48'].shift(3) < -0.030) ) & (dataframe[self.close].shift(3) <= dataframe['min50'].shift(3) * 1.002) & (dataframe[self.open].shift(3) < dataframe['average_line_50'].shift(3)) & ( (dataframe['min200'].shift(3) == dataframe['min200']) ) ), ['enter_long', 'enter_tag']] = (1, 'buy_0_percent12') dataframe.loc[ ( (dataframe['count_buys'] == 0) & ( (dataframe[self.close] <= dataframe['sma5_1h'] * (1 - self.min_max_buys[pair]['profit'])) | (dataframe['percent24'] <= -self.min_max_buys[pair]['profit']) ) & (dataframe[self.close] <= dataframe['sma5_1h']) & ((last_candle['close'] - last_candle['min_previous_1h']) / (last_candle['max_previous_1h'] - last_candle['close']) < 0.2) ), ['enter_long', 'enter_tag']] = (1, 'buy_percent12') # dataframe.loc[ # ( # (dataframe['percent_1d'] <= -0.1) # & (dataframe['min12'].shift(12) == dataframe['min12']) # ), ['enter_long', 'enter_tag']] = (1, 'buy_crash') nan_columns = dataframe.columns[dataframe.isna().any()].tolist() print("Colonnes contenant des NaN :", nan_columns) # Compter les colonnes avec uniquement des NaN num_only_nan_columns = dataframe.isna().all().sum() print(f"Nombre de colonnes contenant uniquement des NaN : {num_only_nan_columns}") # Compter les NaN par colonne nan_count_per_column = dataframe.isna().sum() print("Nombre de NaN par colonne :") print(nan_count_per_column) columns_with_nan = nan_count_per_column[nan_count_per_column > 1000] print("Colonnes avec au moins 1000 NaN :") print(columns_with_nan) #self.model = joblib.load('regression_model.pkl') # Former le modèle si ce n'est pas déjà fait if self.model is None: print("Calculate model") dataframe = self.add_future_direction(dataframe) self.model = self.train_model(dataframe) else: print("Load model") # Coefficients du modèle print("Coefficients :", self.model.coef_) # Ordonnée à l'origine (intercept) print("Ordonnée à l'origine :", self.model.intercept_) # Hyperparamètres du modèle print("Hyperparamètres du modèle :", self.model.get_params()) # Faire des prédictions dataframe = self.predict_with_model(dataframe, self.model) # Sauvegarde du modèle # joblib.dump(self.model, 'regression_model.pkl') # Entrée sur un signal de hausse # dataframe.loc[ # (dataframe['predicted_direction'] == 1), # ['enter_long', 'enter_tag']] = (1, 'buy_predict') return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # dataframe.loc[ # ( # (dataframe['sma5_pct_1h'].shift(24) < dataframe['sma5_pct_1h'].shift(12)) # & (dataframe['sma5_pct_1h'].shift(12) > dataframe['sma5_pct_1h']) # & (dataframe['sma5_pct_1h'] < 0.035) # ), ['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): pair = trade.pair # self.min_max_buys[pair][min] = min(self.min_max_buys[pair][min], current_rate) # self.min_max_buys[pair][max] = max(self.min_max_buys[pair][max], current_rate) dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) # print(dataframe) last_candle = dataframe.iloc[-1].squeeze() last_candle_1 = dataframe.iloc[-2].squeeze() last_candle_3 = dataframe.iloc[-4].squeeze() last_candle_12 = dataframe.iloc[-13].squeeze() last_candle_24 = dataframe.iloc[-25].squeeze() return None # "& (last_candle['sma5_pct_1h'] < -0.035) if (len(dataframe) < 1) | (self.wallets.get_available_stake_amount() < 10): return None if pair not in ('BTC/USDT', 'DOGE/USDT', 'ETH/USDT'): return None if last_candle['rsi_1d'] is None: return None if (last_candle['ha_red_1d'] is None) | (last_candle['ha_red_1d'] == 1): return None max_buys = 10 filled_buys = trade.select_filled_orders('buy') count_of_buys = len(filled_buys) if count_of_buys >= max_buys: return None first_price = filled_buys[0].price days = 0 minutes = 0 last_price = first_price for buy in filled_buys: minutes = (current_time - buy.order_date_utc).seconds / 60 days = (current_time - buy.order_date_utc).days last_price = buy.price condition = (last_candle['sma5_pct_1h'] >= 0) dispo = round(self.wallets.get_available_stake_amount()) profit = ((current_rate - last_price) / current_rate) rsi_1d = last_candle['rsi_1d'] if ( # (last_candle_3['volume'] * 5 < last_candle['volume']) (last_candle['rsi_1h'] > 50) #(last_candle['rsi_1d'] >= self.min_max_buys[pair]['last_rsi_1d']) & (minutes > 180) & (current_rate > max(first_price, last_price) * (1 + count_of_buys / 100)) ): stake_amount = self.calculate_stake(pair, current_rate, last_candle) self.log_trade( date=current_time, action="INC price", pair=trade.pair, rate=current_rate, minutes=minutes, first_rate=first_price, last_rate=last_price, buys=count_of_buys, stake=round(stake_amount, 2), profit=round(profit * 100, 2), dispo=dispo, rsi=round(last_candle['rsi_1d']) ) self.min_max_buys[pair]['last_rsi_1d'] = round(last_candle['rsi_1d']) # logger.info( # f"INC price={trade.pair} {current_time} minutes={minutes} first={first_price:.4f} last={last_price:.4f} rate={current_rate:.4f} buys={count_of_buys} stake={stake_amount:.4f}") return stake_amount # self.protection_nb_buy_lost.value # limit = last_candle[limits[count_of_buys]] limit = last_price * (1 - self.fibo[count_of_buys] / 100) # limit = last_price * (0.99) # stake_amount = min(200, self.adjust_stake_amount(pair, last_candle) * self.fibo[count_of_buys]) # stake_amount = self.config.get('stake_amount', 50) * self.fibo[count_of_buys] stake_amount = self.calculate_stake(pair, current_rate, last_candle) * self.fibo[count_of_buys] percent_max = (last_candle['max200'] - last_candle['close']) / last_candle['close'] if ( False & (last_candle['rsi_1d'] >= self.min_max_buys[pair]['last_rsi_1d']) #(last_candle_1['rsi'] < 12) #(last_price <= first_price) & (current_rate <= limit) & (minutes > 180)): fact = - profit / 0.05 stake_amount = 100 #min(self.wallets.get_available_stake_amount(), max(100, min(666, stake_amount * fact))) self.min_max_buys[pair]['last_rsi_1d'] = round(last_candle['rsi_1d']) self.log_trade( date=current_time, action="DEC price", pair=trade.pair, rate=current_rate, minutes=minutes, first_rate=first_price, last_rate=last_price, buys=count_of_buys, stake=round(stake_amount, 2), profit=round(profit * 100, 2), dispo=dispo, rsi=round(last_candle['rsi_1d']) ) # logger.info( # f"DEC price={trade.pair} {current_time} minutes={minutes} first={first_price:.4f} last={last_price:.4f} rate={current_rate:.4f} buys={count_of_buys} limit={limit:.4f} stake={stake_amount:.4f} rsi={rsi_1d:.4f}") return stake_amount # if days > 1: # self.log_trade( # date=current_time, # action="NO BUY", # pair=trade.pair, # rate=current_rate, # minutes=minutes, # first_rate=first_price, # last_rate=last_price, # buys=count_of_buys, # profit=round(profit * 100, 2), # dispo=dispo, # rsi=round(last_candle['rsi_1d']) # ) return None # print("Adjust " + trade.pair + " time=" + str(current_time) + ' rate=' + str(current_rate) + " buys=" + str(count_of_buys) + " limit=" + str(limit) + " stake=" + str(stake_amount)) # logger.info( # f"Adjust price={trade.pair} buy={condition} rate={current_rate:.4f} buys={count_of_buys} limit={limit:.4f} stake={stake_amount:.4f}") if (0 < count_of_buys <= max_buys) & (current_rate <= limit) & (condition) & (minutes > 180): try: # This then calculates current safety order size # stake_amount = stake_amount * pow(1.5, count_of_buys) # print("Effective Adjust " + trade.pair + " time=" + str(current_time) + ' rate=' + str(current_rate) + " buys=" + str(count_of_buys) + " limit=" + str(limit) + " stake=" + str(stake_amount)) logger.info( f"Adjust price={trade.pair} {current_time} first={first_price:.4f} rate={current_rate:.4f} buys={count_of_buys} limit={limit:.4f} stake={stake_amount:.4f}") return min(self.wallets.get_available_stake_amount(), stake_amount) except Exception as exception: print(exception) return None return None # def adjust_stake_amount(self, pair: str, dataframe: DataFrame): # # Calculer le minimum des 14 derniers jours # current_price = dataframe[self.close] # # v_max = max(current_price, self.min_max_buys[pair]['max']) # v_min = min(current_price, self.min_max_buys[pair]['min']) # # adjusted_stake_amount = self.config['stake_amount'] * v_min / v_max # # #print( # # f"Stack amount ajusté price={current_price} max_min={max_min_4:.4f} min_14={min_14_days_4:.4f} max_14={max_14_days_4:.4f} factor={factor_4:.4f} percent={percent_4:.4f} amount={adjusted_stake_amount:.4f}") # # print(f"Stack amount ajusté price={current_price} max_min={max_min:.4f} min_14={min_14_days:.4f} max_14={max_14_days:.4f} factor={factor:.4f} percent={percent:.4f} amount={adjusted_stake_amount_2:.4f}") # # return adjusted_stake_amount def adjust_exit_price(self, dataframe: DataFrame): # Calculer le max des 14 derniers jours min_14_days = dataframe['lowest_1d'] max_14_days = dataframe['highest_1d'] # entry_price = dataframe['fbp'] current_price = dataframe[self.close] percent = 0.5 * (max_14_days - min_14_days) / min_14_days exit_price = (1 + percent) * entry_price print(f"Exit price ajusté price={current_price:.4f} max_14={max_14_days:.4f} exit_price={exit_price:.4f}") return exit_price def expectedProfit(self, pair: str, dataframe: DataFrame): current_price = dataframe['last_price'] # dataframe[self.close] # trade = self.getTrade(pair) # if trade: # current_price = trade.open_rate # Calculer le max des 14 derniers jours min_14_days = dataframe['lowest_1d'] max_14_days = dataframe['highest_1d'] percent = (max_14_days - current_price) / (min_14_days) min_max = dataframe['pct_min_max_1d'] # (max_14_days - min_14_days) / min_14_days expected_profit = min(0.1, max(0.01, dataframe['min_max200'] * 0.5)) 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}") # self.analyze_conditions(pair, dataframe) return expected_profit def getBinanceOrderBook(self, pair, dataframe: DataFrame): """Fetch the order book (depth) from Binance.""" # print(dataframe) last_candle = dataframe.iloc[-1].squeeze() symbol = pair.replace('/', '') try: url = f"https://api.binance.com/api/v3/depth?symbol={symbol}&limit=5000" response = requests.get(url) data = response.json() # Extract bids and asks from the order book asks, bids = self.calculateSMA(20, data['asks'], data['bids']) # Ventes List of [price, quantity] # bids = data['bids'] # asks = data['asks'] # Achats List of [price, quantity] # Process the depth data as you need it # bid_volume = sum([float(bid[1]) for bid in bids]) # Sum of all bid volumes # $ * nb / $ => nb bid_volume = sum([float(bid[0]) * float(bid[1]) / float(last_candle[self.close]) for bid in bids[:10]]) # ask_volume = sum([float(ask[1]) for ask in asks]) # Sum of all ask volumes ask_volume = sum([float(ask[0]) * float(ask[1]) / float(last_candle[self.close]) for ask in asks[:10]]) # Example: add the difference in volumes as an indicator if bid_volume and ask_volume: self.updateLastValue(dataframe, 'depth_bid_ask_diff', round(bid_volume - ask_volume, 2)) else: self.updateLastValue(dataframe, 'depth_bid_ask_diff', 0) # probabilité baisse hausse sur les n premiers élements for start in [0, 50, 100, 150]: self.updateLastValue(dataframe, 'prob_hausse_' + str(start + 50), self.calculateProbaNb(asks, bids, start, start + 50)) # dataframe['prob_hausse_' + str(nb)] = self.calculateProbaNb(asks, bids, nb) # Analyse des prix moyens pondérés par les volumes (VWAP) : # # Le VWAP (Volume Weighted Average Price) peut être utilisé pour comprendre la pression directionnelle. # Si le VWAP basé sur les ordres d'achat est plus élevé que celui des ordres de vente, # cela peut indiquer une probabilité de hausse. nb = 50 bid_vwap = sum([float(bid[0]) * float(bid[1]) for bid in bids[:nb]]) / sum( [float(bid[1]) for bid in bids[:nb]]) ask_vwap = sum([float(ask[0]) * float(ask[1]) for ask in asks[:nb]]) / sum( [float(ask[1]) for ask in asks[:nb]]) if bid_vwap > ask_vwap: self.updateLastValue(dataframe, 'vwap_hausse', round(100 * (bid_vwap - ask_vwap) / (bid_vwap + ask_vwap), 2)) else: self.updateLastValue(dataframe, 'vwap_hausse', - round(100 * (ask_vwap - bid_vwap) / (bid_vwap + ask_vwap), 2)) current_price = last_candle[self.close] # le prix actuel du marché # Calcul du seuil de variation de 1% lower_threshold = current_price * 0.99 upper_threshold = current_price * 1.01 # Volumes d'achat (bids) sous 1% du prix actuel bid_volume_1percent = sum( [float(bid[1]) for bid in bids if current_price >= float(bid[0]) >= lower_threshold]) # Volumes de vente (asks) au-dessus de 1% du prix actuel ask_volume_1percent = sum( [float(ask[1]) for ask in asks if current_price <= float(ask[0]) <= upper_threshold]) # Estimation de la probabilité basée sur le déséquilibre des volumes total_volume = bid_volume_1percent + ask_volume_1percent if total_volume > 0: prob_hausse = bid_volume_1percent / total_volume prob_baisse = ask_volume_1percent / total_volume else: prob_hausse = prob_baisse = 0 self.updateLastValue(dataframe, 'proba_hausse_1%', round(prob_hausse * 100, 2)) self.updateLastValue(dataframe, 'proba_baisse_1%', round(prob_baisse * 100, 2)) print(f"Probabilité de hausse de 1%: {prob_hausse * 100:.2f}%") print(f"Probabilité de baisse de 1%: {prob_baisse * 100:.2f}%") self.calculateResistance(pair, asks, dataframe) self.calculateSupport(pair, bids, dataframe) dataframe['r_s'] = 100 * (dataframe['r_min'] - dataframe['s_min']) / dataframe['s_min'] except Exception as e: logger.error(f"Error fetching order book: {e}") return None, None def calculateProbaNb(self, asks, bids, start, nb): top_bids = sum([float(bid[1]) for bid in bids[start:nb]]) top_asks = sum([float(ask[1]) for ask in asks[start:nb]]) if top_bids > top_asks: proba = round(100 * (top_bids - top_asks) / (top_bids + top_asks), 2) else: proba = - round(100 * (top_asks - top_bids) / (top_bids + top_asks), 2) return proba def calculateResistance(self, pair, asks, dataframe: DataFrame): last_candle = dataframe.iloc[-1].squeeze() # Filtrage +-5% current_price = float(last_candle[self.close]) lower_bound = current_price * 0.95 upper_bound = current_price * 1.05 ask_prices = [float(ask[0]) for ask in asks] ask_volumes = [float(ask[1]) for ask in asks] ask_df = pd.DataFrame({'price': ask_prices, 'volume': ask_volumes}) filtered_ask_df = ask_df[(ask_df['price'] >= lower_bound) & (ask_df['price'] <= upper_bound)] # Trier le DataFrame sur la colonne 'volume' en ordre décroissant sorted_ask_df = filtered_ask_df.sort_values(by='volume', ascending=False) # Ne garder que les 3 premières lignes (les 3 plus gros volumes) top_3_asks = sorted_ask_df.head(3) print(top_3_asks) # Convertir les ordres de vente en numpy array pour faciliter le traitement asks_array = np.array(filtered_ask_df, dtype=float) # Détecter les résistances : on peut définir qu'une résistance est un niveau de prix où la quantité est élevée # Ex: seuil de résistance à partir des 10% des plus grosses quantités resistance_threshold = np.percentile(asks_array[:, 1], 90) resistances = asks_array[asks_array[:, 1] >= resistance_threshold] # Afficher les résistances trouvées # print(f"{pair} Niveaux de résistance détectés:") # for resistance in resistances: # print(f"{pair} Prix: {resistance[0]}, Quantité: {resistance[1]}") # Exemple : somme des quantités sur des plages de prix # Intervalles de 10 USDT step = last_candle[self.close] / 100 price_intervals = np.arange(asks_array[:, 0].min(), asks_array[:, 0].max(), step=step) for start_price in price_intervals: end_price = start_price + step mask = (asks_array[:, 0] >= start_price) & (asks_array[:, 0] < end_price) volume_in_range = asks_array[mask, 1].sum() amount = volume_in_range * end_price print( f"Prix entre {start_price:.6f} et {end_price:.6f}: Volume total = {volume_in_range:.2f} amount={amount:.2f}") # Trier les asks par quantité en ordre décroissant asks_sorted = asks_array[asks_array[:, 1].argsort()][::-1] # Sélectionner les trois plus gros resistances top_3_resistances = asks_sorted[:3] # Afficher les trois plus gros resistances print("Les trois plus grosses resistances détectées : ") self.updateLastValue(dataframe, 'r3', top_3_resistances[0][0]) self.updateLastValue(dataframe, 'r2', top_3_resistances[1][0]) self.updateLastValue(dataframe, 'r1', top_3_resistances[2][0]) self.updateLastValue(dataframe, 'r_min', min(top_3_resistances[0][0], top_3_resistances[1][0], top_3_resistances[2][0])) for resistance in top_3_resistances: print(f"{pair} Prix: {resistance[0]}, Quantité: {resistance[1]}") def calculateSupport(self, pair, bids, dataframe: DataFrame): last_candle = dataframe.iloc[-1].squeeze() # Convert to pandas DataFrame to apply moving average current_price = float(last_candle[self.close]) lower_bound = current_price * 0.95 upper_bound = current_price * 1.05 bid_prices = [float(bid[0]) for bid in bids] bid_volumes = [float(bid[1]) for bid in bids] bid_df = pd.DataFrame({'price': bid_prices, 'volume': bid_volumes}) filtered_bid_df = bid_df[(bid_df['price'] >= lower_bound) & (bid_df['price'] <= upper_bound)] # Trier le DataFrame sur la colonne 'volume' en ordre décroissant sorted_bid_df = filtered_bid_df.sort_values(by='volume', ascending=False) # Ne garder que les 3 premières lignes (les 3 plus gros volumes) top_3_bids = sorted_bid_df.head(3) print(top_3_bids) # Convertir les ordres d'achat en numpy array pour faciliter le traitement bids_array = np.array(filtered_bid_df, dtype=float) # Détecter les supports : on peut définir qu'un support est un niveau de prix où la quantité est élevée # Ex: seuil de support à partir des 10% des plus grosses quantités support_threshold = np.percentile(bids_array[:, 1], 90) supports = bids_array[bids_array[:, 1] >= support_threshold] # Afficher les supports trouvés # print(f"{pair} Niveaux de support détectés:") # for support in supports: # print(f"{pair} Prix: {support[0]}, Quantité: {support[1]}") step = last_candle[self.close] / 100 # Exemple : somme des quantités sur des plages de prix pour les bids price_intervals = np.arange(bids_array[:, 0].min(), bids_array[:, 0].max(), step=step) # Intervalles de 10 USDT for start_price in price_intervals: end_price = start_price + step mask = (bids_array[:, 0] >= start_price) & (bids_array[:, 0] < end_price) volume_in_range = bids_array[mask, 1].sum() amount = volume_in_range * end_price print( f"Prix entre {start_price:.6f} et {end_price:.6f}: Volume total = {volume_in_range:.2f} amount={amount:.2f}") # Trier les bids par quantité en ordre décroissant bids_sorted = bids_array[bids_array[:, 1].argsort()][::-1] # Sélectionner les trois plus gros supports top_3_supports = bids_sorted[:3] # Afficher les trois plus gros supports print("Les trois plus gros supports détectés:") self.updateLastValue(dataframe, 's1', top_3_supports[0][0]) self.updateLastValue(dataframe, 's2', top_3_supports[1][0]) self.updateLastValue(dataframe, 's3', top_3_supports[2][0]) self.updateLastValue(dataframe, 's_min', max(top_3_supports[0][0], top_3_supports[1][0], top_3_supports[2][0])) for support in top_3_supports: print(f"{pair} Prix: {support[0]}, Quantité: {support[1]}") def updateLastValue(self, df: DataFrame, col, value): if col in df.columns: print(f"update last col {col} {value}") df.iloc[-1, df.columns.get_loc(col)] = value else: print(f"update all col {col} {value}") df[col] = value # def update_last_record(self, dataframe: DataFrame, new_data): # # Vérifiez si de nouvelles données ont été reçues # if new_data is not None: # # Ne mettez à jour que la dernière ligne du dataframe # last_index = dataframe.index[-1] # Sélectionne le dernier enregistrement # dataframe.loc[last_index] = new_data # Met à jour le dernier enregistrement avec les nouvelles données # return dataframe def calculateSMA(self, nb, asks, bids): # Prepare data for plotting bid_prices = [float(bid[0]) for bid in bids] bid_volumes = [float(bid[1]) for bid in bids] ask_prices = [float(ask[0]) for ask in asks] ask_volumes = [float(ask[1]) for ask in asks] # Convert to pandas DataFrame to apply moving average bid_df = pd.DataFrame({'price': bid_prices, 'volume': bid_volumes}) ask_df = pd.DataFrame({'price': ask_prices, 'volume': ask_volumes}) # Apply a rolling window to calculate a 10-value simple moving average (SMA) bid_df['volume_sma'] = bid_df['volume'].rolling(window=nb).mean() ask_df['volume_sma'] = ask_df['volume'].rolling(window=nb).mean() # Pour bid_df bid_df = bid_df.dropna(subset=['volume_sma']) bids_with_sma = list(zip(bid_df['price'], bid_df['volume_sma'])) # Pour ask_df ask_df = ask_df.dropna(subset=['volume_sma']) asks_with_sma = list(zip(ask_df['price'], ask_df['volume_sma'])) # print(bids_with_sma) # print(asks_with_sma) return asks_with_sma, bids_with_sma def calculate_stake(self, pair, value, last_candle): """ Calcule la mise en pourcentage (entre 20% et 100%) pour une paire donnée en fonction de la valeur actuelle et de la plage min/max de la paire. Parameters: pair (str): La paire (ex: "BTC/USDT"). value (float): La valeur actuelle de la paire. Returns: float: Le pourcentage de mise, entre 20% et 100%. """ # if pair not in self.min_max_buys: # raise ValueError(f"La paire {pair} n'est pas définie dans min_max_buys") min_val = last_candle['min_previous_1h'] # self.min_max_buys[pair]["min"] max_val = last_candle['max_previous_1h'] # self.min_max_buys[pair]["max"] amount = self.config['stake_amount'] return amount if (max_val == min_val) | (np.isnan(max_val)): return amount if value <= min_val: return amount # Mise minimale de 20% elif value >= max_val: return amount / 5 # Mise maximale de 100% else: # Calcul de la position relative entre min et max position = (value - min_val) / (max_val - min_val) # Interpolation linéaire entre 20% et 100% stake = amount - (position * (amount * 4 / 5)) return stake