diff --git a/web/app.js b/web/app.js
index 0a4252b..abbe7ab 100644
--- a/web/app.js
+++ b/web/app.js
@@ -416,6 +416,45 @@ function renderM15(s, snap) {
+ "gedeckelt, der Verlust laeuft bis zum Stop. Reine Anzeige.";
}
+ // --- SETUP-BLOCK: Entry Zone · SL · TP1-3 (User-Wunsch 2026-08-20) ------
+ // ⚠⚠ Die RICHTUNG kommt aus dem VETO (erlaubte Richtung), NICHT aus einer
+ // Prognose — die Karte gibt keine ab, und das ist gemessen richtig so
+ // (1.071 Live-Episoden, 48–51 % Treffer). Der Block beantwortet nur:
+ // WENN gehandelt wird, zu welchen Preisen.
+ // ⚠ Nichts wird neu gerechnet — Zonen, ATR und Kegel stehen im Snapshot.
+ // ⚠ TP1 ist ein LEVEL, kein Auftrag: ein fester TP am Gegenlevel ist der
+ // staerkste Gewinner-Kappen-Beleg des Projekts (Squeeze +0,102/+0,108 →
+ // −0,066/−0,004). Deshalb steht bei TP2, dass das Trailing fuehrt.
+ {
+ const sb = $streng("m15-setupblock");
+ const verb = ((s.bias || {}).verboten || "").toUpperCase();
+ const erl = verb === "SHORT" ? "LONG" : verb === "LONG" ? "SHORT" : null;
+ const zonen = s.zonen || [];
+ const nah = zonen.slice().sort((a, b) => (a.gap_atr15 ?? 9) - (b.gap_atr15 ?? 9))[0];
+ const atr = s.atr_m15 || 0;
+ if (sb && erl && nah && atr > 0) {
+ const lg = erl === "LONG";
+ const e = nah.level, tol = 0.15 * atr;
+ const sl = lg ? e - 2.0 * atr : e + 2.0 * atr;
+ const geg = zonen.find(z => lg ? z.level > e : z.level < e);
+ const lv = ((snap || {}).cone || {}).levels || [];
+ const k120 = lv.find(x => x.band === 80 && x.minutes === 120);
+ const zl2 = [`${lg ? "▲" : "▼"} ${erl} — nur wenn die Kette gruen ist`,
+ `Entry Zone: ${f(e - tol, 3)} – ${f(e + tol, 3)}`,
+ `Stop Loss: ${f(sl, 3)} (2,0×ATR_M15)`];
+ if (geg) zl2.push(`TP1: ${f(geg.level, 3)} (Level · ` +
+ `${100 - (geg.p_break ?? 50)} % haelt)`);
+ zl2.push(`TP2: ${f(lg ? e + 3.5 * atr : e - 3.5 * atr, 3)} ` +
+ `(3,5×ATR — das Trailing fuehrt ihn nach)`);
+ if (k120) zl2.push(`TP3: ${f(lg ? k120.hi : k120.lo, 3)} ` +
+ `(Kegel 120′, trifft ${Math.round(k120.coverage || 0)} %)`);
+ sb.innerHTML = zl2.join("
");
+ sb.hidden = false;
+ } else if (sb) {
+ sb.hidden = true;
+ }
+ }
+
// --- M5-Kontext: reine ZUSTANDS-Zeile, keine Richtung, kein Banner ------
// Bewusst KEINE eigene M5-Karte (User-Entscheidung 19.08.): die entkoppelte
// Selbstmessung dieser Karte steht bei n=342 auf 53,5 % [48,2 .. 58,8] und
@@ -1158,6 +1197,33 @@ function render(d) {
// Enter darauf schickt den GERUNDETEN Wert an den Broker — der Stop
// verschiebt sich also still um bis zu 0,5 Cent. Bei einem Trail von
// 11 Cent (ATR_M5 0,11 in dieser Nacht) sind das 5 % der Distanz.
+ // --- TP1/TP2/TP3: voraussichtliche Ziele (User-Wunsch 2026-08-20) -----
+ // ⚠⚠ REINE ANZEIGE — keines davon wird an den Broker geschickt. Ein FESTER
+ // TP am Gegenlevel ist im Projekt der staerkste Gewinner-Kappen-Beleg:
+ // er drehte den Squeeze von +0,102/+0,108 auf −0,066/−0,004
+ // (backtest_trailing_sr.py). Die drei Zahlen sind Orientierung, kein
+ // Auftrag — deshalb Anzeige-Boxen und keine Eingabefelder.
+ // ⚠ NICHTS wird neu gerechnet: Zonen und Kegel stehen ohnehin im Snapshot.
+ // Ein zweiter Rechenweg waere die Divergenz-Falle (dreimal zugeschlagen).
+ {
+ const lg = p.order_type === 0;
+ const ep = Number(p.entry_price) || 0;
+ // TP1 — naechstes STEHENDES Level in Trade-Richtung
+ const zn = ((d.m15_setup || {}).zonen) || [];
+ const ziel = zn.find(z => lg ? (z.seite === "res" && z.level > ep)
+ : (z.seite === "sup" && z.level < ep));
+ $("tb-tp1").textContent = ziel
+ ? `${fmt(ziel.level, 3)} (${100 - (ziel.p_break ?? 50)}% hält)`
+ : "—";
+ // TP2 — der Broker-TP, den das Trailing fuehrt
+ $("tb-tp2").textContent = p.tp ? fmt(p.tp, 3) : "—";
+ // TP3 — Obergrenze des 80-%-Kegels auf 120 min
+ const lv = ((d.cone || {}).levels) || [];
+ const k = lv.find(x => x.band === 80 && x.minutes === 120);
+ $("tb-tp3").textContent = k
+ ? fmt(lg ? k.hi : k.lo, 3) + (k.coverage ? ` (trifft ${Math.round(k.coverage)}%)` : "")
+ : "—";
+ }
if (document.activeElement !== slEl) slEl.value = p.sl ? fmt(p.sl, 3) : "";
if (document.activeElement !== tpEl) tpEl.value = p.tp ? fmt(p.tp, 3) : "";
} else {
diff --git a/web/index.html b/web/index.html
index 8683d5a..b691fdd 100644
--- a/web/index.html
+++ b/web/index.html
@@ -12,7 +12,7 @@
⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard
und muss offline funktionieren. -->
-
+