Affichage graph des données / sélection des indicateurs

This commit is contained in:
Jérôme Delacotte
2025-05-07 20:48:40 +02:00
parent 5861697489
commit 4d7c8d4ee9
5 changed files with 233 additions and 68 deletions

View File

@@ -6,7 +6,6 @@ docker build -t flask-web-app .
## Lancement ## Lancement
docker run -it -p 5000:5000 -v $(pwd)/src/:/src -v /home/jerome/Perso/freqtradeDocker/user_data/:/mnt/external flask-web-app bash
docker run -it -p 5000:5000 -v $(pwd)/src/:/src -v /home/jerome/Perso/freqtradeDocker/user_data/backtest_results:/mnt/external flask-web-app bash
puis : python3 app.py puis : python3 app.py

View File

@@ -8,3 +8,4 @@ Flask==2.2.3
Werkzeug==2.2.3 Werkzeug==2.2.3
joblib==1.4.2 joblib==1.4.2
pyarrow pyarrow
pandas-ta

View File

@@ -1,4 +1,4 @@
from flask import Flask, jsonify, abort, render_template, send_from_directory from flask import Flask, jsonify, abort, render_template, send_from_directory,request
import pandas as pd import pandas as pd
import json import json
import zipfile import zipfile
@@ -102,14 +102,59 @@ def read_json(filename):
def read_feather(filename): def read_feather(filename):
path = os.path.join(FREQTRADE_USERDATA_DIR + "/data/binance/", filename) path = os.path.join(FREQTRADE_USERDATA_DIR + "/data/binance/", filename)
try: try:
print(path) dataframe = pd.read_feather(path)
df = pd.read_feather(path) # dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200)
print(df) # dataframe['min12'] = talib.MIN(dataframe['close'], timeperiod=12)
return df.to_json(orient="records") #
# dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50)
# dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200)
# dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200)
return dataframe.to_json(orient="records")
except Exception as e: except Exception as e:
print(e) print(e)
return jsonify({"error": str(e)}), 500 return jsonify({"error": str(e)}), 500
@app.route('/get_chart_data')
def get_chart_data():
filename = request.args.get('filename', '')
path = os.path.join(FREQTRADE_USERDATA_DIR + "/data/binance/", filename)
print(path)
indicators = request.args.get('indicators', '').split(',')
df = pd.read_feather(path)
print(df)
# # Calculs conditionnels
# if 'sma' in indicators:
# df['sma'] = df['close'].rolling(window=20).mean()
# if 'rsi' in indicators:
# delta = df['close'].diff()
# gain = delta.where(delta > 0, 0)
# loss = -delta.where(delta < 0, 0)
# avg_gain = gain.rolling(14).mean()
# avg_loss = loss.rolling(14).mean()
# rs = avg_gain / avg_loss
# df['rsi'] = 100 - (100 / (1 + rs))
# if 'bollinger' in indicators:
# sma = df['close'].rolling(window=20).mean()
# std = df['close'].rolling(window=20).std()
# df['bb_upper'] = sma + 2 * std
# df['bb_lower'] = sma - 2 * std
#
# df = df.dropna()
# Simplifier les données pour le JSON
# chart_data = {
# 'date': df['date'].astype(str).tolist(),
# 'close': df['close'].tolist(),
# 'sma': df.get('sma', pd.Series()).tolist(),
# 'rsi': df.get('rsi', pd.Series()).tolist(),
# 'bb_upper': df.get('bb_upper', pd.Series()).tolist(),
# 'bb_lower': df.get('bb_lower', pd.Series()).tolist(),
# }
return df.to_json(orient="records") #jsonify(chart_data)
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000) app.run(debug=True, host='0.0.0.0', port=5000)

View File

@@ -79,18 +79,31 @@ function graph() {
chart.setOption(option); chart.setOption(option);
} }
function loadFeather(filename) { function renderChart(data, filename, create_columns) {
fetch(`/read_feather/${filename}`)
.then(response => response.json())
.then(data => {
// Table // Table
const table = document.getElementById('data-table'); // const table = document.getElementById('data-table');
if (data.length > 0) { // if (data.length > 0) {
let header = '<tr>' + Object.keys(data[0]).map(k => `<th>${k}</th>`).join('') + '</tr>'; // let header = '<tr>' + Object.keys(data[0]).map(k => `<th>${k}</th>`).join('') + '</tr>';
let rows = data.map(row => '<tr>' + Object.values(row).map(v => `<td>${v}</td>`).join('') + '</tr>'); // let rows = data.map(row => '<tr>' + Object.values(row).map(v => `<td>${v}</td>`).join('') + '</tr>');
table.innerHTML = header + rows.join(''); // table.innerHTML = header + rows.join('');
// }
const cols = Object.keys(data[0])
if (create_columns === true) {
string = "<ul class='indicators'>" + Object.keys(data[0]).map(cols => `<li><label><input id="${cols}" type="checkbox" value="${cols}" onchange="toggleIndicator(this)">${cols}</label></li>`).join('') + "</ul>"
const indicators = document.getElementById('indicators');
indicators.innerHTML = string
} }
// const label = document.createElement('label');
// label.innerHTML = `<input type="checkbox" value="${col}"> ${col}<br>`;
// indicators.appendChild(label);
let totalPoints = data.length;
let visiblePoints = 100;
let startPercent = ((totalPoints - visiblePoints) / totalPoints) * 100;
// ECharts (Close price over time) // ECharts (Close price over time)
const chart = echarts.init(document.getElementById('chart')); const chart = echarts.init(document.getElementById('chart'));
// const option = { // const option = {
@@ -108,6 +121,45 @@ function loadFeather(filename) {
// }] // }]
// }; // };
// specify chart configuration item and data // specify chart configuration item and data
const series = [{
type: 'line',
name: 'Close',
data: data.map(d => d.close)
}]
// df_ohlc = data
// df_ohlc['date'] = pd.to_datetime(data['date']).dt.strftime('%Y-%m-%d')
// df_ohlc = data[['date', 'open', 'close', 'low', 'high']]
// series.push({
// type: 'candlestick',
// data: {
// 'dates': df_ohlc['date'].tolist(),
// 'ohlc': df_ohlc[['open', 'close', 'low', 'high']].values.tolist()
// },
// itemStyle: {
// color: '#ec0000', // bougie haussière (fermée plus haut que ouverte)
// color0: '#00da3c', // bougie baissière
// borderColor: '#8A0000',
// borderColor0: '#008F28'
// }
// }
// )
for (var key in cols) {
var value = cols[key];
element=document.getElementById(value)
if (element) {
if (element.checked) {
series.push({
name: value,
type: 'line',
data: data.map(d => d[value]),
smooth: true,
lineStyle: { color: stringToColor(value) }
})
}
}
}
var option = { var option = {
title: { title: {
text: filename text: filename
@@ -134,9 +186,21 @@ function loadFeather(filename) {
} }
} }
}, },
// legend: { dataZoom: [
// data:[] {
// }, type: 'slider', // slider visible en bas
start: startPercent,
end: 100
},
{
type: 'inside', // zoom avec la molette de la souris
start: startPercent,
end: 100
}
],
legend: {
data: cols, // Affiche les noms de chaque série avec leur couleur
},
xAxis: { xAxis: {
type: 'category', type: 'category',
data: data.map(d => { data: data.map(d => {
@@ -145,15 +209,65 @@ function loadFeather(filename) {
}) })
}, },
yAxis: { yAxis: {
type: 'value' type: 'value',
min: 'dataMin', // <-- commence à la plus petite valeur
max: 'dataMax' // <-- adapte aussi le maximum
}, },
series: [{ series: series
type: 'line', }
name: 'Close',
data: data.map(d => d.close) chart.setOption(option)
}]
};
chart.setOption(option);
});
} }
function loadFeather(filename) {
const element = document.getElementById('current_file_name');
element.value = filename
fetch(`/read_feather/${filename}`)
.then(response => response.json())
.then(data => renderChart(data, filename, true) );
}
let selectedIndicators = new Set();
function toggleIndicator(checkbox) {
const indicator = checkbox.value;
if (checkbox.checked) {
selectedIndicators.add(indicator);
} else {
selectedIndicators.delete(indicator);
}
loadChartWithIndicators();
}
function loadChartWithIndicators() {
const element = document.getElementById('current_file_name');
let filename = element.value
const indicators = Array.from(selectedIndicators).join(',');
fetch(`/get_chart_data?filename=${filename}&indicators=${indicators}`)
.then(response => response.json())
.then(data => renderChart(data, filename, false));
}
//function createListeners() {
// // Écouteurs d'événement
// document.querySelectorAll('input[type=checkbox]').forEach(cb => {
// cb.addEventListener('change', () => {
// chart.setOption({ series: getSeries() });
// });
// });
//}
function stringToColor(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
// Convertir le hash en une teinte HSL (0-360°)
const hue = hash % 360;
// Saturation et luminosité réglées pour contraste
return `hsl(${hue}, 70%, 50%)`;
}

View File

@@ -29,12 +29,18 @@
</div> </div>
</div> </div>
<div id="indicators">
<label><input id="smaCheckbox" type="checkbox" value="sma" onchange="toggleIndicator(this)"> SMA</label>
<label><input id="rsiCheckbox" type="checkbox" value="rsi" onchange="toggleIndicator(this)"> RSI</label>
<label><input id="bbCheckbox" type="checkbox" value="bollinger" onchange="toggleIndicator(this)"> Bollinger</label>
</div>
<div id="chart" style="width: 100%; height: 1024px;"></div> <div id="chart" style="width: 100%; height: 1024px;"></div>
<h3>Données</h3>
<table border="1" id="data-table"></table> <table border="1" id="data-table"></table>
<!--<div id="data-table" style="margin-left: 220px; padding: 20px;"></div>--> <!--<div id="data-table" style="margin-left: 220px; padding: 20px;"></div>-->
<input id="current_file_name" hidden value="">
<!-- Colonne droite avec le graphique (remplacez avec votre graphique) <!-- Colonne droite avec le graphique (remplacez avec votre graphique)
<div class="graph-container"> <div class="graph-container">