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:
co-authored by
Claude Opus 5
parent
5ddbe07066
commit
86c432f791
@@ -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
|
||||
(45–55 % 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
@@ -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 (45–55 % = 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 45–55 %) 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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user