RandomForestClassifier
This commit is contained in:
@@ -48,10 +48,13 @@ from sklearn.metrics import (
|
||||
accuracy_score,
|
||||
roc_auc_score,
|
||||
roc_curve,
|
||||
precision_score, recall_score, precision_recall_curve,
|
||||
f1_score
|
||||
)
|
||||
from sklearn.tree import export_text
|
||||
import inspect
|
||||
|
||||
from sklearn.feature_selection import mutual_info_classif
|
||||
from sklearn.inspection import permutation_importance
|
||||
|
||||
from tabulate import tabulate
|
||||
|
||||
@@ -77,7 +80,19 @@ def normalize(df):
|
||||
class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
# Machine Learning
|
||||
model = joblib.load('rf_model.pkl')
|
||||
model_indicators = ['rsi_deriv1', "max_rsi_12", "mid_smooth_5_deriv1", "volume_deriv1"]
|
||||
model_indicators = [
|
||||
'rsi', 'rsi_deriv1', "max_rsi_12",
|
||||
"bb_percent",
|
||||
'vol_24',
|
||||
'percent3',
|
||||
'sma5_dist', 'sma5_deriv1',
|
||||
'sma24_dist', 'sma24_deriv1',
|
||||
'sma60_dist', 'sma60_deriv1',
|
||||
'down_count', 'up_count',
|
||||
'down_pct', 'slope_norm',
|
||||
'min_max_60',
|
||||
'rsi_slope', 'adx_change', 'volatility_ratio'
|
||||
]
|
||||
|
||||
levels = [1, 2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
|
||||
# startup_candle_count = 12 * 24 * 5
|
||||
@@ -286,12 +301,12 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
pct_inc = DecimalParameter(0.0001, 0.003, default=0.0022, decimals=4, space='buy', optimize=True, load=True)
|
||||
|
||||
indic_5m_slope_sup_buy = CategoricalParameter(indicators, default="sma60", space='buy')
|
||||
indic_deriv_5m_slop_sup_buy = CategoricalParameter(indicators, default="sma12", space='buy', optimize=True, load=True)
|
||||
deriv_5m_slope_sup_buy = DecimalParameter(-0.1, 0.5, default=0, decimals=2, space='buy', optimize=True, load=True)
|
||||
# indic_deriv_5m_slop_sup_buy = CategoricalParameter(indicators, default="sma12", space='buy', optimize=True, load=True)
|
||||
# deriv_5m_slope_sup_buy = DecimalParameter(-0.1, 0.5, default=0, decimals=2, space='buy', optimize=True, load=True)
|
||||
|
||||
indic_5m_slope_inf_buy = CategoricalParameter(indicators, default="sma60", space='buy')
|
||||
indic_deriv_5m_slop_sup_buy = CategoricalParameter(indicators, default="sma12", space='buy', optimize=True, load=True)
|
||||
deriv_5m_slope_sup_buy = DecimalParameter(-0.1, 0.5, default=0, decimals=2, space='buy', optimize=True, load=True)
|
||||
# indic_deriv_5m_slop_inf_buy = CategoricalParameter(indicators, default="sma12", space='buy', optimize=True, load=True)
|
||||
# deriv_5m_slope_inf_buy = DecimalParameter(-0.1, 0.5, default=0, decimals=2, space='buy', optimize=True, load=True)
|
||||
|
||||
|
||||
# indic_deriv1_5m = DecimalParameter(-2, 2, default=0, decimals=2, space='buy', optimize=True, load=True)
|
||||
@@ -392,7 +407,7 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
condition = True #(last_candle[f"{indic_5m}_deriv1"] >= indic_deriv1_5m) and (last_candle[f"{indic_5m}_deriv2"] >= indic_deriv2_5m)
|
||||
|
||||
# allow_to_buy = True #(not self.stop_all) #& (not self.all_down)
|
||||
# and val > self.buy_val.value #not last_candle['tendency'] in ('B-', 'B--') # (rate <= float(limit)) | (entry_tag == 'force_entry')
|
||||
# and val > self.buy_val.value #not last_candle['tendency'] in (-1, -2) # (rate <= float(limit)) | (entry_tag == 'force_entry')
|
||||
allow_to_buy = (condition and not self.pairs[pair]['stop']) | (entry_tag == 'force_entry')
|
||||
|
||||
# if allow_to_buy:
|
||||
@@ -562,7 +577,7 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
|
||||
# if (last_candle['mid_smooth_deriv1'] >= 0):
|
||||
# return None
|
||||
# if (last_candle['tendency'] in ('H++', 'H+')) and (last_candle['rsi'] < 80):
|
||||
# if (last_candle['tendency'] in (2, 1)) and (last_candle['rsi'] < 80):
|
||||
# return None
|
||||
#
|
||||
# if (last_candle['sma24_deriv1'] < 0 and before_last_candle['sma24_deriv1'] >= 0) and (current_profit > expected_profit):
|
||||
@@ -573,7 +588,7 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
# trend = last_candle['trend_class_1d']
|
||||
# if (trend == "B-" or trend == "B--") and self.pairs[pair]['has_gain'] == 0: # and (last_candle[f"{indic_5m_sell}_deriv1"] <= indic_deriv1_5m_sell and last_candle[f"{indic_5m_sell}_deriv2"] <= indic_deriv2_5m_sell):
|
||||
#
|
||||
# if (last_candle['max_rsi_12_1h'] > 75) and last_candle['trend_class_1h'] == 'H+' and profit > max(5, expected_profit) and (last_candle['hapercent'] < 0):
|
||||
# if (last_candle['max_rsi_12_1h'] > 75) and last_candle['trend_class_1h'] == 1 and profit > max(5, expected_profit) and (last_candle['hapercent'] < 0):
|
||||
# self.pairs[pair]['stop'] = True
|
||||
# self.log_trade(
|
||||
# last_candle=last_candle,
|
||||
@@ -854,11 +869,11 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
|
||||
# On considère les petites valeurs comme zéro
|
||||
if abs(d1) < eps:
|
||||
return 'P' # Palier / neutre
|
||||
return 0 # Palier / neutre
|
||||
if d1 > d1_lim_sup:
|
||||
return 'H++' if d2 > eps else 'H+' # Acceleration Hausse / Ralentissement Hausse
|
||||
return 2 if d2 > eps else 1 # Acceleration Hausse / Ralentissement Hausse
|
||||
if d1 < d1_lim_inf:
|
||||
return 'B--' if d2 < -eps else 'B-' # Acceleration Baisse / Ralentissement Baisse
|
||||
return -2 if d2 < -eps else -1 # Acceleration Baisse / Ralentissement Baisse
|
||||
if abs(d1) < eps:
|
||||
return 'DH' if d2 > eps else 'DB' # Depart Hausse / Depart Baisse
|
||||
return 'Mid'
|
||||
@@ -874,13 +889,13 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
# d1_lim_inf = -0.01
|
||||
# d1_lim_sup = 0.01
|
||||
# if d1 >= d1_lim_inf and d1 <= d1_lim_sup: # and d2 >= d2_lim_inf and d2 <= d2_lim_sup:
|
||||
# return 'P' # Palier
|
||||
# return 0 # Palier
|
||||
# if d1 == 0.0:
|
||||
# return 'DH' if d2 > 0 else 'DB' # Depart Hausse / Départ Baisse
|
||||
# if d1 > d1_lim_sup:
|
||||
# return 'H++' if d2 > 0 else 'H+' # Acceleration Hausse / Ralentissement Hausse
|
||||
# return 2 if d2 > 0 else 1 # Acceleration Hausse / Ralentissement Hausse
|
||||
# if d1 < d1_lim_inf:
|
||||
# return 'B--' if d2 < 0 else 'B-' # Accéleration Baisse / Ralentissement Baisse
|
||||
# return -2 if d2 < 0 else -1 # Accéleration Baisse / Ralentissement Baisse
|
||||
# return 'Mid'
|
||||
#
|
||||
# dataframe[f"tendency{suffixe}"] = dataframe.apply(tag_by_derivatives, axis=1)
|
||||
@@ -898,6 +913,19 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
################### INFORMATIVE 1h
|
||||
informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h")
|
||||
informative = self.populateDataframe(informative, timeframe='1h')
|
||||
# informative['target_value'] = informative['sma5'].shift(-6).rolling(5).max() - informative['sma5'] * 1.005
|
||||
# if self.dp.runmode.value in ('backtest'):
|
||||
# self.trainModel(informative, metadata)
|
||||
#
|
||||
# # Préparer les features pour la prédiction
|
||||
# features = informative[self.model_indicators].fillna(0)
|
||||
#
|
||||
# # Prédiction : probabilité que le prix monte
|
||||
# probs = self.model.predict_proba(features)[:, 1]
|
||||
#
|
||||
# # Sauvegarder la probabilité pour l’analyse
|
||||
# informative['ml_prob'] = probs
|
||||
|
||||
dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True)
|
||||
|
||||
################### INFORMATIVE 1d
|
||||
@@ -953,9 +981,6 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
dataframe['mid_smooth_5h']
|
||||
dataframe["mid_smooth_5h_deriv2"] = 100 * dataframe["mid_smooth_5h_deriv1"].diff().rolling(window=60).mean()
|
||||
|
||||
# Compter les baisses / hausses consécutives
|
||||
# self.calculateDownAndUp(dataframe, limit=0.0001)
|
||||
|
||||
# ===============================
|
||||
# Lissage des valeurs Journalières
|
||||
horizon_d = 12 * 5 * 24
|
||||
@@ -1030,29 +1055,36 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
|
||||
dataframe['stop_buying'] = latched
|
||||
|
||||
self.trainModel(dataframe, metadata)
|
||||
if self.dp.runmode.value in ('backtest'):
|
||||
self.trainModel(dataframe, metadata)
|
||||
|
||||
# Préparer les features pour la prédiction
|
||||
features = dataframe[self.model_indicators].fillna(0)
|
||||
|
||||
# Prédiction : probabilité que le prix monte
|
||||
# probs = self.model.predict_proba(features)[:, 1]
|
||||
probs = self.model.predict_proba(features)[:, 1]
|
||||
|
||||
# Sauvegarder la probabilité pour l’analyse
|
||||
# dataframe['ml_prob'] = probs
|
||||
dataframe['ml_prob'] = probs
|
||||
|
||||
# self.inspect_model(self.model)
|
||||
self.inspect_model(self.model)
|
||||
|
||||
return dataframe
|
||||
|
||||
def trainModel(self, dataframe: DataFrame, metadata: dict):
|
||||
df = dataframe.copy()
|
||||
# 3️⃣ Créer la cible : 1 si le prix monte dans les prochaines bougies
|
||||
df['target'] = (1000 * (df['sma24'].shift(-24) - df['sma24'])) #.astype(int)
|
||||
# df['target'] = (df['sma24'].shift(-24) > df['sma24']).astype(int)
|
||||
df['target'] = (df['sma5'].shift(-12).rolling(12).max() > df['sma5'] * 1.00025).astype(int)
|
||||
df['target'] = df['target'].fillna(0).astype(int)
|
||||
|
||||
# Nettoyage
|
||||
df = df.dropna()
|
||||
|
||||
X = df[self.model_indicators]
|
||||
y = df['target'] # ta colonne cible binaire ou numérique
|
||||
print(self.feature_auc_scores(X, y))
|
||||
|
||||
# 4️⃣ Split train/test
|
||||
X = df[self.model_indicators]
|
||||
y = df['target']
|
||||
@@ -1060,39 +1092,50 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
|
||||
# 5️⃣ Entraînement du modèle
|
||||
# train_model = RandomForestClassifier(n_estimators=200, random_state=42)
|
||||
# train_model = RandomForestClassifier(
|
||||
# n_estimators=300,
|
||||
# max_depth=12,
|
||||
# min_samples_split=4,
|
||||
# min_samples_leaf=2,
|
||||
# max_features='sqrt',
|
||||
# random_state=42,
|
||||
# n_jobs=-1
|
||||
# )
|
||||
train_model = RandomForestRegressor(
|
||||
train_model = RandomForestClassifier(
|
||||
n_estimators=300,
|
||||
max_depth=None,
|
||||
random_state=42,
|
||||
n_jobs=-1
|
||||
max_depth=12,
|
||||
# min_samples_split=4,
|
||||
# min_samples_leaf=2,
|
||||
# max_features='sqrt',
|
||||
# random_state=42,
|
||||
# n_jobs=-1,
|
||||
class_weight='balanced'
|
||||
)
|
||||
train_model.fit(X_train, y_train)
|
||||
|
||||
# 6️⃣ Évaluer la précision (facultatif)
|
||||
preds = train_model.predict(X_test)
|
||||
# acc = accuracy_score(y_test, preds)
|
||||
# print(f"Accuracy: {acc:.3f}")
|
||||
acc = accuracy_score(y_test, preds)
|
||||
print(f"Accuracy: {acc:.3f}")
|
||||
|
||||
# 7️⃣ Sauvegarde du modèle
|
||||
joblib.dump(train_model, 'rf_model.pkl')
|
||||
print("✅ Modèle sauvegardé sous rf_model.pkl")
|
||||
|
||||
y_pred = train_model.predict(X_test)
|
||||
# X = dataframe des features (après shift/rolling/indicators)
|
||||
# y = target binaire ou décimale
|
||||
# model = ton modèle entraîné (RandomForestClassifier ou Regressor)
|
||||
|
||||
print("R² :", r2_score(y_test, y_pred))
|
||||
print("RMSE :", mean_squared_error(y_test, y_pred)) #, squared=False))
|
||||
print("MAE :", mean_absolute_error(y_test, y_pred))
|
||||
# # --- 1️⃣ Mutual Information (MI) ---
|
||||
# mi_scores = mutual_info_classif(X.fillna(0), y)
|
||||
# mi_series = pd.Series(mi_scores, index=X.columns, name='MI')
|
||||
#
|
||||
# # --- 2️⃣ Permutation Importance (PI) ---
|
||||
# pi_result = permutation_importance(train_model, X, y, n_repeats=10, random_state=42, n_jobs=-1)
|
||||
# pi_series = pd.Series(pi_result.importances_mean, index=X.columns, name='PI')
|
||||
#
|
||||
# # --- 3️⃣ Combinaison dans un seul dataframe ---
|
||||
# importance_df = pd.concat([mi_series, pi_series], axis=1)
|
||||
# importance_df = importance_df.sort_values(by='PI', ascending=False) # tri par importance modèle
|
||||
# print(importance_df)
|
||||
#
|
||||
# importance_df.plot(kind='bar', figsize=(10, 5))
|
||||
# plt.title("Mutual Info vs Permutation Importance")
|
||||
# plt.ylabel("Score")
|
||||
# plt.show()
|
||||
|
||||
# self.analyze_model(train_model, X_train, X_test, y_train, y_test)
|
||||
self.analyze_model(train_model, X_train, X_test, y_train, y_test)
|
||||
|
||||
def inspect_model(self, model):
|
||||
"""
|
||||
@@ -1252,39 +1295,138 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
plt.savefig(os.path.join(output_dir, "Courbe ROC.png"), bbox_inches="tight")
|
||||
plt.close()
|
||||
|
||||
# ---- Interprétation SHAP (optionnelle) ----
|
||||
try:
|
||||
import shap
|
||||
# # ---- Interprétation SHAP (optionnelle) ----
|
||||
# try:
|
||||
# import shap
|
||||
#
|
||||
# print("\n===== 💡 ANALYSE SHAP =====")
|
||||
# explainer = shap.TreeExplainer(model)
|
||||
# shap_values = explainer.shap_values(X_test)
|
||||
# # shap.summary_plot(shap_values[1], X_test)
|
||||
# # Vérifie le type de sortie de shap_values
|
||||
# if isinstance(shap_values, list):
|
||||
# # Cas des modèles de classification (plusieurs classes)
|
||||
# shap_values_to_plot = shap_values[0] if len(shap_values) == 1 else shap_values[1]
|
||||
# else:
|
||||
# shap_values_to_plot = shap_values
|
||||
#
|
||||
# # Ajustement des dimensions au besoin
|
||||
# if shap_values_to_plot.shape[1] != X_test.shape[1]:
|
||||
# print(f"⚠️ Mismatch dimensions SHAP ({shap_values_to_plot.shape[1]}) vs X_test ({X_test.shape[1]})")
|
||||
# min_dim = min(shap_values_to_plot.shape[1], X_test.shape[1])
|
||||
# shap_values_to_plot = shap_values_to_plot[:, :min_dim]
|
||||
# X_to_plot = X_test.iloc[:, :min_dim]
|
||||
# else:
|
||||
# X_to_plot = X_test
|
||||
#
|
||||
# plt.figure(figsize=(12, 4))
|
||||
# shap.summary_plot(shap_values_to_plot, X_to_plot, show=False)
|
||||
# plt.savefig(os.path.join(output_dir, "shap_summary.png"), bbox_inches="tight")
|
||||
# plt.close()
|
||||
# except ImportError:
|
||||
# print("\n(SHAP non installé — `pip install shap` pour activer l’analyse SHAP.)")
|
||||
|
||||
print("\n===== 💡 ANALYSE SHAP =====")
|
||||
explainer = shap.TreeExplainer(model)
|
||||
shap_values = explainer.shap_values(X_test)
|
||||
# shap.summary_plot(shap_values[1], X_test)
|
||||
# Vérifie le type de sortie de shap_values
|
||||
if isinstance(shap_values, list):
|
||||
# Cas des modèles de classification (plusieurs classes)
|
||||
shap_values_to_plot = shap_values[0] if len(shap_values) == 1 else shap_values[1]
|
||||
else:
|
||||
shap_values_to_plot = shap_values
|
||||
y_proba = model.predict_proba(X_test)[:, 1]
|
||||
|
||||
# Ajustement des dimensions au besoin
|
||||
if shap_values_to_plot.shape[1] != X_test.shape[1]:
|
||||
print(f"⚠️ Mismatch dimensions SHAP ({shap_values_to_plot.shape[1]}) vs X_test ({X_test.shape[1]})")
|
||||
min_dim = min(shap_values_to_plot.shape[1], X_test.shape[1])
|
||||
shap_values_to_plot = shap_values_to_plot[:, :min_dim]
|
||||
X_to_plot = X_test.iloc[:, :min_dim]
|
||||
else:
|
||||
X_to_plot = X_test
|
||||
# Trace ou enregistre le graphique
|
||||
self.plot_threshold_analysis(y_test, y_proba, step=0.05,
|
||||
save_path="/home/souti/freqtrade/user_data/plots/threshold_analysis.png")
|
||||
|
||||
plt.figure(figsize=(12, 10))
|
||||
shap.summary_plot(shap_values_to_plot, X_to_plot, show=False)
|
||||
plt.savefig(os.path.join(output_dir, "shap_summary.png"), bbox_inches="tight")
|
||||
plt.close()
|
||||
except ImportError:
|
||||
print("\n(SHAP non installé — `pip install shap` pour activer l’analyse SHAP.)")
|
||||
# y_test : vraies classes (0 / 1)
|
||||
# y_proba : probabilités de la classe 1 prédites par ton modèle
|
||||
# Exemple : y_proba = model.predict_proba(X_test)[:, 1]
|
||||
|
||||
seuils = np.arange(0.0, 1.01, 0.05)
|
||||
precisions, recalls, f1s = [], [], []
|
||||
|
||||
for seuil in seuils:
|
||||
y_pred = (y_proba >= seuil).astype(int)
|
||||
precisions.append(precision_score(y_test, y_pred))
|
||||
recalls.append(recall_score(y_test, y_pred))
|
||||
f1s.append(f1_score(y_test, y_pred))
|
||||
|
||||
plt.figure(figsize=(10, 6))
|
||||
plt.plot(seuils, precisions, label='Précision', marker='o')
|
||||
plt.plot(seuils, recalls, label='Rappel', marker='o')
|
||||
plt.plot(seuils, f1s, label='F1-score', marker='o')
|
||||
|
||||
# Ajoute un point pour le meilleur F1
|
||||
best_idx = np.argmax(f1s)
|
||||
plt.scatter(seuils[best_idx], f1s[best_idx], color='red', s=80, label=f'Max F1 ({seuils[best_idx]:.2f})')
|
||||
|
||||
plt.title("Performance du modèle selon le seuil de probabilité")
|
||||
plt.xlabel("Seuil de probabilité (classe 1)")
|
||||
plt.ylabel("Score")
|
||||
plt.grid(True, alpha=0.3)
|
||||
plt.legend()
|
||||
plt.savefig("/home/souti/freqtrade/user_data/plots/seuil_de_probabilite.png", bbox_inches='tight')
|
||||
# plt.show()
|
||||
|
||||
print(f"✅ Meilleur F1 : {f1s[best_idx]:.3f} au seuil {seuils[best_idx]:.2f}")
|
||||
|
||||
print("\n===== ✅ FIN DE L’ANALYSE =====")
|
||||
|
||||
def plot_threshold_analysis(self, y_true, y_proba, step=0.05, save_path=None):
|
||||
"""
|
||||
Affiche la précision, le rappel et le F1-score selon le seuil de décision.
|
||||
y_true : labels réels (0 ou 1)
|
||||
y_proba : probabilités prédites (P(hausse))
|
||||
step : pas entre les seuils testés
|
||||
save_path : si renseigné, enregistre l'image au lieu d'afficher
|
||||
"""
|
||||
|
||||
# Le graphique généré affichera trois courbes :
|
||||
#
|
||||
# 🔵 Precision — la fiabilité de tes signaux haussiers.
|
||||
#
|
||||
# 🟢 Recall — la proportion de hausses que ton modèle détecte.
|
||||
#
|
||||
# 🟣 F1-score — le compromis optimal entre les deux.
|
||||
|
||||
thresholds = np.arange(0, 1.01, step)
|
||||
precisions, recalls, f1s = [], [], []
|
||||
|
||||
for thr in thresholds:
|
||||
preds = (y_proba >= thr).astype(int)
|
||||
precisions.append(precision_score(y_true, preds))
|
||||
recalls.append(recall_score(y_true, preds))
|
||||
f1s.append(f1_score(y_true, preds))
|
||||
|
||||
plt.figure(figsize=(10, 6))
|
||||
plt.plot(thresholds, precisions, label="Precision", linewidth=2)
|
||||
plt.plot(thresholds, recalls, label="Recall", linewidth=2)
|
||||
plt.plot(thresholds, f1s, label="F1-score", linewidth=2, linestyle="--")
|
||||
plt.axvline(0.5, color='gray', linestyle=':', label="Seuil 0.5")
|
||||
plt.title("📊 Performance selon le seuil de probabilité", fontsize=14)
|
||||
plt.xlabel("Seuil de décision (threshold)")
|
||||
plt.ylabel("Score")
|
||||
plt.legend()
|
||||
plt.grid(True, alpha=0.3)
|
||||
|
||||
if save_path:
|
||||
plt.savefig(save_path, bbox_inches='tight')
|
||||
print(f"✅ Graphique enregistré : {save_path}")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
# # =============================
|
||||
# # Exemple d’utilisation :
|
||||
# # =============================
|
||||
# if __name__ == "__main__":
|
||||
# # Exemple : chargement d’un modèle et test
|
||||
# import joblib
|
||||
#
|
||||
# model = joblib.load("/media/Home/home/souti/freqtrade/user_data/strategies/tools/sklearn/model.pkl")
|
||||
# data = np.load("/media/Home/home/souti/freqtrade/user_data/strategies/tools/sklearn/test_data.npz")
|
||||
# X_test, y_test = data["X"], data["y"]
|
||||
#
|
||||
# y_proba = model.predict_proba(X_test)[:, 1]
|
||||
#
|
||||
# # Trace ou enregistre le graphique
|
||||
# plot_threshold_analysis(y_test, y_proba, step=0.05,
|
||||
# save_path="/media/Home/home/souti/freqtrade/user_data/strategies/tools/sklearn/threshold_analysis.png")
|
||||
|
||||
|
||||
def populateDataframe(self, dataframe, timeframe='5m'):
|
||||
heikinashi = qtpylib.heikinashi(dataframe)
|
||||
dataframe['haopen'] = heikinashi['open']
|
||||
@@ -1292,10 +1434,10 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
dataframe['hapercent'] = (dataframe['haclose'] - dataframe['haopen']) / dataframe['haclose']
|
||||
dataframe['mid'] = dataframe['haopen'] + (dataframe['haclose'] - dataframe['haopen']) / 2
|
||||
|
||||
dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"]
|
||||
dataframe["percent3"] = (dataframe["close"] - dataframe["open"].shift(3)) / dataframe["open"].shift(3)
|
||||
dataframe["percent12"] = (dataframe["close"] - dataframe["open"].shift(12)) / dataframe["open"].shift(12)
|
||||
dataframe["percent24"] = (dataframe["close"] - dataframe["open"].shift(24)) / dataframe["open"].shift(24)
|
||||
dataframe["percent"] = dataframe['close'].pct_change()
|
||||
dataframe["percent3"] = dataframe['close'].pct_change(3)
|
||||
dataframe["percent12"] = dataframe['close'].pct_change(12)
|
||||
dataframe["percent24"] = dataframe['close'].pct_change(24)
|
||||
|
||||
if self.dp.runmode.value in ('backtest'):
|
||||
dataframe['futur_percent'] = 100 * (dataframe['close'].shift(-1) - dataframe['close']) / dataframe['close']
|
||||
@@ -1323,6 +1465,7 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
dataframe['max12'] = talib.MAX(dataframe['close'], timeperiod=12)
|
||||
dataframe['max60'] = talib.MAX(dataframe['close'], timeperiod=60)
|
||||
dataframe['min60'] = talib.MIN(dataframe['close'], timeperiod=60)
|
||||
dataframe['min_max_60'] = ((dataframe['max60'] - dataframe['close']) / dataframe['min60'])
|
||||
# dataframe['min36'] = talib.MIN(dataframe['close'], timeperiod=36)
|
||||
# dataframe['max36'] = talib.MAX(dataframe['close'], timeperiod=36)
|
||||
# dataframe['pct36'] = 100 * (dataframe['max36'] - dataframe['min36']) / dataframe['min36']
|
||||
@@ -1332,6 +1475,13 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
dataframe['bb_lowerband'] = bollinger['lower']
|
||||
dataframe['bb_middleband'] = bollinger['mid']
|
||||
dataframe['bb_upperband'] = bollinger['upper']
|
||||
dataframe["bb_percent"] = (
|
||||
(dataframe["close"] - dataframe["bb_lowerband"]) /
|
||||
(dataframe["bb_upperband"] - dataframe["bb_lowerband"])
|
||||
)
|
||||
dataframe["bb_width"] = (
|
||||
(dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"]
|
||||
)
|
||||
|
||||
# Calcul MACD
|
||||
macd, macdsignal, macdhist = talib.MACD(
|
||||
@@ -1403,6 +1553,46 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
# tr = DataFrame({'hl': high_low, 'hc': high_close, 'lc': low_close}).max(axis=1)
|
||||
# dataframe['atr'] = tr.rolling(window=self.DEFAULT_PARAMS['atr_period']).mean()
|
||||
|
||||
###########################
|
||||
# df = ton DataFrame OHLCV avec colonnes: open, high, low, close, volume
|
||||
# Assure-toi qu'il est trié par date croissante
|
||||
|
||||
# --- Volatilité normalisée ---
|
||||
dataframe['atr'] = ta.volatility.AverageTrueRange(
|
||||
high=dataframe['high'], low=dataframe['low'], close=dataframe['close'], window=14
|
||||
).average_true_range()
|
||||
dataframe['atr_norm'] = dataframe['atr'] / dataframe['close']
|
||||
|
||||
# --- Force de tendance ---
|
||||
dataframe['adx'] = ta.trend.ADXIndicator(
|
||||
high=dataframe['high'], low=dataframe['low'], close=dataframe['close'], window=14
|
||||
).adx()
|
||||
|
||||
# --- Volume directionnel (On Balance Volume) ---
|
||||
dataframe['obv'] = ta.volume.OnBalanceVolumeIndicator(
|
||||
close=dataframe['close'], volume=dataframe['volume']
|
||||
).on_balance_volume()
|
||||
|
||||
# --- Volatilité récente (écart-type des rendements) ---
|
||||
dataframe['ret'] = dataframe['close'].pct_change()
|
||||
dataframe['vol_24'] = dataframe['ret'].rolling(24).std()
|
||||
|
||||
# Compter les baisses / hausses consécutives
|
||||
self.calculateDownAndUp(dataframe, limit=0.0001)
|
||||
|
||||
# df : ton dataframe OHLCV + indicateurs existants
|
||||
# Assurez-vous que les colonnes suivantes existent :
|
||||
# 'max_rsi_12', 'roc_24', 'bb_percent_1h'
|
||||
|
||||
# --- Filtrage des NaN initiaux ---
|
||||
# dataframe = dataframe.dropna()
|
||||
|
||||
dataframe['rsi_slope'] = dataframe['rsi'].diff(3) / 3 # vitesse moyenne du RSI
|
||||
dataframe['adx_change'] = dataframe['adx'] - dataframe['adx'].shift(12) # évolution de la tendance
|
||||
dataframe['volatility_ratio'] = dataframe['atr_norm'] / dataframe['bb_width']
|
||||
|
||||
###########################
|
||||
|
||||
dataframe['volume_sma_deriv'] = dataframe['volume'] * dataframe['sma5_deriv1'] / (dataframe['volume'].rolling(5).mean())
|
||||
self.calculeDerivees(dataframe, 'volume', timeframe=timeframe, ema_period=12)
|
||||
|
||||
@@ -1410,6 +1600,15 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
|
||||
return dataframe
|
||||
|
||||
def feature_auc_scores(self, X, y):
|
||||
aucs = {}
|
||||
for col in X.columns:
|
||||
try:
|
||||
aucs[col] = roc_auc_score(y, X[col].fillna(method='ffill').fillna(0))
|
||||
except Exception:
|
||||
aucs[col] = np.nan
|
||||
return pd.Series(aucs).sort_values(ascending=False)
|
||||
|
||||
def macd_tendance_int(self, dataframe: pd.DataFrame,
|
||||
macd_col='macd',
|
||||
signal_col='macdsignal',
|
||||
@@ -1462,8 +1661,8 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
return tendance
|
||||
|
||||
def calculateDownAndUp(self, dataframe, limit=0.0001):
|
||||
dataframe['down'] = dataframe['mid_smooth_1h_deriv1'] < limit # dataframe['hapercent'] <= limit
|
||||
dataframe['up'] = dataframe['mid_smooth_1h_deriv1'] > limit # dataframe['hapercent'] >= limit
|
||||
dataframe['down'] = dataframe['hapercent'] <= limit
|
||||
dataframe['up'] = dataframe['hapercent'] >= limit
|
||||
dataframe['down_count'] = - dataframe['down'].astype(int) * (
|
||||
dataframe['down'].groupby((dataframe['down'] != dataframe['down'].shift()).cumsum()).cumcount() + 1)
|
||||
dataframe['up_count'] = dataframe['up'].astype(int) * (
|
||||
@@ -1502,6 +1701,12 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
factor1 = 100 * (ema_period / 5)
|
||||
factor2 = 10 * (ema_period / 5)
|
||||
|
||||
dataframe[f"{name}{suffixe}_inv"] = (dataframe[f"{name}{suffixe}"].shift(2) >= dataframe[f"{name}{suffixe}"].shift(1)) \
|
||||
& (dataframe[f"{name}{suffixe}"].shift(1) <= dataframe[f"{name}{suffixe}"])
|
||||
# --- Distance à la moyenne mobile ---
|
||||
dataframe[f"{name}{suffixe}_dist"] = (dataframe['close'] - dataframe[f"{name}{suffixe}"]) / dataframe[f"{name}{suffixe}"]
|
||||
|
||||
|
||||
# dérivée relative simple
|
||||
dataframe[d1_col] = (dataframe[name] - dataframe[name].shift(1)) / dataframe[name].shift(1)
|
||||
# lissage EMA
|
||||
@@ -2730,7 +2935,7 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
|
||||
# return last_candle['slope_norm_1d'] < last_candle['slope_norm_1h']
|
||||
|
||||
if self.pairs[pair]['stop'] and last_candle['max_rsi_12_1h'] <= 60 and last_candle['trend_class_1h'] == 'B-':
|
||||
if self.pairs[pair]['stop'] and last_candle['max_rsi_12_1h'] <= 60 and last_candle['trend_class_1h'] == -1:
|
||||
dispo = round(self.wallets.get_available_stake_amount())
|
||||
self.pairs[pair]['stop'] = False
|
||||
self.log_trade(
|
||||
@@ -2948,15 +3153,15 @@ class Zeus_8_3_2_B_4_2(IStrategy):
|
||||
q1, q2, q3, q4 = q
|
||||
v = series.iloc[i]
|
||||
if v <= q1:
|
||||
trend_class.append('B--')
|
||||
trend_class.append(-2)
|
||||
elif v <= q2:
|
||||
trend_class.append('B-')
|
||||
trend_class.append(-1)
|
||||
elif v <= q3:
|
||||
trend_class.append('P')
|
||||
trend_class.append(0)
|
||||
elif v <= q4:
|
||||
trend_class.append('H+')
|
||||
trend_class.append(1)
|
||||
else:
|
||||
trend_class.append('H++')
|
||||
trend_class.append(2)
|
||||
return trend_class
|
||||
|
||||
dataframe['slope_norm'] = df['slope_norm']
|
||||
|
||||
@@ -44,4 +44,4 @@ for title, normalize in titles_options:
|
||||
print(title)
|
||||
print(disp.confusion_matrix)
|
||||
|
||||
plt.show()
|
||||
plt.show()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
from sklearn.ensemble import RandomForestRegressor
|
||||
from sklearn.metrics import r2_score, mean_absolute_error
|
||||
import pandas as pd
|
||||
|
||||
Reference in New Issue
Block a user