Files
Freqtrade/Thor_001.py
Jérôme Delacotte 0397b06a97 Add new strategies
2025-03-06 11:44:55 +01:00

1562 lines
72 KiB
Python

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