Charts-Tab komplett entfernt (User-Vorgabe)

Entfernt: Tab-Button, #view-charts, der gesamte app.js-Chart-Block
(Kartenaufbau, renderChart, loadCharts, 20-s-Refresh, Resize-Handler), die
Chart-CSS-Regeln, die vendorte Bibliothek
web/lightweight-charts.standalone.production.js (164 KB) samt script-Tag - und
die Backend-Kette dahinter: /api/bars und engine.get_bars (60 Zeilen). Beide
existierten ausschliesslich fuer dieses Tab; geprueft, dass es keinen anderen
Aufrufer gibt.

Nicht betroffen (sahen nur aehnlich aus): die Chartmuster-Karte (Verdict-Gewicht
0,25, im Tab "SIG Live"), der MQL5-Indikator samt sr_levels.csv-Export (eigener
Pfad ueber _write_levels_file) und die Analyse-Module structure/patterns/cone -
die speisen den Snapshot, nicht das Chart. Der MT5-Chart bleibt voll bedient.

Ein veralteter Kommentar in trader.py, der auf engine.get_bars verwies, wurde
auf core/gaps.py umgehaengt.

Verifiziert am laufenden Server: /api/bars -> 404, Bibliothek -> 404,
data-view="charts" und id="view-charts" nicht mehr im ausgelieferten HTML,
6 Tabs zu 6 Views paarig, keine verwaisten JS-Referenzen, Klammern-Balance ok,
keine Fehler im Log. v=142.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-05 23:27:24 +02:00
co-authored by Claude Opus 5
parent 6ff3c15b1d
commit a1a5d560b9
8 changed files with 24 additions and 394 deletions
-137
View File
@@ -1423,7 +1423,6 @@ function showView(name) {
if (name === "logs") loadLogs();
if (name === "stats") loadStats();
if (name === "news") loadNews();
if (name === "charts") loadCharts();
}
// Nur echte View-Tabs registrieren — NICHT den Mute-Button (der trägt .tab nur
// fürs Styling; ohne [data-view]-Filter überschrieb das hier seinen onclick und
@@ -1572,146 +1571,10 @@ async function loadNews() {
} catch (e) { bEl.innerHTML = '<div class="down">Netzwerkfehler</div>'; }
}
// ── Charts (Lightweight Charts): M1/M5/M15/M30/H1 untereinander mit Trend ──
const CHART_TFS = ["M1", "M5", "M15", "M30", "H1"];
const _charts = {}; // tf → {chart, candle, s12, s50}
const _TREND_TXT = { up: ["▲ Aufwärts", "up"], down: ["▼ Abwärts", "down"], flat: ["→ Seitwärts", "flat"] };
function _ensureChartCard(tf) {
if (document.getElementById("chart-" + tf)) return;
const card = document.createElement("div");
card.className = "chart-card"; card.id = "chart-" + tf;
card.innerHTML =
`<div class="chart-head"><span class="chart-tf">${tf}</span>` +
`<span class="chart-trend" id="ctr-${tf}">—</span>` +
`<span class="chart-last" id="clast-${tf}">—</span></div>` +
`<div class="chart-cv" id="ccv-${tf}"></div>`;
$("charts-body").appendChild(card);
}
function _makeChart(tf) {
const el = document.getElementById("ccv-" + tf);
// Explizite Größe statt autoSize: autoSize hängt an ResizeObserver und lieferte
// auf Mobil-Browsern teils 0×0-Charts (leerer Tab). clientWidth ist gesetzt,
// weil loadCharts erst nach dem Einblenden des Tabs läuft.
const chart = LightweightCharts.createChart(el, {
width: el.clientWidth || 320, height: 200,
layout: { background: { color: "#0d1117" }, textColor: "#8b949e", fontSize: 15 },
grid: { vertLines: { color: "#10151c" }, horzLines: { color: "#10151c" } },
timeScale: { borderColor: "#30363d", timeVisible: true, secondsVisible: false },
rightPriceScale: { borderColor: "#30363d" },
crosshair: { mode: LightweightCharts.CrosshairMode.Normal },
handleScale: { axisPressedMouseMove: false },
});
const candle = chart.addCandlestickSeries({
upColor: "#3fb950", downColor: "#f85149", borderVisible: false,
wickUpColor: "#3fb950", wickDownColor: "#f85149",
});
const line = (color) => chart.addLineSeries(
{ color, lineWidth: 1, priceLineVisible: false, lastValueVisible: false, crosshairMarkerVisible: false });
const s12 = line("#58a6ff"), s50 = line("#d29922"); // EMA12 blau, EMA50 amber
// Regressionskanal (nur M30 bekommt Daten): oben/unten gestrichelt, Mitte gepunktet
const chLine = (color, style) => chart.addLineSeries(
{ color, lineWidth: 1, lineStyle: style, priceLineVisible: false,
lastValueVisible: false, crosshairMarkerVisible: false });
const chU = chLine("#8b949e", 2), chM = chLine("#8b949e66", 1), chL = chLine("#8b949e", 2);
_charts[tf] = { chart, candle, s12, s50, chU, chM, chL };
return _charts[tf];
}
function renderChart(tf, d) {
const off = d.broker_utc_offset || 0;
const B = d.bars || [];
if (d.error || !B.length) {
const tr = document.getElementById("ctr-" + tf);
if (tr) { tr.textContent = d.error ? "⚠ " + d.error : "keine Daten"; tr.className = "chart-trend flat"; }
return;
}
const candles = B.map(b => ({ time: b.t - off, open: b.o, high: b.h, low: b.l, close: b.c }));
const em = (arr) => (arr || []).map((v, i) => (B[i] ? { time: B[i].t - off, value: v } : null)).filter(Boolean);
const c = _charts[tf] || _makeChart(tf);
c.candle.setData(candles);
c.s12.setData(em(d.ema12));
c.s50.setData(em(d.ema50));
// Regressionskanal (M30) — 3 Linien deckungsgleich mit den Bars; sonst leeren
if (c.chU) {
const ch = d.channel;
if (ch && ch.upper) {
c.chU.setData(em(ch.upper)); c.chM.setData(em(ch.mid)); c.chL.setData(em(ch.lower));
} else {
c.chU.setData([]); c.chM.setData([]); c.chL.setData([]);
}
}
// S/R-Linien + Zonen (Ranges) als horizontale Preislinien einzeichnen
(c.lines || []).forEach(pl => { try { c.candle.removePriceLine(pl); } catch (e) {} });
c.lines = [];
const addLine = (p, color, style, title) => {
if (p == null) return;
c.lines.push(c.candle.createPriceLine({ price: p, color, lineWidth: 1,
lineStyle: style, axisLabelVisible: true, title: title || "" }));
};
const LV = d.levels || {};
// Verständliche Labels: nächstes Level ausgeschrieben, weitere nummeriert
(LV.res || []).forEach((p, i) =>
addLine(p, "#1f6feb", 2, i === 0 ? "Widerstand" : `Widerstand ${i + 1}`));
(LV.sup || []).forEach((p, i) =>
addLine(p, "#58a6ff", 2, i === 0 ? "Unterstützung" : `Unterstützung ${i + 1}`));
(LV.zones || []).forEach(z => {
addLine(z.hi, "#1f6feb", 1, "Zone"); addLine(z.lo, "#1f6feb", 1, "");
});
// Chartmuster-Overlay (nur M30): Trigger = kräftige Linie (grün bull / rot bear),
// Ziel = gepunktete Linie gleicher Farbe. Nur die 2 stärksten, sonst wird's voll.
(d.patterns || []).slice(0, 2).forEach(p => {
const bull = p.dir === "bull";
const col = bull ? "#3fb950" : (p.dir === "bear" ? "#f85149" : "#8b949e");
c.lines.push(c.candle.createPriceLine({
price: p.trigger, color: col, lineWidth: 2, lineStyle: 0,
axisLabelVisible: true, title: (p.status === "confirmed" ? "▶ " : "◌ ") + p.name }));
if (p.target != null)
c.lines.push(c.candle.createPriceLine({
price: p.target, color: col, lineWidth: 1, lineStyle: 1,
axisLabelVisible: true, title: "Ziel" }));
});
c.chart.timeScale().fitContent();
const tr = document.getElementById("ctr-" + tf);
const [txt, cls] = _TREND_TXT[d.trend] || ["—", "flat"];
tr.textContent = txt; tr.className = "chart-trend " + cls;
document.getElementById("clast-" + tf).textContent = candles[candles.length - 1].close.toFixed(3);
}
async function loadCharts() {
if (typeof LightweightCharts === "undefined") {
$("charts-body").innerHTML = '<div class="dim" style="padding:14px">Chart-Framework nicht geladen (Seite neu laden).</div>';
return;
}
for (const tf of CHART_TFS) {
_ensureChartCard(tf);
try {
const d = await (await fetch(`api/bars?tf=${tf}&n=100`)).json();
renderChart(tf, d);
// Breite nachziehen (Rotation/Resize seit Erstellung)
const c = _charts[tf], el = document.getElementById("ccv-" + tf);
if (c && el && el.clientWidth > 0) c.chart.applyOptions({ width: el.clientWidth });
} catch (e) {
// Fehler SICHTBAR machen statt stumm schlucken (war: leere Kacheln ohne Grund)
const tr = document.getElementById("ctr-" + tf);
if (tr) { tr.textContent = "⚠ " + (e && e.message ? e.message : "Fehler"); tr.className = "chart-trend flat"; }
}
}
}
window.addEventListener("resize", () => {
for (const tf of CHART_TFS) {
const c = _charts[tf], el = document.getElementById("ccv-" + tf);
if (c && el && el.clientWidth > 0) c.chart.applyOptions({ width: el.clientWidth });
}
});
$("logs-reload").onclick = loadLogs;
$("stats-reload").onclick = loadStats;
$("news-reload").onclick = loadNews;
$("charts-reload").onclick = loadCharts;
// Auto-Refresh nur solange der Charts-Tab sichtbar ist (schont den mt5_lock)
setInterval(() => { if (!$("view-charts").classList.contains("hidden")) loadCharts(); }, 20000);
// Sofort einmal per REST laden, dann auf den Live-Stream umschalten
fetch("api/snapshot").then(r => r.json()).then(_applySnapshot).catch(() => {});