diff --git a/README.md b/README.md
index a461076..95fe459 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,6 @@ docker build -t flask-web-app .
## Lancement
-
-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
+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
diff --git a/requirements.txt b/requirements.txt
index 8d8fbda..615cb11 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,3 +8,4 @@ Flask==2.2.3
Werkzeug==2.2.3
joblib==1.4.2
pyarrow
+pandas-ta
diff --git a/src/app.py b/src/app.py
index 04cbdac..44afb24 100644
--- a/src/app.py
+++ b/src/app.py
@@ -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 json
import zipfile
@@ -102,14 +102,59 @@ def read_json(filename):
def read_feather(filename):
path = os.path.join(FREQTRADE_USERDATA_DIR + "/data/binance/", filename)
try:
- print(path)
- df = pd.read_feather(path)
- print(df)
- return df.to_json(orient="records")
+ dataframe = pd.read_feather(path)
+ # dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200)
+ # dataframe['min12'] = talib.MIN(dataframe['close'], timeperiod=12)
+ #
+ # 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:
print(e)
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__':
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 b1898f6..a6c9f68 100644
--- a/src/static/js/functions.js
+++ b/src/static/js/functions.js
@@ -79,20 +79,33 @@ function graph() {
chart.setOption(option);
}
-function loadFeather(filename) {
- fetch(`/read_feather/${filename}`)
- .then(response => response.json())
- .then(data => {
- // Table
- const table = document.getElementById('data-table');
- if (data.length > 0) {
- let header = '
' + Object.keys(data[0]).map(k => `| ${k} | `).join('') + '
';
- let rows = data.map(row => '' + Object.values(row).map(v => `| ${v} | `).join('') + '
');
- table.innerHTML = header + rows.join('');
- }
+function renderChart(data, filename, create_columns) {
- // ECharts (Close price over time)
- const chart = echarts.init(document.getElementById('chart'));
+ // Table
+// const table = document.getElementById('data-table');
+// if (data.length > 0) {
+// let header = '' + Object.keys(data[0]).map(k => `| ${k} | `).join('') + '
';
+// let rows = data.map(row => '' + Object.values(row).map(v => `| ${v} | `).join('') + '
');
+// table.innerHTML = header + rows.join('');
+// }
+ const cols = Object.keys(data[0])
+ if (create_columns === true) {
+ string = ""
+ const indicators = document.getElementById('indicators');
+ indicators.innerHTML = string
+ }
+
+
+// const label = document.createElement('label');
+// label.innerHTML = ` ${col}
`;
+// indicators.appendChild(label);
+
+ let totalPoints = data.length;
+ let visiblePoints = 100;
+ let startPercent = ((totalPoints - visiblePoints) / totalPoints) * 100;
+
+ // ECharts (Close price over time)
+ const chart = echarts.init(document.getElementById('chart'));
// const option = {
// xAxis: {
// type: 'category',
@@ -108,52 +121,153 @@ function loadFeather(filename) {
// }]
// };
// specify chart configuration item and data
- var option = {
- title: {
- text: filename
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- toolbox: {
- feature: {
- dataView: {show: true, readOnly: false},
- restore: {show: true},
- saveAsImage: {show: true}
- }
- },
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'cross',
- crossStyle: {
- color: '#999'
- }
- }
- },
- // legend: {
- // data:[]
- // },
- xAxis: {
- type: 'category',
- data: data.map(d => {
- const date = new Date(d.date);
- return date.toLocaleDateString('fr-FR'); // ex : 07/05/2025
- })
- },
- yAxis: {
- type: 'value'
- },
- series: [{
- type: 'line',
- name: 'Close',
- data: data.map(d => d.close)
- }]
- };
- chart.setOption(option);
- });
+ 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 = {
+ title: {
+ text: filename
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true
+ },
+ toolbox: {
+ feature: {
+ dataView: {show: true, readOnly: false},
+ restore: {show: true},
+ saveAsImage: {show: true}
+ }
+ },
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'cross',
+ crossStyle: {
+ color: '#999'
+ }
+ }
+ },
+ dataZoom: [
+ {
+ 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: {
+ type: 'category',
+ data: data.map(d => {
+ const date = new Date(d.date);
+ return date.toLocaleDateString('fr-FR'); // ex : 07/05/2025
+ })
+ },
+ yAxis: {
+ type: 'value',
+ min: 'dataMin', // <-- commence à la plus petite valeur
+ max: 'dataMax' // <-- adapte aussi le maximum
+ },
+ series: series
+ }
+
+ 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%)`;
+}
+
diff --git a/src/templates/index.html b/src/templates/index.html
index a2c13d3..c9cf43d 100644
--- a/src/templates/index.html
+++ b/src/templates/index.html
@@ -29,12 +29,18 @@
+
+
+
+
+
-Données
+
+