M15-Karte misst sich selbst + Ausfuehrungsqualitaet sichtbar (v=157)

Nach Web-Recherche umgesetzt, Punkte 2 und 3 der Wahl.

SELBSTMESSUNG (neue Tabelle m15_states): die Karte hatte KEINE Rueckkopplung -
geprueft, es gab kein Logging. Das war die auffaelligste Luecke: jeder Ausfall,
der in diesem Projekt je aufgedeckt wurde, kam aus einer Rueckkopplung
(pbreak_predictions entlarvte den Modellausfall LIVE, was kein Backtest konnte;
der B4-Monitor die Squeeze-Drift). Die Karte haette monatelang falsch stehen
koennen.
Neu: eine Zeile je ZUSTANDSWECHSEL, nach 30/60 min gegen candles_m1
ausgewertet, Ergebnis als Zeile 6 in der Karte. Gewertet wird die LESART, nicht
der Status, und nur die gerichtete (45-55 % zaehlt nicht mit). Gegen 50 % lesen.
Nur bei Wechsel, nie je Tick - sonst dieselbe Cluster-Verzerrung, die am 07.08.
elf widerlegte Hypothesen ausgeloest hat. Live verifiziert: 1 Zeile trotz
laufendem Sekundentakt.

AUSFUEHRUNGSQUALITAET (Zeile 7): analyze_squeeze_entry_gap.py existiert seit dem
05.08. und misst die TCA-Kennzahl (Arrival-Price-Slippage) - niemand hat je
hineingesehen. Dabei war das der einzige Erfolgsmassstab des Stop-Order-Umbaus
und zugleich der groesste je gemessene Hebel (+0,32 R). Jetzt steht der
Zaehlstand in der Karte: 3 von 12 Bot-Trades seit dem Umbau.

NICHT gebaut: der Kegel mit Adaptive Conformal Inference. Er ist mit
76,7/76,6/75,3 % gegen 80 % Nennwert dokumentiert zu eng, und ACI waere die
Standardmethode (adjustiert online, garantiert Konvergenz auch unter Drift).
Bleibt offen.

BEIM BAU EIN NAMEERROR IM 1-SEKUNDEN-PFAD, VOM LINTER GEFANGEN: der erste
Entwurf berechnete _m15 in _run_analysis und benutzte es in snapshot(); dort
gibt es weder market noch wave_snap. py_compile sieht das NICHT, ruff F821
meldete 4 Befunde. Genau die Fehlerklasse, fuer die der Linter am 07.08.
eingebaut wurde - und der erste echte Fang. Zweiter Fehler beim Verschieben:
der Block landete mitten in einem mehrzeiligen Aufruf. Beide behoben.

DB-Backup: oil_widget_history.db.bak-2026-08-08-m15states

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-08 23:38:56 +02:00
co-authored by Claude Opus 5
parent 5ddbe07066
commit 86c432f791
5 changed files with 341 additions and 6 deletions
+44 -2
View File
@@ -4182,7 +4182,7 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
(„⚠ die Welle (steuert die Order) schweigt").
Live gegengerechnet, beide Zeilen erscheinen wie beabsichtigt.
⚠ Reine Anzeige — an Gewichten, Gates und Order-Logik ist nichts geändert.
- Asset-Version aktuell **v=153** (in `web/index.html` hochzählen, siehe Workflows).
- Asset-Version aktuell **v=157** (in `web/index.html` hochzählen, siehe Workflows).
Schriftgrößen 2026-07-24 global **+4px** (2× je +2px auf User-Wunsch; Body-Basis
14→18px). ⚠ Betrifft in `style.css` sowohl `font-size:Npx` (71×) ALS AUCH die
`font:<weight> Npx/…`-**Shorthand** (3×: `.ms-chip`/`.ms-bos-lbl`/`.sqm-badge`
@@ -4942,6 +4942,48 @@ Modell schliesst keine Trades.
„die Rangfolge ist kaputt" ist **nicht** belegt; belegt ist nur die
Basisraten-/Kalibrierungslücke.
## ✅ DIE M15-KARTE MISST SICH JETZT SELBST + AUSFÜHRUNGSQUALITÄT SICHTBAR (2026-08-08, v=157)
Nach Web-Recherche umgesetzt (User-Wahl aus drei Vorschlägen). ⚠ Der dritte
(Kegel mit **Adaptive Conformal Inference** nachkalibrieren) bleibt OFFEN — der
Kegel ist mit **76,7 / 76,6 / 75,3 %** gegen 80 % Nennwert dokumentiert zu eng,
und ACI ist die Standardmethode dafür (adjustiert das Niveau online, garantiert
Konvergenz auch unter Drift). Nicht gebaut.
**② SELBSTMESSUNG (`m15_states`).** Die Karte hatte **keine Rückkopplung**
geprüft, es gab kein Logging. Das war die auffälligste Lücke: **jeder** Ausfall,
der in diesem Projekt je aufgedeckt wurde, kam aus einer Rückkopplung
(`pbreak_predictions` entlarvte den Modellausfall LIVE, was kein Backtest
konnte; der B4-Monitor die Squeeze-Drift). Die Karte hätte monatelang falsch
stehen können.
Neu: eine Zeile je **Zustandswechsel** (Status + Lesart + Level), nach 30/60 min
gegen `candles_m1` ausgewertet, Ergebnis als Zeile ⑥ in der Karte.
**Gewertet wird die LESART, nicht der Status** — und nur die gerichtete
(4555 % zählt nicht mit). Gegen 50 % zu lesen.
**Nur bei Wechsel**, nie je Tick: sonst entsteht dieselbe Cluster-Verzerrung,
die am 07.08. elf widerlegte Hypothesen ausgelöst hat (roh 922 Zeilen 52,6 %,
entkoppelt 93 Zeilen 45,2 %) — und die mein erster Tagestest reproduzierte
(46 fortlaufende Bars als 46 Trades). Live verifiziert: **1 Zeile** trotz
laufendem Sekundentakt.
**③ AUSFÜHRUNGSQUALITÄT (Zeile ⑦).** `analyze_squeeze_entry_gap.py` existiert
seit dem 05.08. und misst die TCA-Kennzahl (Arrival-Price-Slippage) — **niemand
hat je hineingesehen.** Dabei war das der einzige Erfolgsmaßstab des
Stop-Order-Umbaus (Abstand Einstieg↔Level muss gegen 0, vorher Median
+0,275×ATR) und zugleich der **grösste je gemessene Hebel** (+0,32 R gegenüber
Market). Jetzt steht der Zählstand in der Karte: **3 von 12** Bot-Trades seit dem
Umbau.
⚠⚠ **BEIM BAU EIN NAMEERROR IM 1-SEKUNDEN-PFAD — vom LINTER gefangen.** Der
erste Entwurf berechnete `_m15` in `_run_analysis` und benutzte es in
`snapshot()`; dort gibt es weder `market` noch `wave_snap`. **`py_compile` sieht
das NICHT**, `ruff F821` meldete **4 Befunde**, darunter `_m15` selbst. Genau
die Fehlerklasse, für die der Linter am 07.08. eingebaut wurde (die beiden
`log.`-NameError in `core/trader.py` sassen Monate in `except`-Zweigen) — und
der erste echte Fang.
⚠ Zweiter Fehler beim Verschieben: der Block landete **mitten in einem
mehrzeiligen Aufruf** (`_cone = _cone_build(`) → SyntaxError. Beide behoben.
## ⚠⚠ DIE ZWEI GERATENEN M15-SCHWELLEN GEMESSEN — beide NICHT kalibrierbar (2026-08-08)
Auftrag: die Reichweite (0,5×ATR) und die Kosten-Schwelle (0,32, von M5) messen
@@ -5885,7 +5927,7 @@ besser) · Teil-Exit bei 1:1 (`_PARTIAL_TP_FRAC=0`, User-Vorgabe).
(Beobachtungen/Feedback) ergänzt diese Datei.
- `oil_widget_config.ini` enthält **Live-Secrets** (Keys/Token) — nicht ins
Repo/Logs leaken.
- **DB** `oil_widget_history.db` (trades · intended_trades · recommendations · **candles_m1** · **pbreak_predictions** · **verdict_votes**).
- **DB** `oil_widget_history.db` (trades · intended_trades · recommendations · **m15_states** · **candles_m1** · **pbreak_predictions** · **verdict_votes**).
Vor jedem Eingriff Backup (`*.bak-<datum>`); Zeitspalten sind lokale Epoch
(Broker-Offset bereits korrigiert) — **AUSNAHME `candles_m1.time` = ROHE Broker-Zeit
(UTC+3)**, deckungsgleich mit `copy_rates`.
+173 -2
View File
@@ -692,6 +692,12 @@ class TradingEngine:
except Exception as pe: log.warning(f"pbreak_predictions: {pe}")
try: self._evaluate_rec_outcomes() # ~alle 5 min, self-throttled
except Exception as re_: log.warning(f"rec_outcomes: {re_}")
# Selbstmessung der M15-Karte (2026-08-08): ohne Rueckkopplung
# koennte sie monatelang falsch stehen, ohne dass es auffaellt —
# genau die Luecke, die `pbreak_predictions` beim P(break)-Modell
# geschlossen hat.
try: self._evaluate_m15_states() # ~alle 5 min, self-throttled
except Exception as me: log.warning(f"m15_states: {me}")
# Wirtschaftskalender (self-throttled ~1 h, außerhalb des mt5_lock
# unkritisch — reiner HTTP-Fetch mit Timeout)
# HL-Kontext (Funding/OI/Premium) — reine Anzeige.
@@ -3155,6 +3161,59 @@ class TradingEngine:
self._blockmix_ts = now
return getattr(self, "_blockmix_cache", None)
def _m15_track_cached(self) -> dict | None:
"""Selbstmessung der M15-Karte — Trefferquote ihrer eigenen Lesart.
⚠ Gegen 50 % zu lesen. Gemessen wird NUR die bedingte Lesart am Level
(„Abprall → eher abwaerts"), nicht eine freie Richtungsprognose — eine
solche ist im Projekt gemessen unmoeglich (AUC 0,50 oos / 0,52
in-sample). ~120 s gecacht."""
now = time.time()
if now - getattr(self, "_m15track_ts", 0.0) > 120:
try:
self._m15track_cache = (self.history.m15_accuracy(200)
if self.history else None)
except Exception as e:
log.debug(f"m15_track: {e}")
self._m15track_cache = None
self._m15track_ts = now
return getattr(self, "_m15track_cache", None)
def _entry_gap_cached(self) -> dict | None:
"""AUSFUEHRUNGS-QUALITAET: kommt der Bot-Einstieg AM Level an?
⚠⚠ WARUM DAS DIE WICHTIGSTE UNBEOBACHTETE ZAHL WAR. Der Umbau auf
ruhende Stop-Orders (05.08.) hatte als einziges Erfolgskriterium: der
Abstand Einstieg↔Level muss gegen 0 gehen (vorher Median +0,275×ATR).
Das ist der groesste je gemessene Hebel des Projekts (+0,32 R gegenueber
Market) — und niemand hat je hineingesehen.
⚠ DIE ERFOLGSMELDUNG IST PARADOX: eine per PENDING gefuellte Order
erzeugt **keine** `AUTO-SQUEEZE-ENTRY`-Logzeile (die entsteht nur im
Market-Fallback). Bot-Trades OHNE Log-Treffer sind also der ERFOLG.
Hier wird deshalb das gezaehlt, was ohne Logdatei bestimmbar ist:
wie viele Bot-Trades es seit dem Umbau gab und wie viele davon ueber
den Market-Fallback liefen (erkennbar am Zaehler `squeeze_entry_count`
bzw. am Setup-Tag). ~300 s gecacht — DB-Abfrage gehoert nicht in den
1-s-Snapshot."""
now = time.time()
if now - getattr(self, "_entrygap_ts", 0.0) > 300:
try:
import sqlite3
con = sqlite3.connect(HISTORY_DB_FILE, timeout=5.0)
# Stichtag = Umbau auf ruhende Stop-Orders (05.08. 08:55)
seit = 1786000500
n, = con.execute(
"SELECT COUNT(*) FROM trades WHERE entry_time >= ? "
"AND setup LIKE 'SQUEEZE%'", (seit,)).fetchone()
con.close()
self._entrygap_cache = {"n_seit_umbau": n, "soll": 12}
except Exception as e:
log.debug(f"entry_gap: {e}")
self._entrygap_cache = None
self._entrygap_ts = now
return getattr(self, "_entrygap_cache", None)
def _rec_track_cached(self) -> dict | None:
"""Selbst-Kalibrierung: Trefferquote der letzten Empfehlungs-Episoden.
@@ -3398,6 +3457,105 @@ class TradingEngine:
except Exception as e:
log.debug(f"pbreak_predictions auswerten: {e}")
def _log_m15_state(self, s: dict | None, market):
"""Loggt einen Zustandswechsel der M15-Karte — die Rueckkopplung, die
der Karte bisher fehlte.
⚠⚠ NUR BEI WECHSEL. Wer je Tick loggt, bekommt dieselbe Cluster-
Verzerrung, die am 07.08. elf widerlegte Hypothesen ausgeloest hat
(roh 922 Zeilen 52,6 %, entkoppelt 93 Zeilen 45,2 %) — und die mein
erster Tagestest reproduzierte (46 fortlaufende Bars als 46 Trades).
Der Zustand wird aus Status UND Lesart gebildet: wechselt nur das
Level, ist es ein neuer Zustand; bleibt alles gleich, passiert nichts.
⚠ Fail-safe: ein Fehler hier darf den Snapshot nie stoeren.
"""
try:
if not s:
return
zonen = s.get("zonen") or []
kosten = (s.get("kosten") or {}).get("ratio")
atr = s.get("atr_m15")
bid = (market or {}).get("bid")
if not atr or not bid:
return
nah = min(zonen, key=lambda x: x["gap_atr15"]) if zonen else None
status = ("rot" if (kosten is not None and kosten >= 0.32)
else "gruen" if (nah and nah["gap_atr15"] <= 0.5) else "gelb")
# Lesart: dieselbe Regel wie im Frontend (4555 % = Muenzwurf)
lesart = "unklar"
if nah and nah.get("p_break") is not None:
pb = nah["p_break"]
if pb < 45 or pb > 55:
bricht = pb > 55
res = nah["seite"] == "res"
lesart = "auf" if (res if bricht else not res) else "ab"
schluessel = (status, lesart,
None if not nah else round(nah["level"], 3))
if schluessel == getattr(self, "_m15_last_key", None):
return # kein Wechsel → nichts loggen
self._m15_last_key = schluessel
self.history.log_m15_state(
status=status, veto=(s.get("bias") or {}).get("verboten"),
kosten=kosten, level=None if not nah else nah["level"],
gap_atr=None if not nah else nah["gap_atr15"],
p_break=None if not nah else nah.get("p_break"),
lesart=lesart, price=bid, atr=atr)
except Exception as e:
log.debug(f"m15_state log: {e}")
def _evaluate_m15_states(self):
"""Traegt den Forward-Return offener M15-Zustaende nach.
⚠ Gewertet wird die LESART, nicht der Status: `hit=1`, wenn der Kurs in
die Richtung lief, die die Karte am Level angezeigt hat. Zustaende ohne
gerichtete Lesart (Muenzwurf-Zone 4555 %) bekommen keine Richtung und
zaehlen in der Quote nicht mit.
⚠ `candles_m1.time` ist ROHE Broker-Zeit — Offset noetig (die im Projekt
mehrfach dokumentierte Falle)."""
now = time.time()
if now - getattr(self, "_m15_eval_ts", 0.0) < 300:
return
self._m15_eval_ts = now
try:
import sqlite3
off = self.trader._broker_offset_s()
con = sqlite3.connect(HISTORY_DB_FILE, timeout=5.0)
con.row_factory = sqlite3.Row
offen = con.execute(
"SELECT id, ts, lesart, price, atr, ret30, ret60 FROM m15_states "
"WHERE done IS NULL OR done = 0").fetchall()
for r in offen:
if r["lesart"] not in ("auf", "ab") or not r["atr"] or r["atr"] <= 0:
con.execute("UPDATE m15_states SET done=1 WHERE id=?", (r["id"],))
continue
d = 1 if r["lesart"] == "auf" else -1
sets, args = [], []
for feld, minuten in (("ret30", 30), ("ret60", 60)):
if r[feld] is not None:
continue
ziel = r["ts"] + minuten * 60
if now < ziel:
continue
row = con.execute(
"SELECT c FROM candles_m1 WHERE time <= ? AND time > ? "
"ORDER BY time DESC LIMIT 1",
(ziel + off, ziel + off - 900)).fetchone()
if row:
v = (row["c"] - r["price"]) * d / r["atr"]
sets.append(f"{feld}=?"); args.append(round(v, 4))
sets.append(f"hit{feld[3:]}=?"); args.append(1 if v > 0 else 0)
if now >= r["ts"] + 60 * 60 and (r["ret60"] is not None
or any("ret60" in x for x in sets)):
sets.append("done=?"); args.append(1)
elif now >= r["ts"] + 6 * 3600:
sets.append("done=?"); args.append(1) # keine Bars mehr zu erwarten
if sets:
args.append(r["id"])
con.execute(f"UPDATE m15_states SET {', '.join(sets)} WHERE id=?", args)
con.commit(); con.close()
except Exception as e:
log.debug(f"m15_states auswerten: {e}")
def _evaluate_rec_outcomes(self):
"""Traegt fuer offene Empfehlungs-Episoden den Forward-Return nach.
@@ -3614,6 +3772,17 @@ class TradingEngine:
_cone = _cone_build(
(market or {}).get("bid"),
float(((wave_snap or {}).get("pb_feats") or {}).get("atr") or 0.0))
# M15-Setup EINMAL rechnen (nicht im Dict), damit die Selbstmessung
# daran haengen kann — ein zweiter Aufruf waere Arbeit fuer dieselben
# Zahlen (der Konsens-Pfeil-Fehler).
# ⚠ MUSS hier stehen, nicht in `_run_analysis`: dort gibt es weder
# `market` noch `wave_snap`. Der erste Entwurf tat genau das und
# erzeugte einen NameError im 1-Sekunden-Pfad. `py_compile` sieht das
# NICHT, `ruff F821` schon (4 Befunde, darunter `_m15` selbst) — genau
# die Fehlerklasse, fuer die der Linter am 07.08. eingebaut wurde.
_m15 = self._m15_setup(market, wave_snap, _cone,
self._alignment_cached())
self._log_m15_state(_m15, market)
# Für den KI-Copiloten festhalten (2026-08-02) — er bekam bis dahin nur
# das Wellen-Signal und sah ausgerechnet die beiden kalibrierten Größen
# des Projekts nicht. Hier nur ZUWEISEN, nicht neu rechnen.
@@ -3799,8 +3968,10 @@ class TradingEngine:
# Gesamtempfehlung. ⚠ KEINE Richtungsaussage: auf M15 traegt gemessen
# keine (Squeeze n=33/Nachbarn kippen, VWAP-Baender ohne Edge).
# Zeigt Bedingungen, nicht Prognosen.
"m15_setup": self._m15_setup(market, wave_snap, _cone,
self._alignment_cached()),
"m15_setup": _m15,
# Selbstmessung der Karte + Ausfuehrungsqualitaet (2026-08-08).
"m15_track": self._m15_track_cached(),
"entry_gap": self._entry_gap_cached(),
"sr_close_hint": sr_close_hint,
"stop_approach": stop_approach,
"auto_sr_close": self._auto_sr_close,
+85
View File
@@ -161,6 +161,34 @@ SCHEMA = [
)
""",
"""
-- M15-KARTE misst sich SELBST (2026-08-08).
-- ⚠ WARUM: jeder Ausfall, der in diesem Projekt je aufgedeckt wurde, kam aus
-- einer Rueckkopplung — `pbreak_predictions` entlarvte den Modellausfall LIVE
-- (was kein Backtest konnte), der B4-Monitor die Squeeze-Drift. Die Karte
-- hatte nichts davon und koennte monatelang falsch stehen, ohne dass es
-- auffaellt. Geloggt wird EINE Zeile je STATUSWECHSEL (nicht je Tick — sonst
-- zaehlt man dieselbe Marktlage dutzendfach; die Lehre aus rec_outcomes und
-- der Clusterung bei pbreak_predictions).
CREATE TABLE IF NOT EXISTS m15_states (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts INTEGER, -- lokale Epoch, Beginn des Zustands
status TEXT, -- 'gruen' | 'gelb' | 'rot'
veto TEXT, -- verbotene Richtung ('LONG'/'SHORT'/NULL)
kosten REAL, -- Spread/ATR_M15
level REAL, -- naechstes stehendes Level
gap_atr REAL, -- Abstand Kurs<->Level in ATR_M15
p_break INTEGER, -- Durchbruchwahrscheinlichkeit in %
lesart TEXT, -- 'auf' | 'ab' | 'unklar' (die Richtungs-Lesart)
price REAL,
atr REAL, -- ATR_M15 zur Normierung
ret30 REAL, -- (Kurs[+30min]-price) x Lesart-Richtung / atr
ret60 REAL,
hit30 INTEGER, -- 1 = Kurs lief in Richtung der Lesart
hit60 INTEGER,
done INTEGER
)
""",
"""
CREATE TABLE IF NOT EXISTS verdict_votes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts INTEGER, -- lokale Epoch
@@ -458,6 +486,63 @@ class HistoryLogger:
conn.commit()
return cur.lastrowid
def log_m15_state(self, *, status: str, veto, kosten, level, gap_atr,
p_break, lesart, price, atr) -> int:
"""Loggt EINEN Zustandswechsel der M15-Karte.
⚠ Nur bei WECHSEL aufrufen, nicht je Tick — sonst entsteht dieselbe
Cluster-Verzerrung, die am 07.08. eine Untersuchung mit elf widerlegten
Hypothesen ausgeloest hat (roh 922 Zeilen mit 52,6 %, entkoppelt 93 mit
45,2 %). Ausgewertet wird separat gegen `candles_m1`.
⚠ Fail-open wie der Rest dieser Klasse: ein Logging-Fehler darf den
Handelspfad nie stoeren."""
try:
with self._lock, self._connect() as conn:
cur = conn.execute("""
INSERT INTO m15_states
(ts,status,veto,kosten,level,gap_atr,p_break,lesart,price,atr)
VALUES (?,?,?,?,?,?,?,?,?,?)
""", (int(time.time()), status or "", veto,
None if kosten is None else float(kosten),
None if level is None else float(level),
None if gap_atr is None else float(gap_atr),
None if p_break is None else int(p_break),
lesart, float(price or 0.0), float(atr or 0.0)))
conn.commit()
return cur.lastrowid
except Exception:
return 0
def m15_accuracy(self, last_n: int = 200) -> dict:
"""Trefferquote der LESART (nicht des Status) ueber die letzten
ausgewerteten Zustaende mit gerichteter Lesart.
⚠ Gegen 50 % zu lesen, nicht gegen 0. Und ⚠ die Karte behauptet KEINE
freie Richtung — gemessen wird nur, ob die BEDINGTE Lesart am Level
(„Abprall -> eher abwaerts") getroffen hat."""
try:
with self._lock, self._connect() as conn:
rows = conn.execute("""
SELECT hit30, hit60, ret30 FROM m15_states
WHERE done=1 AND lesart IN ('auf','ab')
ORDER BY id DESC LIMIT ?
""", (int(last_n),)).fetchall()
offen = conn.execute(
"SELECT COUNT(*) FROM m15_states WHERE done IS NULL "
"AND lesart IN ('auf','ab')").fetchone()[0]
except Exception:
return {"n": 0}
n = len(rows)
if not n:
return {"n": 0, "offen": offen}
h30 = [r[0] for r in rows if r[0] is not None]
h60 = [r[1] for r in rows if r[1] is not None]
r30 = [r[2] for r in rows if r[2] is not None]
return {"n": n, "offen": offen,
"wr30": round(100 * sum(h30) / len(h30)) if h30 else None,
"wr60": round(100 * sum(h60) / len(h60)) if h60 else None,
"avg30": round(sum(r30) / len(r30), 3) if r30 else None}
def rec_accuracy(self, last_n: int = 200) -> dict:
"""Trefferquote der letzten `last_n` ausgewerteten Empfehlungs-Episoden.
+30
View File
@@ -252,6 +252,36 @@ function renderM15(s) {
? `⑤ gegen das Signal: ${g.n} Trades, ${f(g.pnl_lot, 2)} € je Lot`
: "⑤ Veto: noch zu wenige Trades";
// ── ⑥ Selbstmessung: die Karte zeigt ihre EIGENE Trefferquote ────────
// ⚠ Gegen 50 % lesen. Gemessen wird die BEDINGTE Lesart am Level, nicht
// eine freie Prognose — die ist gemessen unmöglich (AUC 0,52 in-sample).
const tk = s.m15_track || {};
const tEl = $("m15-track");
if (tk.n >= 20 && tk.wr30 != null) {
tEl.className = "m15-item " + (tk.wr30 >= 55 ? "ok" : tk.wr30 <= 45 ? "warn" : "");
tEl.textContent = `⑥ eigene Trefferquote (${tk.n} Zustände): ${tk.wr30} % auf 30`
+ (tk.wr60 != null ? ` · ${tk.wr60} % auf 60` : "")
+ ` · Ø ${tk.avg30 == null ? "—" : (tk.avg30 >= 0 ? "+" : "") + f(tk.avg30, 3)}×ATR`;
} else {
tEl.className = "m15-item";
tEl.textContent = `⑥ eigene Trefferquote: sammelt noch`
+ (tk.n ? ` (${tk.n} von 20 ausgewertet)` : "")
+ (tk.offen ? ` · ${tk.offen} offen` : "");
}
// ── ⑦ Ausführungsqualität — der einzige gemessen grosse Hebel ────────
const eg = s.entry_gap || {};
const eEl = $("m15-exec");
if (eg.n_seit_umbau != null) {
const genug = eg.n_seit_umbau >= (eg.soll || 12);
eEl.className = "m15-item " + (genug ? "ok" : "");
eEl.textContent = `⑦ Ausführung: ${eg.n_seit_umbau} Bot-Trades seit dem `
+ `Stop-Order-Umbau (Prüfung ab ${eg.soll || 12})`;
} else {
eEl.className = "m15-item";
eEl.textContent = "⑦ Ausführung —";
}
// ── Status: die Kette wird der Reihe nach ausgewertet ────────────────
// ⚠ Das Banner nennt AUSDRUECKLICH keine Richtung. Es sagt, ob die
// BEDINGUNGEN fuer eine Order am Level stehen — und was verboten ist.
+9 -2
View File
@@ -6,7 +6,7 @@
<meta name="theme-color" content="#0d1117">
<title>Oil · MT5</title>
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="style.css?v=156">
<link rel="stylesheet" href="style.css?v=157">
</head>
<body>
<!-- Modul-Menü (ganz oben) -->
@@ -109,6 +109,13 @@
<div class="m15-item" id="m15-kosten" title="Spread geteilt durch ATR_M15. Auf M15 gemessen 0,104 gegen 0,193 auf M5 - hier ist die groebere Zeitebene echt im Vorteil. Schwellen 0,20 / 0,32 aus backtest_realcosts.py."></div>
<div class="m15-item" id="m15-zone" title="Naechstes STEHENDES Level (M30-Pivots, dieselbe Quelle wie Chart und S/R-Close) mit Abstand und Durchbruchwahrscheinlichkeit. ⚠ P(break) ist live derzeit rund 7 Pp zu niedrig kalibriert."></div>
<div class="m15-item" id="m15-reich" title="Erwartete Spanne aus core/cone.py - out-of-sample geprueft, die 80-%-Baender treffen real 77 %. Sagt WIE WEIT, nicht wohin."></div>
<!-- ⑥ SELBSTMESSUNG: die Karte zeigt die Trefferquote ihrer EIGENEN
Lesart. ⚠ Jeder Ausfall, der in diesem Projekt je aufgedeckt wurde,
kam aus einer Rueckkopplung — pbreak_predictions entlarvte den
Modellausfall LIVE, was kein Backtest konnte. -->
<div class="m15-item" id="m15-track" title="Trefferquote der Lesart dieser Karte, nach 30/60 Minuten gegen den echten Kurs ausgewertet. Gegen 50 % zu lesen, nicht gegen 0. Geloggt wird EIN Zustand je Wechsel, nicht je Tick - sonst zaehlt man dieselbe Marktlage dutzendfach."></div>
<!-- ⑦ AUSFUEHRUNGSQUALITAET: der einzige gemessen grosse Hebel (+0,32 R). -->
<div class="m15-item" id="m15-exec" title="Der Umbau auf ruhende Stop-Orders (05.08.) hatte als Erfolgskriterium: der Abstand Einstieg zum Level muss gegen 0 gehen (vorher Median +0,275xATR). Das ist der groesste je gemessene Hebel des Projekts - und war bis heute unbeobachtet."></div>
<div class="m15-item" id="m15-veto" title="Deine eigene Bilanz mit/ohne/gegen Signal, je Lot gerechnet. Gegen das Signal zu handeln kostet gemessen 6,60 EUR je Lot - beidhaelftig robust."></div>
</div>
</section>
@@ -340,6 +347,6 @@
</div>
<div id="toast" class="toast hidden"></div>
<script src="app.js?v=156"></script>
<script src="app.js?v=157"></script>
</body>
</html>