function loadJson(filename) {
fetch(`/read_json/${filename}`)
.then(res => res.json())
.then(data => {
const tabButtons = document.getElementById('tab-buttons');
const tabContents = document.getElementById('tab-contents');
tabButtons.innerHTML = '';
tabContents.innerHTML = '';
const isMulti = typeof data === 'object' && !Array.isArray(data);
if (isMulti) {
let count = 0
Object.entries(data).forEach(([name, content], index) => {
count = count + 1
if (name.includes("signals")) {
sessionStorage.setItem('signals', content);
addTab('signals', count, content)
}
if (name.includes("exited")) {
sessionStorage.setItem('exited', content);
addTab('exited', count, content)
}
if (name.includes("json")) {
sessionStorage.setItem('result_strategy', JSON.stringify(content.strategy));
sessionStorage.setItem('strategy_comparison', JSON.stringify(content.strategy_comparison));
addTab('result', count, JSON.stringify(content.strategy))
count = count + 1
addTab('comparaison', count, JSON.stringify(content.strategy_comparison))
}
if (name.includes("market")) {
sessionStorage.setItem('market', content);
addTab('market', count, content)
}
});
} else {
const div = document.createElement('div');
div.innerHTML = `
${JSON.stringify(data, null, 2)}`;
tabContents.appendChild(div);
}
})
.catch(err => alert("Erreur : " + err.message));
}
function jsonToUl(json) {
if (typeof json !== 'object' || json === null) return document.createTextNode(json);
const ul = document.createElement('ul');
for (const key in json) {
const li = document.createElement('li');
const value = json[key];
if (typeof value === 'object' && value !== null) {
li.textContent = key;
li.appendChild(jsonToUl(value)); // recurse
} else {
li.textContent = `${key}: ${value}`;
}
ul.appendChild(li);
}
return ul;
}
function jsonToTable(data, depth = 0) {
if (Array.isArray(data)) {
// Cas: tableau d'objets
if (data.length > 0 && typeof data[0] === 'object' && !Array.isArray(data[0])) {
const table = document.createElement('table');
table.border = 1;
// En-tête
const headerRow = document.createElement('tr');
Object.keys(data[0]).forEach(key => {
const th = document.createElement('th');
th.textContent = key;
headerRow.appendChild(th);
});
table.appendChild(headerRow);
// Lignes de données
data.forEach(item => {
const row = document.createElement('tr');
Object.values(item).forEach(value => {
const td = document.createElement('td');
td.textContent = (typeof value === 'object') ? JSON.stringify(value) : value;
row.appendChild(td);
});
table.appendChild(row);
});
return table;
} else {
// Liste simple : afficher horizontalement
const table = document.createElement('table');
table.border = 1;
const row = document.createElement('tr');
data.forEach(value => {
const td = document.createElement('td');
td.textContent = value;
row.appendChild(td);
});
table.appendChild(row);
return table;
}
} else if (typeof data === 'object' && data !== null) {
// Cas: objet clé/valeur
const table = document.createElement('table');
table.border = 1;
for (const key in data) {
const row = document.createElement('tr');
const tdKey = document.createElement('td');
tdKey.textContent = key;
row.appendChild(tdKey);
const tdValue = document.createElement('td');
const value = data[key];
if (typeof value === 'object') {
tdValue.appendChild(jsonToTable(value, depth + 1));
} else {
tdValue.textContent = value;
}
row.appendChild(tdValue);
table.appendChild(row);
}
return table;
} else {
const span = document.createElement('span');
span.textContent = data;
return span;
}
}
function addTab(name, index, content) {
const tabButtons = document.getElementById('tab-buttons');
const tabContents = document.getElementById('tab-contents');
const tabId = `tab-${index}`;
const li = document.createElement('li');
const btn = document.createElement('button');
btn.textContent = name;
btn.onclick = () => showTab(tabId);
li.appendChild(btn);
tabButtons.appendChild(li);
const div = document.createElement('div');
div.id = tabId;
div.className = 'tab-content';
div.style.display = index === 0 ? 'block' : 'none';
// div.innerHTML = Array.isArray(content) ? renderTable(content) : jsonToUl(JSON.parse(content));
ul = jsonToTable(JSON.parse(content))
div.appendChild(ul);
tabContents.appendChild(div);
}
function renderTable(rows) {
if (!rows.length) return "Aucune donnée
";
const headers = Object.keys(rows[0]);
const thead = `${headers.map(h => `| ${h} | `).join('')}
`;
const tbody = rows.map(r => `${headers.map(h => `| ${r[h]} | `).join('')}
`).join('');
return ``;
}
function showTab(tabId) {
document.querySelectorAll('.tab-content').forEach(div => {
div.style.display = 'none';
});
document.getElementById(tabId).style.display = 'block';
}
function graph() {
// Initialiser l'instance de graphique
var chart = echarts.init(document.getElementById('chart'));
// Définir les options du graphique
var option = {
title: {
text: 'Exemple de Graphique Linéaire'
},
tooltip: {},
legend: {
data: ['Ventes']
},
xAxis: {
data: ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim']
},
yAxis: {},
series: [{
name: 'Ventes',
type: 'line',
data: [5, 20, 36, 10, 10, 20, 30]
}]
};
// Utiliser les options pour afficher le graphique
chart.setOption(option);
}
function renderChart(data, filename, create_columns) {
let signals = JSON.parse(sessionStorage.getItem("signals"));
let exited = JSON.parse(sessionStorage.getItem("exited"));
let market = JSON.parse(sessionStorage.getItem("market"));
let result_strategy = JSON.parse(sessionStorage.getItem("result_strategy"));
let strategy_comparison = JSON.parse(sessionStorage.getItem("strategy_comparison"));
strategy_name = strategy_comparison[0].key;
result_of_strategy = result_strategy[strategy_name]
const cols = Object.keys(data[0])
if (create_columns === true) {
string = "";
const indicators = document.getElementById('indicators');
indicators.innerHTML = string
}
let totalPoints = data.length;
let visiblePoints = 100;
let startPercent = ((totalPoints - visiblePoints) / totalPoints) * 100;
// ECharts (Close price over time)
if (create_columns === true) {
var tab_element = document.getElementById('tab-contents')
var index_tabs = tab_element.childNodes.length
addTab('Graph', 'chart' /*+ index_tabs*/, "{}")
addTab('Données', 'data' /*+ index_tabs*/, JSON.stringify(data))
}
const chart = echarts.init(document.getElementById('tab-chart' /*_' + index_tabs*/), null,
{
width: window.innerWidth - 250,
height: window.innerHeight - 150
});
window.addEventListener('resize', function() {chart.resize();});
const series = [{
type: 'line',
name: 'Close',
data: data.map(d => d.close)
}]
marks = []
const options = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
}
for (var key in result_of_strategy.trades) {
var d = result_of_strategy.trades[key];
var date = new Date(d.open_date);
marks.push({
name: 'Buy',
coord: [date.toLocaleString('fr-FR', options), d.open_rate],
value: d.open_rate,
itemStyle: {
color: 'rgb(0,90,0)'
}
})
for (var key2 in d.orders) {
var order = d.orders[key2]
date = new Date(order.order_filled_timestamp);
marks.push({
name: 'Buy',
coord: [date.toLocaleString('fr-FR', options), order.safe_price],
value: order.safe_price,
itemStyle: {
color: 'rgb(0,0,90)'
}
})
}
date = new Date(d.close_date);
marks.push({
name: 'Sell',
coord: [date.toLocaleString('fr-FR', options), d.close_rate],
value: d.close_rate,
itemStyle: {
color: 'rgb(90,0,0)'
}
})
}
// Bougies
const result = data.map(({ open, close, low, high }) => [open, close, low, high]);
series.push({
type: 'candlestick',
data: result,
yAxisIndex: 0,
itemStyle: {
color: '#00da3c', // hausse (close > open) => vert
color0: '#ec0000', // baisse (close < open) => rouge
borderColor: '#008F28',
borderColor0: '#8A0000'
},
markPoint: {
label: {
formatter: function (param) {
return param != null ? Math.round(param.value) + '' : '';
}
},
data: marks,
tooltip: {
formatter: function (param) {
return param.name + '
' + (param.data.coord || '');
}
}
}
}
)
// 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',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 2,
itemStyle: {
color: '#7fbe9e'
},
emphasis: {
itemStyle: {
color: '#140'
}
},
data: data.map(d => d.volume)
})
for (var key in cols) {
var value = cols[key];
element=document.getElementById(value)
if (element) {
if (element.checked) {
// RSI
if (value.toLowerCase().indexOf('rsi') >= 0) {
series.push({
name: value,
type: 'line',
yAxisIndex: 1,
lineStyle: { color: stringToColor(value) },
data: data.map(d => d[value])
})
}
else if (value.toLowerCase().indexOf('pct') >= 0 | value.indexOf('percent') >= 0) {
series.push({
name: value,
type: 'line',
yAxisIndex: 3,
lineStyle: { color: stringToColor(value) },
data: data.map(d => d[value])
})
}
else {
series.push({
name: value,
type: 'line',
yAxisIndex: 0,
data: data.map(d => d[value]),
smooth: true,
lineStyle: { color: stringToColor(value) }
})
}
}
}
}
var option = {
title: {
text: strategy_name
},
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.toLocaleString('fr-FR', options); // ex : 07/05/2025
})
},
{
type: 'category',
data: data.map(d => {
const date = new Date(d.date);
return date.toLocaleString('fr-FR', options); // ex : 07/05/2025
}),
gridIndex: 1
}
],
yAxis: [
// yAxisIndex: 0
{
type: 'value',
min: 'dataMin', // <-- commence à la plus petite valeur
max: 'dataMax' // <-- adapte aussi le maximum
},
// yAxisIndex: 1
{
type: 'value', // Axe pour le RSI
name: 'RSI',
position: 'right',
min: 0,
max: 100
},
// yAxisIndex: 2
{
gridIndex: 1,
min: 'dataMin', // <-- commence à la plus petite valeur
max: 'dataMax' // <-- adapte aussi le maximum
},
// yAxisIndex: 3
{
type: 'value', // Axe pour le RSI
name: 'PCT',
position: 'right',
min: -1,
max: 1
},
],
grid: [
{ left: '10%', right: '8%', height: '70%' },
{ left: '10%', right: '8%', top: '80%', height: '10%' }
],
series: series
}
chart.setOption(option)
document.querySelectorAll('.indicators 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');
}
});
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) {
const element = document.getElementById('current_file_name');
element.value = filename
fetch(`/read_feather/${filename}`)
.then(response => response.json())
.then(data => {
renderChart(data, filename, true)
initReport(data)
})
}
function initReport(data) {
string = ""
const indicators = document.getElementById('indicatorsReport')
indicators.innerHTML = string
}
function generateModel() {
const element = document.getElementById('current_file_name');
let filename = element.value
const indicators = Array.from(selectedReportIndicators).join(',');
fetch(`/generate_model?filename=${filename}`)
.then(alert('Generation en cours'));
}
function generateReport() {
const element = document.getElementById('current_file_name');
let filename = element.value
const indicators = Array.from(selectedReportIndicators).join(',');
fetch(`/generate_report?filename=${filename}&indicators=${indicators}`)
.then(alert('Generation en cours'));
}
let selectedIndicators = new Set();
let selectedReportIndicators = new Set();
function toggleIndicator(checkbox) {
const indicator = checkbox.value;
if (checkbox.checked) {
selectedIndicators.add(indicator);
} else {
selectedIndicators.delete(indicator);
}
loadChartWithIndicators();
}
function toggleReportIndicator(checkbox) {
const indicator = checkbox.value;
if (checkbox.checked) {
selectedReportIndicators.add(indicator);
} else {
selectedReportIndicators.delete(indicator);
}
}
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 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%)`;
}
function init() {
const dialog = document.getElementById('indicatorDialog');
document.getElementById('openIndicatorsBtn').addEventListener('click', () => {
dialog.showModal();
});
document.getElementById('closeIndicatorsBtn').addEventListener('click', () => {
dialog.close();
});
const dialogReport = document.getElementById('indicatorReportDialog');
document.getElementById('openIndicatorsReportBtn').addEventListener('click', () => {
dialogReport.showModal();
});
document.getElementById('closeIndicatorsReportBtn').addEventListener('click', () => {
dialogReport.close();
});
}