Chartmuster je Zeitebene als Bild + M1/M5 in der Meldungskarte (v=204)

User-Vorgabe: die hinterlegten Muster-Bilder sollen je Minuten-Chart das
gerade entstehende Muster zeigen, M1 und M5 zusaetzlich in der Meldungskarte.

BILDER: 15 Vorlagen aus /Chartmuster, 436-611 KB je Stueck (7,5 MB gesamt).
Das Dashboard uebertraegt ~14 KB je Snapshot ueber WireGuard - ein Originalbild
waere das 40-fache. Verkleinert auf 520 px / JPEG q80 nach web/muster/:
7,5 MB -> 379 KB (Faktor 20), im Frontend zusaetzlich loading="lazy".
Die Originale sind gitignored, nur die Web-Fassungen kommen ins Repo.

DETEKTOR: core/patterns.py bekommt die Zeitebene als PARAMETER. Der Default
bleibt M30 und damit bitgleich - `PatternDetector()` speist das Verdict-Gewicht
0,25, eine stille Verschiebung dort waere eine Strategie-Aenderung durch die
Hintertuer. Daneben laufen vier weitere Instanzen (M1/M5/M15/H1) mit
gestaffelten Refresh-Zeiten (20/25/35/60 s), damit sie den mt5_lock nicht im
Pulk belegen. Sie haben KEINE Stimme.

Neu Snapshot `patterns_tf`: je Zeitebene das STAERKSTE Muster (bestaetigte vor
sich bildenden, dann Symmetrie-Guete; ungueltige fallen raus) samt Bildnamen
ueber die Karte `patterns.BILD`.
Doppeltop/-boden, Dreifach-Top/-Boden und Flagge bull/baer teilen sich je ein
Bild (die Vorlagen zeigen beide Richtungen); `inv_cup` bekommt KEINS - lieber
kein Bild als ein falsches.

⚠⚠ REINE ANZEIGE, und das steht auch dran. Die Muster-Klasse ist als SIGNAL
gemessen und verworfen (Ziel-Trefferquote nur 13-38 %, backtest_patterns.py),
und auf M1 ist sie am schwaechsten: dort liegt der Spread bei 0,319xATR, ein
Muster ueber wenige Bars ist weitgehend Rauschen. Kein Verdict-Gewicht, kein
Trade-Trigger; die Tooltips nennen die Messung.

⚠ Beim Bau zwei eigene Fehler gefangen: (1) ich hatte die Feldnamen GERATEN -
es heisst `type`, nicht `typ`, und der Status ist "confirmed"/"forming"/
"invalidated". Ein falscher Schluessel haette still None geliefert, die Karte
waere leer geblieben ohne jede Fehlermeldung. (2) Der Emoji-Surrogat-Abbruch
zum sechsten Mal - die .tmp-Regel hat app.js erneut gerettet (0 Bytes verhindert),
danach mit literalen Zeichen statt \u-Escapes geschrieben.

Live verifiziert: M1 Doppelboden, M5 Tasse+Henkel, M15 aufsteigendes Dreieck,
M30 Doppeltop; alle Bilder HTTP 200 (22-28 KB). Pipeline A-G gruen, 97 Tests,
14 render*-Funktionen laufen durch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-20 22:05:18 +02:00
co-authored by Claude Opus 5
parent fd2ca46dc0
commit 02bede7ef9
22 changed files with 798 additions and 11 deletions
+51 -1
View File
@@ -715,6 +715,21 @@ function renderKontext(snap) {
// dann wieder einschaltet"). Der Text kommt FERTIG aus dem Backend
// (`trail_info`) — ihn hier nachzubauen waere die Divergenz-Falle, die im
// Projekt dreimal zugeschlagen hat.
// M1/M5-Muster kompakt in der Meldungen-Karte (User 2026-08-20) — bewusst
// OHNE Bild: das hier ist der Live-Textstrom zum laufenden Trade.
const patN = $streng("pat-note");
if (patN) {
const ptf = (snap && snap.patterns_tf) || {};
const teile = ["M1", "M5"].map(tf => {
const m = ptf[tf];
if (!m) return `${tf} kein Muster`;
const p = m.dir === "bull" ? "▲" : m.dir === "bear" ? "▼" : "◆";
return `${tf} ${p} ${m.name}` + (m.status === "confirmed" ? " (bestätigt)" : "");
});
patN.textContent = "Muster: " + teile.join(" · ");
patN.classList.remove("hidden");
}
const trN = $streng("trail-note");
if (trN) {
const trTxt = snap && snap.trail_info;
@@ -978,6 +993,40 @@ function renderCone(cn) {
+ "leicht zu eng. Kein Signal, kein Einfluss auf die Empfehlung.";
}
// Je Zeitebene das gerade entstehende Muster als BILD (User-Wunsch 2026-08-20).
// ⚠ REINE ANZEIGE. Die Muster-Klasse ist als Signal gemessen und verworfen
// (Ziel-Trefferquote 1338 %, `backtest_patterns.py`); auf M1 ist sie am
// schwächsten — dort liegt der Spread bei 0,319×ATR, ein Muster über wenige
// Bars ist dort weitgehend Rauschen. Kein Verdict-Gewicht, kein Trade-Trigger.
// ⚠ Bilder mit `loading="lazy"`: die 15 Vorlagen liegen verkleinert bei ~25 KB
// (Originale 436611 KB), aber über WireGuard soll nur geladen werden, was
// wirklich sichtbar ist.
function renderPatternsTF(d) {
const el = $streng("pt-tfgrid");
if (!el) return;
const pt = (d && d.patterns_tf) || null;
if (!pt) { el.innerHTML = ""; return; }
el.innerHTML = ["M1", "M5", "M15", "M30", "H1"].map(tf => {
const m = pt[tf];
if (!m) {
return `<div class="pt-tfcell leer"><span class="pt-tflbl">${tf}</span>`
+ `<span class="dim">kein Muster</span></div>`;
}
const cls = m.dir === "bull" ? "up" : m.dir === "bear" ? "down" : "dim";
const arr = m.dir === "bull" ? "▲" : m.dir === "bear" ? "▼" : "◆";
const best = m.status === "confirmed";
const bild = m.bild
? `<img class="pt-img" src="muster/${m.bild}.jpg" alt="${m.name}" loading="lazy">`
: `<div class="pt-img leer">kein Bild</div>`;
const q = (m.quality != null ? m.quality : 0).toFixed(2);
return `<div class="pt-tfcell${best ? " best" : ""}">`
+ `<span class="pt-tflbl">${tf}</span>${bild}`
+ `<span class="pt-tfname ${cls}">${arr} ${m.name}</span>`
+ `<span class="pt-tfst">${best ? "bestätigt" : "bildet sich"} · Güte ${q}</span>`
+ `</div>`;
}).join("");
}
function renderPatterns(pt) {
const el = $("pt-list");
if (!el) return;
@@ -1198,6 +1247,7 @@ function render(d) {
renderBrk(d);
renderKontext(d);
renderCircuitBreaker(d.circuit_breaker);
renderPatternsTF(d);
renderPatterns(d.patterns);
renderCandles(d.candle_anat);
renderCone(d.cone);
@@ -2397,7 +2447,7 @@ async function closeSlot(slot, label, pnl) {
// und der Browser zeigte Altes. Statt das jedes Mal von Hand zu diagnostizieren,
// sagt die Seite es jetzt selbst.
// ⚠ Bewusst SICHTBAR statt nur Konsole: die Konsole sieht am Handy niemand.
const _JS_V = "203";
const _JS_V = "204";
{
const htmlV = document.body && document.body.dataset
? document.body.dataset.htmlV : null;
+11 -3
View File
@@ -12,9 +12,9 @@
⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard
und muss offline funktionieren. -->
<link rel="stylesheet" href="pico.min.css?v=1">
<link rel="stylesheet" href="style.css?v=203">
<link rel="stylesheet" href="style.css?v=204">
</head>
<body data-html-v="203">
<body data-html-v="204">
<!-- Modul-Menü (ganz oben) -->
<nav id="tabs">
<button class="tab active" data-view="dash">Dashboard</button>
@@ -178,6 +178,9 @@
<!-- Trailing-Zustand im Klartext. Die Trailing-KARTE ist seit dem
22.07. ausgeblendet; damit gab es keine Stelle mehr, an der man
sah, WARUM der Stop nicht nachgezogen wird (User-Frage 20.08.). -->
<!-- M1/M5-Muster kompakt (User 2026-08-20). Bewusst OHNE Bild:
die Meldungen-Karte ist der Live-Textstrom zum laufenden Trade. -->
<div class="hl-note hidden" id="pat-note" title="Das gerade entstehende Muster auf M1 und M5. Reine Anzeige — die Muster-Klasse ist als Signal gemessen und verworfen (Ziel-Trefferquote 1338 %), auf M1 am schwaechsten."></div>
<div class="hl-note hidden" id="trail-note" title="Was das Trailing gerade tut. Zwei Gruende, warum es scheinbar nichts tut und beide richtig sind: es startet erst ab 1,3×ATR Gewinn (seit 20.08., vorher 0,3×), und es lockert einen bereits engeren Stop niemals."></div>
<!-- Auto-Flip-Close: schließt eine Position, wenn die Empfehlung dreht.
⚠ GEMESSEN in BEIDEN Halbjahren negativ (92/160 R,
@@ -277,6 +280,11 @@
`forming` zählt halb. Deshalb LIVE, nicht Anzeige. -->
<section class="card wide" id="card-patterns">
<h2 title="Mechanisch erkannte Chartmuster auf M30 mit Trigger und Kursziel. Gewicht 0,25. Die Ziel-Trefferquote liegt gemessen nur bei 1338 % — Kontext, keine Vorhersage.">Chartmuster <span class="pt-tf">M30 · Gewicht 0,25</span></h2>
<!-- Je Zeitebene das gerade entstehende Muster als BILD (User 2026-08-20).
⚠ REINE ANZEIGE: die Muster-Klasse ist als Signal gemessen und
verworfen (Ziel-Trefferquote 1338 %); auf M1 ist sie am schwaechsten,
dort liegt der Spread bei 0,319×ATR. -->
<div class="pt-tfgrid" id="pt-tfgrid" title="Das jeweils staerkste Muster je Zeitebene, mit Bild. Bestaetigte gehen vor sich bildenden; bei gleichem Status entscheidet die Symmetrie-Guete."></div>
<div class="pt-list" id="pt-list" title="Erkannte Muster mit Skizze, Trigger und Kursziel."><span class="dim"></span></div>
</section>
</section>
@@ -425,6 +433,6 @@
</div>
<div id="toast" class="toast hidden"></div>
<script src="app.js?v=203"></script>
<script src="app.js?v=204"></script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+24
View File
@@ -710,3 +710,27 @@ main{padding-bottom:4.1111rem} /* Platz für die fixe Leiste */
/* ⚠ Blinken uebernommen vom entfernten CLOSE-Knopf — sonst haette der
Close-Alarm seine Sichtbarkeit verloren und nur noch gepiept. */
.px-close.blink{animation:blkClose .6s ease-in-out infinite}
/* ── Chartmuster je Zeitebene (User 2026-08-20) ────────────────────────────
⚠ Bilder sind 520×284 und werden auf die Zellbreite skaliert. Das Raster
passt sich selbst an (auto-fit), damit es am Handy einspaltig und am
Desktop mehrspaltig laeuft — ohne zweite Media-Query. */
.pt-tfgrid{
display:grid; gap:0.5556rem; margin:0 0 0.6667rem;
grid-template-columns:repeat(auto-fit, minmax(9.4444rem, 1fr));
}
.pt-tfcell{
display:flex; flex-direction:column; gap:0.1667rem;
background:var(--surface-2); border:1px solid var(--border);
border-radius:var(--r); padding:0.3889rem;
}
.pt-tfcell.best{border-color:#4a3600}
.pt-tfcell.leer{opacity:.55; justify-content:center; min-height:4.4444rem;
align-items:center; text-align:center}
.pt-tflbl{font:600 0.7222rem/1 var(--mono); color:var(--dim); letter-spacing:.04em}
.pt-img{width:100%; height:auto; border-radius:var(--r-sm); display:block;
background:#0d1117}
.pt-img.leer{height:3.3333rem; display:flex; align-items:center;
justify-content:center; font-size:0.6667rem; color:var(--dim)}
.pt-tfname{font-size:0.7778rem; font-weight:600; line-height:1.2}
.pt-tfst{font-size:0.6667rem; color:var(--dim); font-family:var(--mono)}