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
+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.