Breakout-Karte vorbereitet (inaktiv bis Neustart) + Bewertung der Margin-Idee

User-Idee: 40 % Margin manuell, 50 % fuer Breakout reservieren, neue Karte.

Bewertung: (1) 40 % ist der groesste gemessene Hebel (MaxDD 82,8 %) und ein
Schalter ohne Neustart. (2) Reserve ist technisch moeglich (Konto ist
RETAIL_HEDGING), aber _check_auto_squeeze feuert 'nur FLAT' und engine.py hat 17
Einzel-Positions-Annahmen -- Kern-Umbau, kein Parameter. Zudem steht die
Zuteilung gegen die Messung: autonom -4,71 EUR/Lot gegen Mensch +1,56 EUR/Lot.
Live-Befund macht (1) dringlich: freie Margin -6,21 EUR, Margin-Level 99,3 %.
Es gibt derzeit nichts zu reservieren.

Gebaut (inaktiv): history.fetch_squeeze_since, engine._squeeze_b5_cached
(120 s gecacht), Snapshot-Feld squeeze_b5, Karte #card-brk. Eigenes Fenster ab
01.08. -- squeeze_monitor.live rechnet ueber alle 42 Trades, B5 zaehlt 11. Die
Karte zeigt beide Zahlen, weil der Monitor 'ontrack' aus der MECHANIK meldet,
waehrend live Verhaeltnis 0,14 / PF 0,39 steht.

Live aendert sich nichts: Karte ist hidden, renderBrk blendet sie aus solange
squeeze_b5 fehlt. Aktiv nach dem naechsten Neustart (wartet auf den Trade).

BEINAHE-SCHADEN: ein UnicodeEncodeError liess web/app.js mit 0 Bytes zurueck.
node --check bestand trotzdem -- eine leere Datei ist gueltiges JS. Gefangen hat
es erst der Groessenvergleich. Neue Regel: erst .tmp schreiben, Groesse pruefen,
dann os.replace.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-19 13:32:48 +02:00
co-authored by Claude Opus 5
parent 3affe51030
commit 30cb77b795
8 changed files with 8490 additions and 2 deletions
+41
View File
@@ -7434,6 +7434,47 @@ vollständig.
**Verifikation der Zustellung steht aus** — sie erfolgt mit dem Report am
nächsten Morgen um 07:30. Die Bausteine sind einzeln live gegengeprüft.
## ⚙ BREAKOUT-KARTE VORBEREITET — aktiv erst nach dem nächsten Neustart (2026-08-19, v=176)
User-Idee: (1) manuell fest 40 % Margin, (2) 50 % für Breakout reservieren,
(3) neue Dashboard-Karte. **Bewertung:**
| Punkt | Urteil |
|---|---|
| ① 40 % fest | ✅ grösster gemessener Hebel (MaxDD **82,8 %**, schlechtester Trade 381 €). **Ein Schalter, kein Umbau** — Feld „Einsatz %“, ohne Neustart, nur auf neue Positionen. |
| ② 50 % Reserve | ⚠ Konto ist **RETAIL_HEDGING**, also technisch möglich — aber: `_check_auto_squeeze` feuert **„nur FLAT“**, dazu **17 Einzel-Positions-Annahmen** in `engine.py` (Trailing, Notfall-Stop, S/R-Close, Time-Stop, Breaker verwalten je EINE Position). Kern-Umbau, kein Parameter. |
| ③ Karte | ✅ gebaut, s. u. |
⚠⚠ **Der Live-Befund macht ① dringlich:** Balance 937,40 € · Equity 877,41 € ·
Margin belegt 883,62 € → **freie Margin 6,21 €, Margin-Level 99,3 %**. EINE
Position (1,19 Lot) frisst das ganze Konto. **Es gibt derzeit nichts zu
reservieren** — die Reserve aus ② entsteht überhaupt erst durch ①.
⚠⚠ **Und die Zuteilung steht gegen die Messung:** seit 01.08. je Lot
**autonom 4,71 €** (n=11) gegen **Mensch +1,56 €** (n=185). ② gäbe dem
gemessen schlechteren Pfad den grösseren Anteil; 40+50 lassen zudem nur 10 %
Puffer. **Empfehlung: ① und ③ jetzt, ② erst wenn freie Margin existiert UND
der autonome Pfad auf einem NEU vorab fixierten Fenster nicht negativ ist.**
**Gebaut (inaktiv):** `history.fetch_squeeze_since` · `engine._squeeze_b5_cached`
(~120 s gecacht, Muster `_alignment_cached`) · Snapshot-Feld `squeeze_b5` ·
Karte `#card-brk` (Zustand · Squeeze jetzt · liegende Orders · **B5-Stand**).
**Eigenes Fenster ab 01.08.**`squeeze_monitor.live` rechnet über **alle 42**
Trades, B5 zählt **11**. Beide ungetrennt zu zeigen wäre dieselbe stille
Fenster-Verwechslung wie bei `pbreak_accuracy_v2`.
⚠ Die Karte nennt beide Zahlen: der Monitor meldet **`ontrack`** aus der
**Mechanik** (ØR +0,163 / PF 1,24), während live **Verhältnis 0,14 / PF 0,39**
steht. Die Lücke IST der B4-Befund — eine der beiden allein wäre irreführend.
**Live ändert sich nichts:** die Karte ist `hidden` und `renderBrk` blendet
sie aus, solange `squeeze_b5` fehlt. Aktiv nach dem nächsten Neustart — der
wartet auf User-Wunsch, bis die offene Position zu ist.
⚠⚠ **BEINAHE-SCHADEN, dokumentiert weil es eine Fehlerklasse ist:** ein
`UnicodeEncodeError` (Emoji-Escapes) brach mitten im Schreiben ab und liess
**`web/app.js` mit 0 Bytes** zurück — `io.open(p,"w")` kürzt VOR dem Schreiben.
**`node --check` bestand trotzdem: eine LEERE Datei ist gültiges JS.** Die
Syntaxprüfung allein hätte den Totalverlust durchgewinkt; gefangen hat ihn erst
der Grössenvergleich. **Regel: Datei erst in `.tmp` schreiben, Grösse gegen das
Original prüfen, dann `os.replace`** — so ist es jetzt gemacht.
## ✅ BRK BLEIBT AN (User 2026-08-19) — und die 13-tägige Flaute war kein Defekt
User: „Breakout-Squeeze soll an bleiben. Rechne und überprüfe erneut.“
+46
View File
@@ -3248,6 +3248,48 @@ class TradingEngine:
self._align_ts = now
return getattr(self, "_align_cache", None)
def _squeeze_b5_cached(self) -> dict | None:
"""B5-Stand des Auto-Squeeze fuer die Breakout-Karte (2026-08-19).
WARUM EIN EIGENES FELD und nicht `squeeze_monitor.live`: der Monitor
rechnet ueber ALLE SQUEEZE_*-Trades (n=42). Die B5-Abbruchregel zaehlt
aber ausdruecklich **ab dem 01.08.** (n=11). Beide Zahlen im selben Bild
zu zeigen, ohne sie zu trennen, waere genau die Sorte stille
Fenster-Verwechslung, an der `pbreak_accuracy_v2` und die
Verdict-Faelligkeit schon aufgelaufen sind.
Der Monitor meldet zudem `verdict: ontrack` aus der **Mechanik**
(Regel neu gerechnet, OeR +0,163 / PF 1,24) waehrend die LIVE-Bilanz
im selben Moment bei Verhaeltnis 0,14 / PF 0,39 steht. Genau diese
Luecke ist der B4-Befund; die Karte muss beide Zahlen nebeneinander
zeigen, sonst liest sich 'ontrack' wie eine Entwarnung.
DB-Query ~120 s gecacht (Muster wie `_alignment_cached`)."""
now = time.time()
if now - getattr(self, "_b5_ts", 0.0) > 120:
self._b5_cache = None
try:
ab = int(datetime(2026, 8, 1, tzinfo=_BERLIN).timestamp())
rows = self.history.fetch_squeeze_since(ab)
g = [x for x in rows if x > 0]
v = [x for x in rows if x <= 0]
og = (sum(g) / len(g)) if g else 0.0
ov = abs(sum(v) / len(v)) if v else 0.0
self._b5_cache = {
"n": len(rows), "ziel": 20,
"wins": len(g), "losses": len(v),
"avg_win": round(og, 2), "avg_loss": round(-ov, 2),
# die beiden Groessen, an denen die Regel haengt
"ratio": round(og / ov, 2) if ov else None,
"pf": round(sum(g) / abs(sum(v)), 2) if v and sum(v) else None,
"sum_eur": round(sum(rows), 2),
"latte": 1.0,
}
except Exception as e:
log.debug(f"squeeze_b5: {e}")
self._b5_ts = now
return getattr(self, "_b5_cache", None)
def _hl_ctx_refresh(self):
"""Holt den HL-Kontext im Hintergrund (s. Aufrufstelle im Trend-Loop)."""
try:
@@ -4231,6 +4273,10 @@ class TradingEngine:
# kostet ungenutzt nichts (sie laeuft nur beim Aufruf).
"hl_ctx": getattr(self, "_hl_ctx", None),
"rec_track": self._rec_track_cached(),
# B5-Stand des Auto-Squeeze fuer die Breakout-Karte (2026-08-19).
# ⚠ Eigenes Fenster (ab 01.08.) — NICHT dasselbe wie
# `squeeze_monitor.live` (alle Trades). s. `_squeeze_b5_cached`.
"squeeze_b5": self._squeeze_b5_cached(),
# Kennzahlen der MANUELLEN Trades für die gleichnamige Karte
# (2026-08-04). Rein deskriptiv; ~120 s gecacht, damit der heiße
# Snapshot-Pfad nicht je Abruf die DB aggregiert.
File diff suppressed because it is too large Load Diff
+26
View File
@@ -855,6 +855,32 @@ class HistoryLogger:
"pct": round(100.0 * (r["c"] or 0) / (r["n"] or 1), 1)}
return out
def fetch_squeeze_since(self, ab: int) -> list:
"""P&L aller geschlossenen SQUEEZE_*-Trades ab Epoch `ab` (2026-08-19).
⚠ Bewusst NUR `setup LIKE 'SQUEEZE%'` — die `setup`-Spalte taugt NICHT
als allgemeine Bot/Mensch-Trennung: `engine._open` schreibt die
Wellenlage auch bei MANUELLEN Trades mit (`WAVE`, n=124). Nur
`SQUEEZE_*` ist autonom eroeffnet. Ein erster Auswertungsversuch am
13.08. hat genau das verwechselt und dem Bot 152 Trades zugeschrieben.
⚠ NICHT je Lot normiert: die B5-Regel ist ausdruecklich auf
OeGewinn/OeVerlust in EURO fixiert worden. Sie hier still auf eine
andere Einheit umzustellen waere ein nachtraegliches Verschieben der
Latte.
Fail-open (leere Liste) — `core/history.py` hat bewusst keinen Logger.
"""
try:
with self._connect() as c:
return [float(r[0]) for r in c.execute(
"SELECT pnl FROM trades "
" WHERE setup LIKE 'SQUEEZE%' AND exit_time IS NOT NULL "
" AND pnl IS NOT NULL AND entry_time > ? "
" ORDER BY entry_time", (int(ab),))]
except Exception:
return []
def alignment_stats(self, last_n: int = 0) -> dict:
"""Ausrichtungs-Split: liefen die Trades MIT, GEGEN oder OHNE die Empfehlung?
File diff suppressed because it is too large Load Diff
+53
View File
@@ -153,6 +153,58 @@ function beepRecommendation(sig, isNew) {
// muss jedes Gate EINZELN passieren. Ein Gate, das fällt, lässt sich nicht
// durch ein anderes ausgleichen.
// ══════════════════════════════════════════════════════════════════════════
// Breakout-Autotrader-Karte (2026-08-19). Nur ANZEIGE aus vorhandenen
// Snapshot-Feldern -- hier wird nichts neu gerechnet.
// Die Karte bleibt VERSTECKT, solange `squeeze_b5` fehlt (aelterer Server, das
// Feld gibt es erst nach einem Neustart). Eine halb gefuellte Karte waere
// schlimmer als keine: sie saehe aus wie ein Zustand statt wie eine fehlende
// Quelle.
function renderBrk(d) {
const box = $streng("card-brk");
if (!box) return;
const b5 = d.squeeze_b5;
if (!b5) { box.classList.add("hidden"); return; }
box.classList.remove("hidden");
const an = !!d.auto_squeeze;
const nh = (d.squeeze_night_hours || []).map((h) => h + ":00").join(", ");
$("brk-state").textContent = (an ? "AN" : "AUS")
+ (d.squeeze_pending ? " · ruhende Stop-Order" : " · Market-Fallback")
+ (nh ? " · Nachtsperre " + nh : "");
$("brk-state").className = an ? "ok" : "warn";
const sq = (d.wave || {}).squeeze || {};
const st = sq.state;
$("brk-squeeze").textContent =
st === "active" ? "AUSBRUCH " + (sq.dir || "") + " @ " + (sq.level ?? "?")
: st === "armed" ? "komprimiert — Ausbruch steht bevor"
: "keine Kompression (Box " + (sq.box_atr ?? "?") + "×ATR)";
const pl = d.pending_levels || [];
$("brk-pending").textContent = pl.length
? pl.length + " liegend (" + (d.pending_quelle || "?") + ")"
: "keine — " + (an ? "Squeeze nicht armiert oder Position offen" : "Trader aus");
// B5: der Stand der einzigen Regel, die die Autonomie beenden kann.
const r = b5.ratio, pf = b5.pf;
const erf = r != null && pf != null && r >= b5.latte && pf >= 1;
$("brk-b5").textContent = b5.n + "/" + b5.ziel + " Trades · Verhältnis "
+ (r == null ? "—" : r.toFixed(2)) + " · PF "
+ (pf == null ? "—" : pf.toFixed(2))
+ " · " + b5.sum_eur.toFixed(2) + " €";
$("brk-b5").className = erf ? "ok" : "bad";
const el = $("brk-note");
if (el) {
el.textContent = erf
? "Latte (Verhältnis ≥ 1,0 UND PF ≥ 1) erfüllt."
: "⚠ Latte verfehlt (Ziel: Verhältnis ≥ 1,0 UND PF ≥ 1). Der Monitor "
+ "im Statistik-Tab meldet daneben „ontrack“ — das kommt aus der MECHANIK "
+ "(Regel neu gerechnet), nicht aus diesen echten Trades. Die Lücke "
+ "zwischen beiden ist der Befund, kein Widerspruch.";
}
}
function renderM15(s) {
const st = $("m15-status");
if (!s) { st.className = "m15-status"; st.textContent = "— keine Daten"; return; }
@@ -936,6 +988,7 @@ function render(d) {
// ── Gesamtempfehlung (aggregiert) ───────
renderM15(d.m15_setup);
renderBrk(d);
renderKontext(d);
renderCircuitBreaker(d.circuit_breaker);
renderPatterns(d.patterns);
File diff suppressed because it is too large Load Diff
+19 -2
View File
@@ -12,7 +12,7 @@
⚠ 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=175">
<link rel="stylesheet" href="style.css?v=176">
</head>
<body>
<!-- Modul-Menü (ganz oben) -->
@@ -156,6 +156,23 @@
<div class="reasons" id="wave-reasons" title="Die Begründungen, aus denen sich Richtung und Konfidenz zusammensetzen."></div>
</section>
<!-- Breakout-Autotrader (User-Wunsch 2026-08-19). ⚠ Der autonome Pfad handelt
echtes Geld, seine Abbruchregel (B5) stand bisher NUR im taeglichen
Report. Diese Karte macht ihn sichtbar — Zustand, liegende Orders und
B5-Stand an einer Stelle.
⚠⚠ Sie zeigt BEIDE Zahlen getrennt: die B4-'Mechanik' (Regel neu
gerechnet, sieht gut aus) und die LIVE-Bilanz (verliert). Genau diese
Luecke IST der Befund — eine der beiden allein waere irrefuehrend.
Reine ANZEIGE: schaltet nichts, blockiert nichts. -->
<section class="card wide hidden" id="card-brk">
<h2 title="Autonomer Entry auf den Squeeze-Ausbruch. Einziges 2-Stichproben-validiertes Auto-Setup des Projekts.">🚀 Breakout-Autotrader</h2>
<div class="kv"><span>Zustand</span><b id="brk-state"></b></div>
<div class="kv"><span>Squeeze jetzt</span><b id="brk-squeeze"></b></div>
<div class="kv"><span>Liegende Orders</span><b id="brk-pending"></b></div>
<div class="kv" title="B5 = die VORAB fixierte Abbruchregel: nach 20 Trades ab 01.08. muss Verhaeltnis (OeGewinn/OeVerlust) >= 1,0 UND PF >= 1 sein."><span>B5-Regel</span><b id="brk-b5"></b></div>
<div id="brk-note" class="hint"></div>
</section>
<section class="card wide" id="card-kontext">
<h2 title="Drei unabhaengige Kontext-Anzeigen. KEINE Empfehlung und keine Richtungsaussage - die Aggregation zu einer Gesamtempfehlung wurde am 08.08. entfernt.">Marktkontext</h2>
<!-- Zeitebenen-Ampel (M1/M5/M15/M30/H1) -->
@@ -370,6 +387,6 @@
</div>
<div id="toast" class="toast hidden"></div>
<script src="app.js?v=175"></script>
<script src="app.js?v=176"></script>
</body>
</html>