diff --git a/tools/detect_regime.py b/tools/detect_regime.py index 966a9d3..b9ceaca 100755 --- a/tools/detect_regime.py +++ b/tools/detect_regime.py @@ -33,44 +33,67 @@ if 'close' not in df.columns or 'timestamp' not in df.columns: sys.exit(1) # --- paramètres --- -SMOOTH_WIN = 5 # EMA pour lisser la pente -NUM_TOP = 3 # nombre de segments à garder par classe +SMOOTH_WIN = 10 # EMA pour lisser la pente +NUM_TOP = 1 # nombre de segments à garder par classe # --- charger les données --- df['timestamp'] = pd.to_datetime(df['timestamp'], errors='coerce') +# filtrer uniquement 2024 et 2025 +df = df[df['timestamp'].dt.year.isin([2024, 2025])] + # --- calcul SMA14 --- -df['sma14'] = ta.trend.sma_indicator(df['close'], 14) +df['sma'] = talib.SMA(df, timeperiod=20) #ta.trend.sma_indicator(df['close'], 14) # --- pente brute --- -df['slope'] = df['sma14'].diff() +df['slope'] = df['sma'].diff() # --- lissage EMA --- df['slope_smooth'] = df['slope'].ewm(span=SMOOTH_WIN, adjust=False).mean() +#df["slope_smooth"] = savgol_filter(df["slope_smooth"], window_length=21, polyorder=3) + # --- normalisation relative --- df['slope_norm'] = df['slope_smooth'] / df['close'] -df['slope_norm'].fillna(0, inplace=True) +# df['slope_norm'].fillna(0, inplace=True) +df['slope_norm'] = df['slope_norm'].fillna(0) + # --- classification dynamique via quantiles --- -q = df['slope_norm'].quantile([0.07, 0.21, 0.35, 0.65, 0.79, 0.93]).values -q1, q2, q3, q4, q5, q6 = q + +q = df['slope_norm'].quantile([0.125, 0.375, 0.625, 0.875]).values +q1, q2, q3, q4 = q def classify(v): if v <= q1: - return 'BR3' - elif v <= q2: return 'BR2' - elif v <= q3: + elif v <= q2: return 'BR1' - elif v <= q4: + elif v <= q3: return 'RG' - elif v <= q5: + elif v <= q4: return 'BU1' - elif v <= q6: - return 'BU2' else: - return 'BU3' + return 'BU2' + +# q = df['slope_norm'].quantile([0.7, 0.21, 0.35, 0.65, 0.79, 0.93]).values +# q1, q2, q3, q4, q5, q6 = q +# +# def classify(v): +# if v <= q1: +# return 'BR3' +# elif v <= q2: +# return 'BR2' +# elif v <= q3: +# return 'BR1' +# elif v <= q4: +# return 'RG' +# elif v <= q5: +# return 'BU1' +# elif v <= q6: +# return 'BU2' +# else: +# return 'BU3' df['trend_class'] = df['slope_norm'].apply(classify) @@ -100,7 +123,7 @@ if trend is not None and start_idx is not None: # --- extraire les 5 plus longs segments par classe --- top_segments_by_class = {} -for cls in ['BR3','BR2','BR1','RG','BU1','BU2','BU3']: +for cls in ['BR2','BR1','RG','BU1','BU2']: cls_segments = [(t,s,e) for t,s,e in segments if t==cls] # calcul de la durée cls_segments = [(t,s,e,(e-s).total_seconds()) for t,s,e in cls_segments] diff --git a/tools/read_trends.py b/tools/read_trends.py new file mode 100644 index 0000000..d3e0c94 --- /dev/null +++ b/tools/read_trends.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +import os +import json +from pathlib import Path + +def load_params_tree(base_path="user_data/strategies/params/"): + base = Path(base_path) + params_tree = {} + if not base.exists(): + raise FileNotFoundError(f"Base path '{base_path}' not found.") + + for pair_dir in base.iterdir(): + if not pair_dir.is_dir(): + continue + pair = pair_dir.name # ex : BTC-USDT + params_tree.setdefault(pair, {}) + + for trend_dir in pair_dir.iterdir(): + if not trend_dir.is_dir(): + continue + trend = trend_dir.name # ex : bull / bear / range + params_tree[pair].setdefault(trend, []) + + for file in trend_dir.glob("*-hyperopt_result.json"): + filename = file.name + + # Extraire START et END + try: + prefix = filename.replace("-hyperopt_result.json", "") + start, end = prefix.split("-", 1) # split en 2 + except Exception: + start = None + end = None + + # Lire le JSON + try: + with open(file, "r") as f: + content = json.load(f) + except Exception as err: + content = {"error": str(err)} + + params_tree[pair][trend].append({ + "start": start, + "end": end, + "file": str(file), + "content": content, + }) + + return params_tree + +def getTrend(data, pair, trend, space, param): + return data[pair][trend][0]['content']['params'][space][param] + + +if __name__ == "__main__": + data = load_params_tree("user_data/strategies/params/") + # print(data) + # Test affichage minimal + for pair, trends in data.items(): + for trend, entries in trends.items(): + if entries: + indic_5m = getTrend(data, pair, trend, 'buy', 'indic_5m') + indic_deriv1_5m = getTrend(data, pair, trend, 'buy', 'indic_deriv1_5m') + indic_deriv2_5m = getTrend(data, pair, trend, 'buy', 'indic_deriv2_5m') + + indic_5m_sell = getTrend(data, pair, trend, 'sell', 'indic_5m_sell') + indic_deriv1_5m_sell = getTrend(data, pair, trend, 'sell', 'indic_deriv1_5m_sell') + indic_deriv2_5m_sell = getTrend(data, pair, trend, 'sell', 'indic_deriv2_5m_sell') + + print(f"{pair} -> {trend} -> {indic_5m} {indic_deriv1_5m} {indic_deriv2_5m} {indic_5m_sell} {indic_deriv1_5m_sell} {indic_deriv2_5m_sell}") + # for entry in entries: + # print(entry) diff --git a/tools/run_hyperopt_batch.sh b/tools/run_hyperopt_batch.sh index df631d6..37b711c 100755 --- a/tools/run_hyperopt_batch.sh +++ b/tools/run_hyperopt_batch.sh @@ -47,10 +47,10 @@ while read -r PAIR; do continue fi - if [[ "$START" < "2024-09-01" ]]; then - echo "TOO OLD $START" - continue - fi +# if [[ "$START" < "2024-09-01" ]]; then +# echo "TOO OLD $START" +# continue +# fi TIMERANGE="${START//-/}-${END//-/}" echo "Running hyperopt for $PAIR $REGIME with timerange $TIMERANGE" @@ -63,7 +63,7 @@ while read -r PAIR; do # COLUMNS=200 LINES=40 script -q -c " - freqtrade hyperopt --strategy $STRATEGIE --config user_data/config.json --hyperopt-loss OnlyProfitHyperOptLoss --timerange $TIMERANGE --timeframe 5m --spaces sell buy protection --pair $converted -e 80 -j7 + freqtrade hyperopt --strategy $STRATEGIE --config user_data/config.json --hyperopt-loss OnlyProfitHyperOptLoss --timerange $TIMERANGE --timeframe 5m --spaces sell buy --pair $converted -e 80 -j7 --print-all echo "Saved hyperopt output to $OUTPUT_JSON" diff --git a/tools/statistique/ewm.py b/tools/statistique/ewm.py new file mode 100644 index 0000000..f6074dd --- /dev/null +++ b/tools/statistique/ewm.py @@ -0,0 +1,41 @@ +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt + +np.random.seed(42) + +# Générer 100 valeurs simulant un prix réel avec fluctuations +t = np.arange(100) +trend = np.sin(t/10) * 2 # tendance ondulante +noise = np.random.randn(100) * 0.5 # bruit +prices = 50 + trend + noise # prix centré autour de 50 + +df = pd.DataFrame({"price": prices}) + +# Rolling simple sur 5 périodes +df["rolling5"] = df["price"].rolling(5).mean() + +# EMA plus réactive (span=5) +# EMA5 standard +df["ema5"] = df["price"].ewm(span=5, adjust=False).mean() + +# EMA5 “lissée” avec double application +df["ema5_smooth"] = df["price"].ewm(span=5, adjust=False).mean().ewm(span=5, adjust=False).mean() + + +# EMA plus lissée (span=20) +df["ema20"] = df["price"].ewm(span=20, adjust=False).mean() + +# Plot +plt.figure(figsize=(12,6)) +plt.plot(df["price"], label="Prix", color='black', alpha=0.6) +plt.plot(df["rolling5"], label="Rolling 5", linestyle="--", color='blue') +plt.plot(df["ema5"], label="EMA 5", color='red') +plt.plot(df["ema5_smooth"], label="EMA 5S", color='blue') +plt.plot(df["ema20"], label="EMA 20", color='green') +plt.title("Rolling vs Exponential Moving Average (prix réaliste)") +plt.xlabel("Période") +plt.ylabel("Prix") +plt.legend() +plt.grid(True) +plt.show() diff --git a/tools/statistique/savgol.py b/tools/statistique/savgol.py new file mode 100644 index 0000000..fee6a77 --- /dev/null +++ b/tools/statistique/savgol.py @@ -0,0 +1,27 @@ +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +from scipy.interpolate import make_interp_spline +from scipy.signal import savgol_filter + +np.random.seed(42) + +# Générer 100 valeurs simulant un prix réel avec fluctuations +t = np.arange(100) +trend = np.sin(t/10) * 2 # tendance ondulante +noise = np.random.randn(100) * 0.5 # bruit +prices = 50 + trend + noise # prix centré autour de 50 + +df = pd.DataFrame({"price": prices}) + +df["ema5"] = df["price"].ewm(span=5, adjust=False).mean() +# ATTENTION cela regarde dans le futur +df["ema5_savgol"] = savgol_filter(df["ema5"], window_length=21, polyorder=3) + +# Plot +# fenetre=21 points, poly order 3 +plt.plot(df["price"], alpha=0.5, label="Prix") +plt.plot(df["ema5_savgol"], label="EMA5 lissée Savitzky-Golay", color="red") +plt.legend() +plt.show() + diff --git a/tools/statistique/spline.py b/tools/statistique/spline.py new file mode 100644 index 0000000..53db0da --- /dev/null +++ b/tools/statistique/spline.py @@ -0,0 +1,35 @@ +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +from scipy.interpolate import make_interp_spline + +np.random.seed(42) + +# Générer 100 valeurs simulant un prix réel avec fluctuations +t = np.arange(100) +trend = np.sin(t/10) * 2 # tendance ondulante +noise = np.random.randn(100) * 0.5 # bruit +prices = 50 + trend + noise # prix centré autour de 50 + +df = pd.DataFrame({"price": prices}) + +df["ema5"] = df["price"].ewm(span=5, adjust=False).mean() + +x = np.arange(len(df)) +y = df["ema5"].values + +# Créer une nouvelle série de points x plus dense +x_smooth = np.linspace(x.min(), x.max(), 300) + +# Spline (B-spline) +spl = make_interp_spline(x, y, k=3) # k=3 pour cubic spline +y_smooth = spl(x_smooth) + +# Plot +plt.figure(figsize=(12,6)) +plt.plot(df["price"], label="Prix", alpha=0.5, color="black") +plt.plot(df["ema5"], label="EMA 5", color='blue') +plt.plot(x_smooth, y_smooth, label="EMA5 lissée Bézier", color="red") +plt.title("EMA5 très lisse avec spline") +plt.legend() +plt.show()