P(break) v2 live geprueft: Kalibrierung daneben, Trennschaerfe unentschieden

Faellige B4-Messung nach dem Nachtrainieren vom 31.07. (878 Vorhersagen ab
01.08.). Drei Befunde.

1) Das Skript mischte zwei Modelle. Es trennte bei der M30-Umstellung (30.07.),
nicht beim Nachtrainieren (31.07.) - "GESAMT" las altes und neues Modell
zusammen. Derselbe Fehler wie am 02.08. in analyze_divergence.py. Neu:
MODELL_V2 mit demselben Stichtag wie die Faelligkeitsbedingung.

2) Kalibrierung messbar daneben. Die Basisrate ist gewandert: Training 37,8 %,
live 53,2 %, stabil ueber vier volle Tage (51,4 / 54,1 / 56,4 / 53,8 % bei
n=122-276). Ein Modell, dessen Niveau an 37,8 % verankert ist, muss in einem
53-%-Regime systematisch zu niedrig sagen - genau das passiert. Oberste Klasse
invertiert (69 % vorhergesagt, 17 % real).

3) Trennschaerfe NICHT entscheidbar - und das ist der wichtigere Befund.
Entkoppelt n=87, AUC 0,556, 95-%-Bootstrap-KI [0,434 ... 0,674]. Das Intervall
enthaelt den Zufall (0,50) UND die Messlatte (0,654).

Die Faelligkeitsbedingung zaehlte die falsche Groesse: 800 ROHE Vorhersagen,
die aber nicht unabhaengig sind (dieselbe Position pendelt um dasselbe Level).
Aus 878 rohen werden 87 entkoppelte - die Bedingung ueberschaetzte die
Beweismenge um rund das Zehnfache und meldete "faellig", obwohl die Messung
nichts entscheiden konnte. Jetzt 350 ENTKOPPELTE (halbiert die KI-Breite).
Stand 87/350.

Operativ kein Notfall: auto_sr_close ist seit 06.08. false, das Modell
schliesst keine Trades. Es speist nur Anzeige, Chart-Linien und
Copilot-Kontext.

Nichts umgestellt - ein drittes Fitten derselben Bauart wuerde denselben Weg
gehen; der Befund zeigt aufs Niveau, nicht zwingend auf die Rangfolge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-07 07:43:24 +02:00
co-authored by Claude Opus 5
parent 3e8fa9fea8
commit 908e20a9d0
3 changed files with 117 additions and 5 deletions
+63
View File
@@ -4785,6 +4785,69 @@ Signal-Eingriffe sind gescheitert, und die gemessene Stärke des Users liegt in
AUSWAHL, nicht in einer Regel. Ein Modul, das ihm seine eigene Historie zur aktuellen
Lage zeigt, spielt auf diese Stärke; eines das für ihn einsteigt, ersetzt sie.
## ⚠⚠ P(break) v2 LIVE GEPRÜFT (2026-08-07) — KALIBRIERUNG DANEBEN, TRENNSCHÄRFE UNENTSCHIEDEN
Fällige B4-Messung nach dem Nachtrainieren vom 31.07. (`analyze_pbreak_live.py`,
878 ausgewertete Vorhersagen ab 01.08.). Zwei Befunde, die auseinandergehalten
gehören — und einer davon betrifft die Messung selbst.
⚠⚠ **ZUERST: DAS SKRIPT MISCHTE ZWEI MODELLE.** Es trennte bei der
M30-Level-Umstellung (30.07.), **nicht** beim Nachtrainieren (31.07.); „GESAMT"
las damit altes und neues Modell zusammen. Genau der Fehler, der am 02.08. in
`analyze_divergence.py` schon behoben wurde (`_EPOCHS`) — hier stand er noch.
Neu: `MODELL_V2` mit demselben Stichtag wie die Fälligkeitsbedingung (01.08.),
plus eigener Abschnitt „NACHTRAINIERTES MODELL". Die alten Abschnitte bleiben.
**(1) KALIBRIERUNG — messbar daneben, und die Ursache ist benennbar.**
| P(break) | n | vorhergesagt | real |
|---|---|---|---|
| 019 | 56 | 15,1 % | **53,6 %** |
| 2034 | 199 | 29,8 % | 43,2 % |
| 3549 | 414 | 42,2 % | 56,8 % |
| 5064 | 180 | 54,1 % | 61,7 % |
| **65100** | 29 | **69,0 %** | **17,2 %** ⚠ invertiert |
**Die Basisrate ist gewandert** — Training **37,8 %**, live **53,2 %**, und das
**stabil über vier volle Tage** (03.08. 51,4 % · 04.08. 54,1 % · 05.08. 56,4 % ·
06.08. 53,8 %, je n=122276). Das ist kein Rauschen. Ein Modell, dessen Niveau an
einer 37,8-%-Basisrate verankert ist, muss in einem 53-%-Regime systematisch zu
niedrig sagen — genau das steht in der Tabelle. Dieselbe Signatur wie beim alten
Modell (alle unteren Klassen „zu niedrig", oberste invertiert).
**(2) TRENNSCHÄRFE — NICHT entscheidbar, und das ist der wichtigere Befund.**
| | |
|---|---|
| AUC roh (n=878) | 0,541 |
| **AUC entkoppelt (n=87)** | **0,556** |
| **95-%-Bootstrap-KI** | **[0,434 … 0,674]** |
Das Intervall enthält **den Zufall (0,50) UND die Messlatte (0,654)**. Es ist
also **nicht** belegt, dass das Modell schlechter rankt als beim Nachtrainieren —
es ist nur nichts belegt. „AUC 0,54" als Durchfall zu lesen wäre falsch.
⚠⚠ **DIE FÄLLIGKEITSBEDINGUNG ZÄHLTE DIE FALSCHE GRÖSSE — behoben.** Sie stand
auf **800 ROHEN** Vorhersagen; die sind nicht unabhängig (dieselbe Position
pendelt vielfach um dasselbe Level). Aus 878 rohen werden **87 entkoppelte**
die Bedingung hat die Beweismenge um **rund das Zehnfache** überschätzt und die
Messung als „fällig" gemeldet, obwohl sie nichts entscheiden konnte. Die
Abhängigkeit war im Skript seit jeher dokumentiert („entkoppelte Sicht"), nur die
Fälligkeit zählte daran vorbei. **Jetzt: 350 ENTKOPPELTE** (halbiert die
KI-Breite 0,12 → 0,06, erst dann sind 0,50 und 0,65 trennbar; bei ~17/Tag ≈ 3
Wochen). Stand: **87/350**.
⚠ Verwandt mit der Bootstrap-Grenze bei `backtest_cost_gate.ci95`
(„überlappende Beobachtungen → effektives n ist ~n/24") — dieselbe Klasse Fehler,
diesmal in einer Fälligkeitsbedingung statt in einem Konfidenzintervall.
**Operativ kein Notfall:** `auto_sr_close = false` seit 06.08. — das Modell
schliesst **keine** Trades. Es speist nur den Hinweis in der App, die
Chart-Linien, den Copilot-Kontext und `stop_approach`. Die Fehlkalibrierung
kostet damit aktuell **kein Geld**, sie macht nur die Anzeige zu pessimistisch
(sie sagt „Level hält", wo es in 53 % der Fälle bricht).
**Vor einem erneuten Nachtrainieren:** der letzte Versuch hat live auch nicht
gehalten. Der Befund zeigt auf das **Niveau** (Basisrate/Achsenabschnitt), nicht
zwingend auf die Rangfolge — ein drittes Fitten derselben Bauart würde denselben
Weg gehen. Naheliegender wäre, das Niveau **laufend** an die jüngste Basisrate zu
koppeln oder ein Vola-/Regime-Merkmal aufzunehmen. **Nicht gebaut, nicht
gemessen.**
## ⚠⚠ LIVE-CODE HING AN EINEM BACKTEST-SKRIPT — behoben 2026-08-07
**Befund:** `core/engine.py` (`squeeze_monitor`) und `weekly_review.py` importierten
+37 -2
View File
@@ -28,6 +28,17 @@ from datetime import datetime
DB = "oil_widget_history.db"
M30_SWITCH = datetime(2026, 7, 30).timestamp()
# ⚠⚠ MODELL-EPOCHE (ergänzt 2026-08-07). Am 31.07. wurde das P(break)-Modell
# NACHTRAINIERT (`backtest_pbreak_retrain.py`) und die Schwelle von 0,55 auf 0,35
# gesenkt. Die Tabelle `pbreak_predictions` enthält beides — Vorhersagen des ALTEN
# und des NEUEN Modells. Wer sie zusammen auswertet, misst eine Mischung und liest
# die Fehlkalibrierung des alten Modells als aktuellen Alarm.
# Derselbe Fehler wurde am 02.08. in `analyze_divergence.py` behoben (`_EPOCHS`);
# hier stand er noch. Stichtag identisch zur Fälligkeitsbedingung in
# `measurement_reminder.py` ("pbreak_accuracy_v2", ts >= 2026-08-01), damit
# Erinnerung und Auswertung dieselbe Grundmenge zählen.
MODELL_V2 = datetime(2026, 8, 1).timestamp()
def auc(ps, ys):
"""Rangbasiert, mit Bindungskorrektur."""
@@ -106,8 +117,32 @@ def main():
print(f" {len(rows)} ausgewertete Vorhersagen, {t0:%d.%m.%Y} bis {t1:%d.%m.%Y}")
print("=" * 84)
report(rows, "GESAMT")
calib(rows, "gesamt")
# ⚠ GESAMT mischt ALTES und NEUES Modell (s. MODELL_V2 oben) — bleibt als
# Historie stehen, ist aber NICHT der B4-Maßstab.
report(rows, "GESAMT ⚠ mischt altes + nachtrainiertes Modell")
calib(rows, "gesamt (gemischt)")
# ── DAS ist die fällige B4-Messung: nur das nachtrainierte Modell ──────
v2 = [(float(p), 1 if o == "break" else 0, pr)
for ts, _d, _l, p, pr, o in raw if ts >= MODELL_V2]
print("\n" + "=" * 84)
print(" NACHTRAINIERTES MODELL (ab 01.08.) — die fällige B4-Prüfung")
print(" Messlatte: AUC 0,654 (out-of-sample beim Nachtrainieren erreicht)")
print("=" * 84)
report(v2, "MODELL v2")
if len(v2) >= 30:
calib(v2, "Modell v2")
# entkoppelt auch hier — der Cluster-Effekt gilt unabhängig vom Modell
seen2 = set(); dd2 = []
for ts, _d, lvl, p, pr, o in raw:
if ts < MODELL_V2:
continue
key = (round(float(lvl or 0), 2), ts // 3600)
if key in seen2:
continue
seen2.add(key)
dd2.append((float(p), 1 if o == "break" else 0, pr))
report(dd2, "MODELL v2 · ENTKOPPELT (max. 1 je Level und Stunde)")
# ── entkoppelt: max. 1 Vorhersage je (Level, Stunde)
seen = set(); dedup = []
+17 -3
View File
@@ -62,9 +62,23 @@ CHECKS = [
"backtest_srclose_prob.py aufnehmen und neu fitten (live in beiden "
"Hälften +6/+4 Pp für die 2.4. Berührung, 5.+ fällt zurück). "
"Danach mit frischen Live-Daten gegenprüfen.",
"have": lambda: _q("SELECT COUNT(*) FROM pbreak_predictions "
"WHERE outcome IS NOT NULL AND ts >= strftime('%s','2026-08-01')"),
"need": 800, "unit": "Vorhersagen seit 01.08.",
# ⚠⚠ EINHEIT KORRIGIERT 2026-08-07 — die alte Bedingung zählte die FALSCHE
# Größe. Sie stand auf 800 ROHEN Vorhersagen; die sind aber NICHT
# unabhängig (dieselbe Position pendelt vielfach um dasselbe Level). Am
# 07.08. war sie mit 878 „fällig" — entkoppelt (max. 1 je Level und
# Stunde) waren es **87**, und das 95-%-Bootstrap-Intervall der AUC ging
# von 0,434 bis 0,674: es enthält sowohl den Zufall (0,50) als auch die
# Messlatte (0,654). Die Messung konnte also gar nichts entscheiden.
# Die Abhängigkeit war im Skript seit jeher dokumentiert („entkoppelte
# Sicht"), nur die Fälligkeit zählte daran vorbei — sie überschätzte die
# Beweismenge um rund das Zehnfache.
# 350 entkoppelte Zeilen ≈ halbe KI-Breite (0,12 → 0,06), erst dann sind
# 0,50 und 0,65 überhaupt trennbar. Bei ~17/Tag sind das ~3 Wochen.
"have": lambda: _q(
"SELECT COUNT(*) FROM (SELECT DISTINCT ROUND(level,2), ts/3600 "
"FROM pbreak_predictions WHERE outcome IS NOT NULL "
"AND ts >= strftime('%s','2026-08-01'))"),
"need": 350, "unit": "ENTKOPPELTE Vorhersagen seit 01.08. (max. 1 je Level und Stunde)",
"cmd": "python analyze_pbreak_live.py",
},
{