M15-Karte: Flacker-Ursache gemessen, Hysterese + EINE Quelle (v=165)
Die Karte wechselte den Zustand alle 12 Sekunden (~1.030/Tag) - der Grund, warum ihre Selbstmessung so stark geclustert war. Mein erster Verdacht (das Level im Wechsel-Schluessel) war falsch. Ausgezaehlt ueber 3.525 Zeilen: status 32,9 %, lesart 32,7 %, level allein nur 7,6 %. Das Level zu entfernen braechte 3.525 -> 3.248, also nichts. Der Zustand selbst zappelt: gap <= 0,5 und das 45/55-Band von P(break) sind stetige Groessen um ihre Schwelle. Das Projekt hatte die Loesung schon (analyze_pbreak_flicker.py, 14.07.: Hysterese +-5 Pp) - die Karte umging sie, weil sie P(break) selbst neu rechnet. Jetzt dieselbe Hysterese auf beide Schwellen: Status gruen ab 0,50 / zurueck erst ueber 0,60; Lesart gerichtet ab 40/60 / zurueck erst innerhalb 45/55. Simuliert: 64 % weniger Wechsel. Zweiter, groesserer Befund: Status und Lesart wurden an DREI Stellen unabhaengig gebildet und wichen bereits ab - der Logger kannte den "kein Veto"-Fall des Frontends nicht, protokollierte also einen anderen Zustand als angezeigt wurde. Jetzt eine Quelle: _m15_setup liefert status/lesart/nah im Snapshot, Logger und Frontend lesen sie. Die alten 3.525 Zeilen sind mit den neuen nicht vergleichbar; die Selbstmessung beginnt faktisch neu (Stichtag 12.08.). Kein Verlust - sie liess vorher ohnehin kein Urteil zu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e882d795a6
commit
5fb1d7e268
@@ -6161,6 +6161,49 @@ Neumessung. Sie bleiben unverändert und reproduzierbar; wo ihre Schlussfolgerun
|
|||||||
relevant wird, gehört sie live-treu **neu gerechnet** (Muster:
|
relevant wird, gehört sie live-treu **neu gerechnet** (Muster:
|
||||||
`backtest_auto_signal_norev.py`).
|
`backtest_auto_signal_norev.py`).
|
||||||
|
|
||||||
|
## ⚠⚠ DAS FLACKERN DER M15-KARTE — Ursache gemessen, Hysterese eingebaut (2026-08-12)
|
||||||
|
|
||||||
|
Die Karte wechselte den Zustand **alle 12 Sekunden** (~1.030/Tag). Das ist der
|
||||||
|
Grund, warum ihre Selbstmessung so stark geclustert war (roh 60 %, entkoppelt
|
||||||
|
53 %).
|
||||||
|
|
||||||
|
⚠⚠ **MEIN ERSTER VERDACHT WAR FALSCH — und die Messung war schnell.** Ich hatte
|
||||||
|
das Level im Wechsel-Schlüssel (`round(level, 3)`) im Verdacht. Ausgezählt über
|
||||||
|
3.525 Zeilen:
|
||||||
|
| Was ändert sich zwischen zwei Zeilen? | Anteil |
|
||||||
|
|---|---|
|
||||||
|
| **`status`** | 32,9 % |
|
||||||
|
| **`lesart`** | 32,7 % |
|
||||||
|
| lesart+level | 20,3 % |
|
||||||
|
| **level allein** | **7,6 %** |
|
||||||
|
Das Level aus dem Schlüssel zu nehmen brächte 3.525 → 3.248 Zeilen — praktisch
|
||||||
|
nichts. **Der Zustand selbst zappelt**: `gap ≤ 0,5` und das 45/55-Band von
|
||||||
|
P(break) sind stetige Grössen, die um ihre Schwelle pendeln.
|
||||||
|
|
||||||
|
✅ **Das Projekt hatte die Lösung schon** (`analyze_pbreak_flicker.py`, 14.07.:
|
||||||
|
Zeit-Glättung + **Hysterese ±5 Pp**). Die Karte umging sie, weil sie P(break)
|
||||||
|
selbst neu rechnet. Jetzt dieselbe Hysterese auf beide Schwellen:
|
||||||
|
· **Status**: grün ab `gap ≤ 0,50`, zurück auf gelb erst **über 0,60**
|
||||||
|
· **Lesart**: gerichtet erst ausserhalb **40/60**, zurück auf „unklar" erst
|
||||||
|
innerhalb **45/55**
|
||||||
|
Simuliert an 4.000 Ticks direkt auf den Schwellen: **3.084 → 1.103 Wechsel
|
||||||
|
= 64 % weniger.**
|
||||||
|
|
||||||
|
⚠⚠ **ZWEITER, GRÖSSERER BEFUND: Status und Lesart wurden an DREI Stellen
|
||||||
|
unabhängig gebildet** — Frontend, `_log_m15_state` und die Karte selbst. **Sie
|
||||||
|
wichen bereits ab**: der Logger kannte den „kein Veto"-Fall des Frontends nicht,
|
||||||
|
hat also einen anderen Zustand protokolliert als angezeigt wurde. Genau die
|
||||||
|
Divergenz-Falle, die im Projekt schon dreimal zugeschlagen hat (`sim_run` in ≥6
|
||||||
|
Skripten · der nachgebaute Level-Cluster · das fünfte Exit-Modell).
|
||||||
|
✅ Jetzt **EINE Quelle**: `_m15_setup` liefert `status`/`lesart`/`nah` im
|
||||||
|
Snapshot, Logger und Frontend lesen sie. Anzeige und Messung können nicht mehr
|
||||||
|
auseinanderlaufen.
|
||||||
|
|
||||||
|
⚠ **Die alten 3.525 Zeilen sind mit den neuen NICHT vergleichbar** — sie wurden
|
||||||
|
unter dem flackernden Regime geschrieben. Die Selbstmessung beginnt faktisch neu;
|
||||||
|
das ist kein Verlust, weil sie vorher ohnehin kein Urteil zuliess (KI enthielt
|
||||||
|
die 50). Stichtag: 12.08.
|
||||||
|
|
||||||
## ⚠⚠ DIE M15-KARTE HAT SICH ZUM ERSTEN MAL SELBST GEMESSEN (2026-08-12)
|
## ⚠⚠ DIE M15-KARTE HAT SICH ZUM ERSTEN MAL SELBST GEMESSEN (2026-08-12)
|
||||||
|
|
||||||
Anlass: User-Frage „könntest du mir auch eine **M5** · Setup-Bereitschaft bauen?
|
Anlass: User-Frage „könntest du mir auch eine **M5** · Setup-Bereitschaft bauen?
|
||||||
|
|||||||
+58
-11
@@ -3574,17 +3574,19 @@ class TradingEngine:
|
|||||||
bid = (market or {}).get("bid")
|
bid = (market or {}).get("bid")
|
||||||
if not atr or not bid:
|
if not atr or not bid:
|
||||||
return
|
return
|
||||||
nah = min(zonen, key=lambda x: x["gap_atr15"]) if zonen else None
|
# ⚠⚠ Status und Lesart werden NICHT mehr hier nachgerechnet
|
||||||
status = ("rot" if (kosten is not None and kosten >= 0.32)
|
# (2026-08-12). Sie kommen aus `_m15_setup` — EINE Quelle fuer
|
||||||
else "gruen" if (nah and nah["gap_atr15"] <= 0.5) else "gelb")
|
# Anzeige UND Messung, mit Hysterese gegen das Schwellen-Flattern.
|
||||||
# Lesart: dieselbe Regel wie im Frontend (45–55 % = Muenzwurf)
|
# Vorher gab es drei unabhaengige Fassungen, und sie wichen bereits
|
||||||
lesart = "unklar"
|
# ab: dieser Logger kannte den „kein Veto"-Fall des Frontends nicht.
|
||||||
if nah and nah.get("p_break") is not None:
|
nah = s.get("nah")
|
||||||
pb = nah["p_break"]
|
status = s.get("status")
|
||||||
if pb < 45 or pb > 55:
|
lesart = s.get("lesart")
|
||||||
bricht = pb > 55
|
if not status:
|
||||||
res = nah["seite"] == "res"
|
return
|
||||||
lesart = "auf" if (res if bricht else not res) else "ab"
|
# ⚠ Das Level bleibt im Schluessel: ein anderes Level IST ein anderes
|
||||||
|
# Setup. Gemessen ist es ohnehin nicht der Treiber (nur 7,6 % der
|
||||||
|
# Wechsel gingen allein darauf zurueck).
|
||||||
schluessel = (status, lesart,
|
schluessel = (status, lesart,
|
||||||
None if not nah else round(nah["level"], 3))
|
None if not nah else round(nah["level"], 3))
|
||||||
if schluessel == getattr(self, "_m15_last_key", None):
|
if schluessel == getattr(self, "_m15_last_key", None):
|
||||||
@@ -4661,6 +4663,51 @@ class TradingEngine:
|
|||||||
out["ziel_grund"] = (None if d_ok else
|
out["ziel_grund"] = (None if d_ok else
|
||||||
"kein Veto — Richtung offen, kein Ziel bestimmbar")
|
"kein Veto — Richtung offen, kein Ziel bestimmbar")
|
||||||
|
|
||||||
|
# ── 4d) STATUS + LESART mit HYSTERESE — EINMAL berechnet ────────
|
||||||
|
# ⚠⚠ ANLASS (2026-08-12): die Karte wechselte den Zustand **alle 12
|
||||||
|
# Sekunden** (~1.030 Wechsel/Tag). Gemessen an den Logzeilen war
|
||||||
|
# NICHT das Level die Ursache (nur 7,6 %), sondern **Schwellen-
|
||||||
|
# Flattern**: `gap ≤ 0,5` und das 45/55-Band von P(break) sind
|
||||||
|
# stetige Groessen, die um die Schwelle pendeln.
|
||||||
|
# Folge: die Selbstmessung war massiv geclustert — roh 60 %,
|
||||||
|
# entkoppelt 53 %.
|
||||||
|
# ✅ Das Projekt hat genau dieses Problem am 14.07. schon geloest
|
||||||
|
# (`analyze_pbreak_flicker.py`): Zeit-Glaettung + **Hysterese
|
||||||
|
# ±5 Pp**. Die Karte umging sie, weil sie P(break) selbst neu
|
||||||
|
# rechnet. Hier ist dieselbe Hysterese, auf beide Schwellen.
|
||||||
|
# ⚠⚠ UND: Status/Lesart wurden an DREI Stellen unabhaengig gebildet
|
||||||
|
# (Frontend, `_log_m15_state`, hier) — sie wichen bereits ab (der
|
||||||
|
# Logger kannte den „kein Veto"-Fall nicht). Jetzt EINE Quelle;
|
||||||
|
# Anzeige und Messung koennen nicht mehr auseinanderlaufen.
|
||||||
|
hy = getattr(self, "_m15_hyst", None) or {}
|
||||||
|
nah_z = min(zonen, key=lambda x: x["gap_atr15"]) if zonen else None
|
||||||
|
# (a) Status: gruen ab gap ≤ 0,50 — zurueck auf gelb erst > 0,60
|
||||||
|
war_gruen = hy.get("status") == "gruen"
|
||||||
|
if kr is not None and kr >= 0.32:
|
||||||
|
status = "rot"
|
||||||
|
elif not nah_z:
|
||||||
|
status = "gelb"
|
||||||
|
else:
|
||||||
|
g = nah_z["gap_atr15"]
|
||||||
|
status = "gruen" if (g <= (0.60 if war_gruen else 0.50)) else "gelb"
|
||||||
|
# (b) Lesart: gerichtet erst ausserhalb 40/60, zurueck auf „unklar"
|
||||||
|
# erst innerhalb 45/55 (die dokumentierten ±5 Pp).
|
||||||
|
lesart = "unklar"
|
||||||
|
if nah_z and nah_z.get("p_break") is not None:
|
||||||
|
pb = nah_z["p_break"]
|
||||||
|
war_ger = hy.get("lesart") in ("auf", "ab")
|
||||||
|
lo_s, hi_s = (45, 55) if war_ger else (40, 60)
|
||||||
|
if pb < lo_s or pb > hi_s:
|
||||||
|
bricht = pb > hi_s if not war_ger else pb > 55
|
||||||
|
res = nah_z["seite"] == "res"
|
||||||
|
lesart = "auf" if (res if bricht else not res) else "ab"
|
||||||
|
elif war_ger:
|
||||||
|
lesart = hy.get("lesart") # im Band: alten Zustand halten
|
||||||
|
self._m15_hyst = {"status": status, "lesart": lesart}
|
||||||
|
out["status"] = status
|
||||||
|
out["lesart"] = lesart
|
||||||
|
out["nah"] = nah_z
|
||||||
|
|
||||||
# ── 4c) VOLA-REGIME als Slippage-HINWEIS (kein Gate, s. Docstring)
|
# ── 4c) VOLA-REGIME als Slippage-HINWEIS (kein Gate, s. Docstring)
|
||||||
out["vola"] = self._vola_regime(atr15)
|
out["vola"] = self._vola_regime(atr15)
|
||||||
|
|
||||||
|
|||||||
+8
-1
@@ -189,9 +189,16 @@ function renderM15(s) {
|
|||||||
+ slipTxt;
|
+ slipTxt;
|
||||||
|
|
||||||
// ── Gate 3: Level in Reichweite ──────────────────────────────────────
|
// ── Gate 3: Level in Reichweite ──────────────────────────────────────
|
||||||
|
// ⚠⚠ `zOk` kommt seit 2026-08-12 aus dem BACKEND (`s.status`), nicht mehr aus
|
||||||
|
// einem eigenen Schwellenvergleich hier. Grund: die Karte wechselte den
|
||||||
|
// Zustand alle 12 Sekunden — Schwellen-Flattern an `gap ≤ 0,5`. Das Backend
|
||||||
|
// rechnet es EINMAL mit Hysterese (grün ab 0,50, zurück erst über 0,60) und
|
||||||
|
// liefert Status und Lesart mit; Anzeige und Selbstmessung können damit nicht
|
||||||
|
// mehr auseinanderlaufen (sie taten es bereits: der Logger kannte den
|
||||||
|
// „kein Veto"-Fall nicht).
|
||||||
const z = (s.zonen || []);
|
const z = (s.zonen || []);
|
||||||
const nah = z.filter(x => x.gap_atr15 != null && x.gap_atr15 <= 0.5);
|
const nah = z.filter(x => x.gap_atr15 != null && x.gap_atr15 <= 0.5);
|
||||||
const zOk = nah.length > 0;
|
const zOk = s.status ? (s.status === "gruen") : (nah.length > 0);
|
||||||
$("m15-zone").className = "m15-item " + (z.length ? (zOk ? "ok" : "warn") : "");
|
$("m15-zone").className = "m15-item " + (z.length ? (zOk ? "ok" : "warn") : "");
|
||||||
// ⚠⚠ RICHTUNGS-LESART, aber BEDINGT auf das Level. Eine FREIE Prognose ist
|
// ⚠⚠ RICHTUNGS-LESART, aber BEDINGT auf das Level. Eine FREIE Prognose ist
|
||||||
// gemessen unmoeglich (`analyze_reversal.py`: AUC 0,499–0,509 oos und
|
// gemessen unmoeglich (`analyze_reversal.py`: AUC 0,499–0,509 oos und
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
<meta name="theme-color" content="#0d1117">
|
<meta name="theme-color" content="#0d1117">
|
||||||
<title>Oil · MT5</title>
|
<title>Oil · MT5</title>
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
<link rel="stylesheet" href="style.css?v=164">
|
<link rel="stylesheet" href="style.css?v=165">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Modul-Menü (ganz oben) -->
|
<!-- Modul-Menü (ganz oben) -->
|
||||||
@@ -354,6 +354,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="toast" class="toast hidden"></div>
|
<div id="toast" class="toast hidden"></div>
|
||||||
|
|
||||||
<script src="app.js?v=164"></script>
|
<script src="app.js?v=165"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user