Files
BoursePython/app/static/js/charts.js
T

201 lines
6.7 KiB
JavaScript
Raw Normal View History

2026-08-03 21:09:33 +02:00
/**
* charts.js — Graphiques Chart.js pour l'analyse technique.
* - Graphique principal : cours de clôture + MM20/MM50/MM200 (toggle)
* - Graphique volume : histogramme + moyenne 20j
* - Graphique volatilité : courbe 20j
*
* Utilise un axe X catégoriel (dates en labels) pour éviter toute
* dépendance à un adapter temporel.
*/
document.addEventListener("DOMContentLoaded", () => {
const dataCoursEl = document.getElementById("data-cours");
const dataVolEl = document.getElementById("data-volumes");
const dataVolatEl = document.getElementById("data-volatilite");
if (!dataCoursEl) return; // pas de données (page d'accueil)
let dataCours, dataVol, dataVolat;
try {
dataCours = JSON.parse(dataCoursEl.textContent);
dataVol = JSON.parse(dataVolEl.textContent);
dataVolat = JSON.parse(dataVolatEl.textContent);
} catch (e) {
console.error("Erreur parsing JSON graphiques:", e);
return;
}
console.log("[charts] données chargées:",
"cours=" + dataCours.dates.length + "pts",
"vol=" + dataVol.volumes.length + "pts");
// --- Configuration globale Chart.js ---
if (typeof Chart === "undefined") {
console.error("[charts] Chart.js non chargé");
return;
}
Chart.defaults.color = "#9ca3af";
Chart.defaults.borderColor = "#1f2937";
Chart.defaults.font.family = "system-ui, sans-serif";
const labels = dataCours.dates;
// ===================== GRAPHIQUE PRINCIPAL =====================
const datasetsCours = [
{
label: "Cours",
data: dataCours.close,
borderColor: "#60a5fa",
backgroundColor: "rgba(96, 165, 250, 0.1)",
borderWidth: 1.5,
pointRadius: 0,
fill: true,
tension: 0.1,
},
];
if (dataCours.MM20) {
datasetsCours.push({
label: "MM20", data: dataCours.MM20, borderColor: "#3b82f6",
borderWidth: 1.2, pointRadius: 0, fill: false, tension: 0.1, hidden: false, id: "mm20",
});
}
if (dataCours.MM50) {
datasetsCours.push({
label: "MM50", data: dataCours.MM50, borderColor: "#10b981",
borderWidth: 1.2, pointRadius: 0, fill: false, tension: 0.1, hidden: false, id: "mm50",
});
}
if (dataCours.MM200) {
datasetsCours.push({
label: "MM200", data: dataCours.MM200, borderColor: "#f59e0b",
borderWidth: 1.2, pointRadius: 0, fill: false, tension: 0.1, hidden: false, id: "mm200",
});
}
const ctxCours = document.getElementById("chartCours");
if (ctxCours) {
const chartCours = new Chart(ctxCours, {
type: "line",
data: { labels: labels, datasets: datasetsCours },
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { intersect: false, mode: "index" },
scales: {
x: {
grid: { color: "#1f2937" },
ticks: { maxTicksLimit: 10, autoSkip: true },
},
y: {
position: "right",
grid: { color: "#1f2937" },
ticks: { callback: v => v.toFixed(2) },
},
},
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: "#111827", borderColor: "#374151", borderWidth: 1,
callbacks: { label: ctx => `${ctx.dataset.label}: ${ctx.parsed.y !== null ? ctx.parsed.y.toFixed(2) : 'N/A'}` },
},
},
},
});
document.getElementById("toggle-mm20")?.addEventListener("change", e => {
const ds = chartCours.data.datasets.find(d => d.id === "mm20");
if (ds) ds.hidden = !e.target.checked;
chartCours.update();
});
document.getElementById("toggle-mm50")?.addEventListener("change", e => {
const ds = chartCours.data.datasets.find(d => d.id === "mm50");
if (ds) ds.hidden = !e.target.checked;
chartCours.update();
});
document.getElementById("toggle-mm200")?.addEventListener("change", e => {
const ds = chartCours.data.datasets.find(d => d.id === "mm200");
if (ds) ds.hidden = !e.target.checked;
chartCours.update();
});
}
// ===================== GRAPHIQUE VOLUME =====================
const ctxVol = document.getElementById("chartVolume");
if (ctxVol) {
const volColors = dataVol.volumes.map((v, i) => {
const vm = dataVol.moyenne_20j[i];
return vm && v > vm * 2 ? "#f59e0b" : "#3b82f6";
});
new Chart(ctxVol, {
type: "bar",
data: {
labels: dataVol.dates,
datasets: [
{ label: "Volume", data: dataVol.volumes, backgroundColor: volColors, borderWidth: 0, order: 2 },
{
label: "Moyenne 20j", type: "line", data: dataVol.moyenne_20j,
borderColor: "#10b981", borderWidth: 1.5, pointRadius: 0, fill: false, tension: 0.1, order: 1,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { intersect: false, mode: "index" },
scales: {
x: { display: false },
y: {
position: "right", grid: { color: "#1f2937" },
ticks: { callback: v => v >= 1e6 ? (v / 1e6).toFixed(1) + "M" : v >= 1e3 ? (v / 1e3).toFixed(0) + "K" : v },
},
},
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: "#111827", borderColor: "#374151", borderWidth: 1,
callbacks: { label: ctx => ctx.dataset.label + ": " + (ctx.parsed.y !== null ? ctx.parsed.y.toLocaleString("fr-FR") : "N/A") },
},
},
},
});
}
// ===================== GRAPHIQUE VOLATILITÉ =====================
const ctxVolat = document.getElementById("chartVolatilite");
if (ctxVolat) {
new Chart(ctxVolat, {
type: "line",
data: {
labels: dataVolat.dates,
datasets: [
{
label: "Volatilité 20j", data: dataVolat.volatilite_20j,
borderColor: "#f59e0b", backgroundColor: "rgba(245, 158, 11, 0.1)",
borderWidth: 1.5, pointRadius: 0, fill: true, tension: 0.2,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { intersect: false, mode: "index" },
scales: {
x: { grid: { color: "#1f2937" }, ticks: { maxTicksLimit: 10, autoSkip: true } },
y: { position: "right", grid: { color: "#1f2937" }, ticks: { callback: v => v.toFixed(1) + "%" } },
},
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: "#111827", borderColor: "#374151", borderWidth: 1,
callbacks: { label: ctx => "Volatilité: " + (ctx.parsed.y !== null ? ctx.parsed.y.toFixed(2) : "N/A") + "%" },
},
},
},
});
}
console.log("[charts] graphiques initialisés");
});