From 16783a79be3f2af1dd07ddbd8a708001f5361c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Delacotte?= Date: Sun, 11 May 2025 16:53:01 +0200 Subject: [PATCH] =?UTF-8?q?Ajout=20analyse=20de=20donn=C3=A9es=20par=20r?= =?UTF-8?q?=C3=A9seau=20neuronal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 +- README.md | 35 ++++++++++++++++++--- requirements.txt | 7 ++++- src/app.py | 62 ++++++++++++++++++++++++++++++++++++++ src/static/js/functions.js | 34 ++++++++++++++++++++- src/templates/index.html | 2 ++ src/templates/model.html | 11 +++++++ 7 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 src/templates/model.html diff --git a/Dockerfile b/Dockerfile index b5c8e5b..a70bbeb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,5 +17,5 @@ COPY . /src EXPOSE 5000 # lancer l'application Python -CMD python3 app.py +CMD ["python", "app.py"] diff --git a/README.md b/README.md index 53240c8..77f7d34 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,39 @@ # FreqStats -## Construction +## Adaptation de la stratégie -docker build -t flask-web-app . + ### Génération du dataframe en fichier + + Ajouter à la fin de populate_buy_trend : + + if self.dp.runmode.value in ('backtest'): + dataframe.to_feather(f"user_data/data/binance/{metadata['pair'].replace('/', '_')}_df.feather") -## Lancement + ### Lancer un backtest avec export signals + + freqtrade backtesting --strategy Zeus_8_3_2_B_4_2 --config config.json --timerange 20250423-20250426 --timeframe 5m --breakdown week --enable-protections --export signals --pairs BTC/USDT -docker run -it -p 5000:5000 -v $(pwd)/src/:/src -v /home/jerome/Perso/freqtradeDocker/user_data/:/mnt/external flask-web-app bash +# Docker + ## Construction + + docker build -t flask-web-app . + + ## Lancement + + docker run -it -p 5000:5000 -v $(pwd)/src/:/src -v /home/jerome/Perso/freqtradeDocker/user_data/:/mnt/external flask-web-app bash + + puis : python3 app.py + +# Application Web + + Url : http://127.0.0.1:5000/ + + Choisir un backtest dans la liste + + Choisir le fichier généré par le backtest par la stratégie + + Cliquer sur les boutons -puis : python3 app.py ## librairies diff --git a/requirements.txt b/requirements.txt index 7669346..dad7a1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,9 @@ Werkzeug==2.2.3 joblib==1.4.2 pyarrow pandas-ta -ydata-profiling \ No newline at end of file +ydata-profiling +tensorflow +keras +scikit-learn +pydot +graphviz \ No newline at end of file diff --git a/src/app.py b/src/app.py index 80bff17..f48d625 100644 --- a/src/app.py +++ b/src/app.py @@ -8,6 +8,13 @@ import joblib from io import TextIOWrapper from ydata_profiling import ProfileReport +# model +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import StandardScaler +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import Dense +from tensorflow.keras.utils import plot_model + app = Flask(__name__) FREQTRADE_USERDATA_DIR = '/mnt/external' @@ -121,6 +128,43 @@ def read_feather(filename): # dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) # dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + # Choisir les colonnes techniques comme variables d'entrée (X) + feature_cols = ['rsi', 'sma20', 'sma5_1h', 'volume'] + df = dataframe + # Variable cible + df['target'] = df['futur_price_1h'] + + # Supprimer les lignes avec des NaN + df.dropna(subset=feature_cols + ['target'], inplace=True) + + X = df[feature_cols].values + y = df['target'].values + + # Normalisation + scaler = StandardScaler() + X = scaler.fit_transform(X) + + # Split + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) + + # Modèle + model = Sequential([ + Dense(64, input_dim=X.shape[1], activation='relu'), + Dense(32, activation='relu'), + Dense(1) # Prédiction continue + ]) + + model.compile(optimizer='adam', loss='mse', metrics=['mae']) + + # Entraînement + model.fit(X_train, y_train, epochs=50, batch_size=32, validation_data=(X_test, y_test)) + + loss, mae = model.evaluate(X_test, y_test) + print(f"Erreur moyenne absolue : {mae:.4f}") + + model.summary() + + plot_model(model, show_shapes=True, show_layer_names=True, to_file=FREQTRADE_USERDATA_DIR + "/reports/model.png") return dataframe.to_json(orient="records") except Exception as e: print(e) @@ -185,6 +229,24 @@ def get_chart_data(): return df.to_json(orient="records") #jsonify(chart_data) +@app.route('/model') +def show_model(): + # Créer un exemple de modèle si non encore généré + model_path = FREQTRADE_USERDATA_DIR + "/reports/model.png" + if not os.path.exists(model_path): + model = Sequential([ + Dense(64, input_shape=(6,), activation='relu'), + Dense(32, activation='relu'), + Dense(1) + ]) + plot_model(model, to_file=model_path, show_shapes=True, show_layer_names=True) + return render_template('model.html', model_image=model_path) + +# Route pour servir les fichiers statiques (optionnelle si bien configuré) +@app.route('/static/') +def static_files(filename): + return send_from_directory('static', filename) + if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000) diff --git a/src/static/js/functions.js b/src/static/js/functions.js index 474fa70..6156a18 100644 --- a/src/static/js/functions.js +++ b/src/static/js/functions.js @@ -329,6 +329,30 @@ function renderChart(data, filename, create_columns) { } ) + // Achat + series.push({ + name: 'Buy', + type: 'scatter', + symbolSize: 10, + itemStyle: { + color: '#00aa00' + }, +// label: { +// show: true, +// position: 'top', // ou 'right', 'inside', etc. +// formatter: function (param) { +// return param.value[2]; // ou par ex. param.value[1] pour afficher le prix +// }, +// fontSize: 12, +// color: '#000' +// }, + data: data + .filter(d => d.enter_long === 1) + .map(d => { + const date = new Date(d.date).toLocaleString('fr-FR', options); + return [date, d.close, d.enter_tag]; + }) + }) // Volume series.push({ name: 'Volume', @@ -349,6 +373,7 @@ function renderChart(data, filename, create_columns) { for (var key in cols) { var value = cols[key]; element=document.getElementById(value) + if (element) { if (element.checked) { @@ -489,7 +514,14 @@ function renderChart(data, filename, create_columns) { li.classList.add('is-1d'); } }); - + document.querySelectorAll('.indicatorsReport li').forEach(li => { + if (li.textContent.trim().endsWith('_1h')) { + li.classList.add('is-1h'); + } + if (li.textContent.trim().endsWith('_1d')) { + li.classList.add('is-1d'); + } + }); } function loadFeather(filename) { diff --git a/src/templates/index.html b/src/templates/index.html index 5380c16..d0e1ff6 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -52,6 +52,8 @@ + Voir le modèle +
diff --git a/src/templates/model.html b/src/templates/model.html new file mode 100644 index 0000000..a05066d --- /dev/null +++ b/src/templates/model.html @@ -0,0 +1,11 @@ + + + + Modèle de réseau + + +

Structure du modèle

+ Modèle Keras +
Retour + +