Verdict: Chartmuster + Orderbuch + Liquiditaets-Trend als Stimmen (v=121)

User-Vorgaben: "beruecksichtige die chartmuster mit in der gesamtempfehlung sobald
eins erkannt wird" + "auch der liquiditaetstrend und das orderbuch".

MUSTER (Gewicht 1,0 bestaetigt / 0,5 bildet sich) -- vorher den faelligen
KONTROLLTEST nachgeholt (stand seit heute im measurement_reminder):
  Muster    H1 +0.206 (Ziel 23%)  H2 +0.328 (Ziel 26%)
  Kontrolle H1 +0.184 (Ziel 19%)  H2 +0.220 (Ziel 18%)   <- beliebiger Swing-Bruch
  Delta     H1 +0.022             H2 +0.108
Die Muster schlagen die Kontrolle in BEIDEN Haelften, aber der Loewenanteil des
Ertrags kommt vom Breakout+Trailing. Deshalb Gewicht 1,0 (wie Elliott/KI), nicht
2,0 (Squeeze). Ziel-Trefferquote bleibt niedrig -> die Stimme sagt Richtungs-
tendenz, kein Kursziel.

ORDERBUCH-Imbalance + LIQ-TREND (Gewicht je 0,5 = kleinstes im Verdict):
⚠ BEIDE UNGEMESSEN. Die Uebertragbarkeit HL->CFD wird gerade erst erhoben
(analyze_bookflow.py, ~3% der Daten). Der Lead-Lag-Test zeigt sogar, dass
PEPPERSTONE fuehrt (r=0.525 bei k=-1) und HL folgt -- HL ist Oracle-basierter
HIP-3-Perp, strukturell Follower. Daher das kleinste Gewicht.
Imbalance-Schwelle _OB_IMB_MIN=0.30, darunter keine Stimme.

MITLOGGING: verdict_votes um pattern/pattern_w/orderbook/orderbook_w/liqtrend/
liqtrend_w erweitert (DB migriert, Backup angelegt). Nur so ist in ein paar Wochen
datenbasiert entscheidbar, ob die Stimmen bleiben, mehr Gewicht bekommen oder
rausfliegen (wie TU 2026-07-06).

Frontend: Chips rendern dynamisch (keine Aenderung noetig); NEU wird das GEWICHT
im Chip angezeigt und stumme Module (w=0) ausgegraut -- so ist sichtbar, dass
Orderbuch/Liq-Trend schwaecher zaehlen als Welle (3,0) oder Squeeze (2,0).

Verifiziert live: alle 9 Stimmen korrekt, Bias-Rechnung geprueft (1.5/5.5=0.273),
Logging schreibt die neuen Spalten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-07-30 21:12:40 +02:00
co-authored by Claude Opus 4.8
parent 99bed4bdce
commit 7e0d2a4563
5 changed files with 105 additions and 15 deletions
+76 -5
View File
@@ -66,6 +66,7 @@ _MIN_CONF_DISPLAY = 70 # Konfidenz-Schwelle für die "Order ohne Sign
# bis 2026-07-23 auf dem entfernten Dry-Run-Auto-
# Trader beheimatet, hier als eigener Wert erhalten)
_BERLIN = ZoneInfo("Europe/Berlin")
_OB_IMB_MIN = 0.30 # Orderbuch-Imbalance darunter = zu schwach fuer eine Stimme
_SQUEEZE_NIGHT = (0, 1, 2, 3, 4, 5, 6, 7) # Auto-Squeeze-Nacht-Sperre (Kostenfalle)
_SQUEEZE_SR_COOLDOWN_S = 180.0 # nach S/R-Close kein Auto-Squeeze in GLEICHER Richtung
# (sonst Close-„Level hält" vs. Entry-„Level bricht"-
@@ -1305,7 +1306,8 @@ class TradingEngine:
# py, 2026-07-24): Copilot nicht robust prädiktiv, n aber klein → sammeln.
try:
vd = self._verdict(s, rec, self.wave.snapshot(), self.tu.snapshot(),
self.agent.snapshot(), self.elliott.snapshot())
self.agent.snapshot(), self.elliott.snapshot(),
self.patterns.snapshot())
vv = {v["name"]: v for v in vd.get("votes") or []}
def _v(name, key="vote"):
return (vv.get(name) or {}).get(key, 0)
@@ -1315,7 +1317,16 @@ class TradingEngine:
tf=vd.get("tf") or "", wave=_v("Welle"), m30=_v("M30"), h1=_v("H1"),
ki=_v("KI-Copilot"), ki_w=float(_v("KI-Copilot", "weight") or 0.0),
elliott=_v("Elliott"), elliott_w=float(_v("Elliott", "weight") or 0.0),
squeeze=_v("Squeeze"), news_score=_news)
squeeze=_v("Squeeze"), news_score=_news,
# Die drei ab 2026-07-30 ergänzten Stimmen MITLOGGEN — nur so ist in
# ein paar Wochen datenbasiert entscheidbar, ob sie bleiben (Muster:
# Kontrolltest zeigt kleinen Zusatz) bzw. ob die UNGEMESSENEN
# Orderbuch-/Liq-Trend-Stimmen überhaupt Prädiktivität haben.
pattern=_v("Muster"), pattern_w=float(_v("Muster", "weight") or 0.0),
orderbook=_v("Orderbuch"),
orderbook_w=float(_v("Orderbuch", "weight") or 0.0),
liqtrend=_v("Liq-Trend"),
liqtrend_w=float(_v("Liq-Trend", "weight") or 0.0))
except Exception as e:
log.debug(f"log_verdict_votes: {e}")
@@ -2094,7 +2105,8 @@ class TradingEngine:
except Exception as e:
log.debug(f"write_levels: {e}")
def _verdict(self, market, wave_signal, wave_snap, tu, agent, elliott) -> dict:
def _verdict(self, market, wave_signal, wave_snap, tu, agent, elliott,
snap_parts_patterns=None) -> dict:
"""Gesamtempfehlung aus den Modulen für die Widget-Grafik.
Headline = die **validierte Welle** (einziges Modul mit belegtem Edge);
@@ -2161,6 +2173,63 @@ class TradingEngine:
add("Squeeze", sqv, 2.0 if sq_active else 0.0,
f"{sq.get('state') or ''}" + (f" {sq.get('dir')}" if sq.get("dir") else ""))
# 8) Chartmuster — User-Vorgabe 2026-07-30 („berücksichtige die Chartmuster
# in der Gesamtempfehlung, sobald eins erkannt wird"). Gewicht bewusst KLEIN:
# der fällige KONTROLLTEST (2026-07-30) zeigt, dass die Muster einen zwar in
# BEIDEN Hälften positiven, aber kleinen Zusatz über einen GENERISCHEN
# Breakout hinaus liefern — Muster ØR +0,206/+0,328 vs. beliebiger Swing-Bruch
# +0,184/+0,220 (Delta nur +0,022 in H1, +0,108 in H2). Der Löwenanteil des
# Ertrags kommt also vom Breakout+Trailing, nicht vom Muster. Deshalb 1,0
# (wie Elliott/KI) statt 2,0 (Squeeze) — und `forming` zählt nur halb, weil
# das Muster dort noch nicht bestätigt ist. Ziel-Trefferquote bleibt niedrig
# (2326 %), die Stimme sagt also „Richtungstendenz", nicht „Kursziel".
pv, pdet, pw = 0, "", 0.0
try:
_pats = (snap_parts_patterns or {}).get("patterns") or []
_p0 = next((m for m in _pats if m.get("dir") in ("bull", "bear")), None)
if _p0:
pv = 1 if _p0["dir"] == "bull" else -1
_conf = _p0.get("status") == "confirmed"
pw = 1.0 if _conf else 0.5
pdet = f"{_p0.get('name', '')} · {'bestätigt' if _conf else 'bildet sich'}"
except Exception:
pass
add("Muster", pv, pw, pdet)
# 9/10) ORDERBUCH-Imbalance + LIQUIDITÄTS-TREND (User-Vorgabe 2026-07-30).
# ⚠ BEIDE sind UNGEMESSEN — anders als alle anderen Stimmen. Die Übertragbar-
# keit von Hyperliquid-Orderbuchdaten auf den Pepperstone-CFD wird gerade
# erst erhoben (`analyze_bookflow.py`, ab ~50k Snapshots ≈ 3 Tage erster Blick).
# ⚠ Der Lead-Lag-Test (2026-07-30, 72 h) zeigt sogar, dass **Pepperstone
# FÜHRT** (Peak bei k=1, r=0,525) und HL folgt — HL ist ein Oracle-basierter
# HIP-3-Perp, also strukturell Follower. Das dämpft die Erwartung erheblich.
# Deshalb bewusst das KLEINSTE Gewicht im ganzen Verdict (0,5 je Stimme;
# zum Vergleich: Welle 3,0 · Squeeze 2,0 · Muster/Elliott/KI 1,0).
# Beide werden in `verdict_votes` mitgeloggt → in ein paar Wochen ist
# datenbasiert entscheidbar, ob sie bleiben, mehr Gewicht bekommen oder
# rausfliegen (wie TU 2026-07-06).
ov, odet, ow = 0, "", 0.0
lv, ldet, lw = 0, "", 0.0
try:
w = self.hl_walls.walls()
if w:
imb = w.get("imb5")
if isinstance(imb, (int, float)) and abs(imb) >= _OB_IMB_MIN:
ov = 1 if imb > 0 else -1
ow = 0.5
odet = f"Imbalance {imb:+.2f}"
elif isinstance(imb, (int, float)):
odet = f"Imbalance {imb:+.2f} (zu schwach)"
tr = w.get("trend") or {}
if tr.get("dir") in ("up", "down"):
lv = 1 if tr["dir"] == "up" else -1
lw = 0.5
ldet = f"{tr['dir']} {tr.get('slope_per_h', 0):+.2f}$/h"
except Exception:
pass
add("Orderbuch", ov, ow, odet)
add("Liq-Trend", lv, lw, ldet)
# Bias nur über stimmberechtigte Module (weight>0); agree/total = wie viele
# der ANDEREN stimmberechtigten Module die Wellen-Richtung teilen (die Welle
# zählte sich vorher selbst mit — „3/6 für LONG" war geschönt).
@@ -2548,6 +2617,8 @@ class TradingEngine:
}
sr_close_hint = self._sr_close_hint(market, position, wave_snap)
stop_approach = self._stop_approach_hint(market, position, wave_snap)
# einmal holen — Verdict-Stimme UND Snapshot-Feld nutzen dasselbe Objekt
_pat_snap = self.patterns.snapshot()
snap = {
"ts": time.time(),
"connected": self._connected,
@@ -2580,11 +2651,11 @@ class TradingEngine:
"tu": tu,
"agent": agent,
"verdict": self._verdict(market, wave_signal, wave_snap,
tu, agent, elliott),
tu, agent, elliott, _pat_snap),
"daily_levels": self.daily.snapshot(),
"gaps": self.gaps.snapshot(),
"structure": self.structure.snapshot(),
"patterns": self.patterns.snapshot(),
"patterns": _pat_snap,
"stats_compact": {"week": self._compact_stats("week"),
"all": self._compact_stats("all")},
"close_alert_count": self._close_alert_count,
+16 -5
View File
@@ -154,7 +154,14 @@ SCHEMA = [
elliott INTEGER,
elliott_w REAL,
squeeze INTEGER,
news_score REAL -- News-Sentiment zum Zeitpunkt (Kontext)
news_score REAL, -- News-Sentiment zum Zeitpunkt (Kontext)
-- ab 2026-07-30 (User-Vorgabe, Stimmen mit KLEINEM Gewicht):
pattern INTEGER, -- Chartmuster-Vote (Kontrolltest: kleiner Zusatz)
pattern_w REAL, -- 1,0 bestaetigt / 0,5 bildet sich / 0 keins
orderbook INTEGER, -- HL-Orderbuch-Imbalance (UNGEMESSEN)
orderbook_w REAL,
liqtrend INTEGER, -- Liquiditaets-Trend (UNGEMESSEN)
liqtrend_w REAL
)
""",
]
@@ -417,7 +424,9 @@ class HistoryLogger:
def log_verdict_votes(self, *, headline: str, conf: int, bias: float, tf: str,
wave: int, m30: int, h1: int, ki: int, ki_w: float,
elliott: int, elliott_w: float, squeeze: int,
news_score=None) -> None:
news_score=None, pattern: int = 0, pattern_w: float = 0.0,
orderbook: int = 0, orderbook_w: float = 0.0,
liqtrend: int = 0, liqtrend_w: float = 0.0) -> None:
"""Verdict-Modul-Stimmen für die spätere Kalibrier-Auswertung loggen
(Copilot/Elliott sind unbelegte Stimmen — nach ein paar Wochen kann
`analyze_verdict_calibration.py`-artig entschieden werden, ob sie
@@ -430,10 +439,12 @@ class HistoryLogger:
conn.execute("""
INSERT INTO verdict_votes
(ts, headline, conf, bias, tf, wave, m30, h1, ki, ki_w,
elliott, elliott_w, squeeze, news_score)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
elliott, elliott_w, squeeze, news_score,
pattern, pattern_w, orderbook, orderbook_w, liqtrend, liqtrend_w)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""", (int(now), headline, conf, bias, tf, wave, m30, h1, ki, ki_w,
elliott, elliott_w, squeeze, news_score))
elliott, elliott_w, squeeze, news_score,
pattern, pattern_w, orderbook, orderbook_w, liqtrend, liqtrend_w))
conn.commit()
def alignment_stats(self, last_n: int = 40) -> dict:
+8 -2
View File
@@ -131,8 +131,14 @@ function renderVerdict(v, snap) {
const vote = (x) => x > 0 ? ["long", "▲"] : x < 0 ? ["short", "▼"] : ["flat", "—"];
$("vd-modules").innerHTML = (v.votes || []).map(mod => {
const [c, a] = vote(mod.vote);
return `<div class="vd-mod ${c}"><div class="m-top">` +
`<span class="m-name">${mod.name}</span>` +
// Gewicht sichtbar machen: Module OHNE Aussage (w=0) grau, sonst das Gewicht
// anzeigen — so ist erkennbar, dass z. B. Orderbuch/Liq-Trend (0,5, ungemessen)
// schwächer zählen als die Welle (3,0) oder der Squeeze (2,0).
const w = Number(mod.weight || 0);
const cls = w > 0 ? c : "flat mute";
const wtag = w > 0 ? `<span class="m-w">${w.toFixed(1).replace(".0", "")}</span>` : "";
return `<div class="vd-mod ${cls}"><div class="m-top">` +
`<span class="m-name">${mod.name}</span>${wtag}` +
`<span class="m-vote">${a}</span></div>` +
`<div class="m-det">${mod.detail || ""}</div></div>`;
}).join("");
+2 -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=120">
<link rel="stylesheet" href="style.css?v=121">
</head>
<body>
<!-- Modul-Menü (ganz oben) -->
@@ -229,6 +229,6 @@
<div id="toast" class="toast hidden"></div>
<script src="lightweight-charts.standalone.production.js?v=114"></script>
<script src="app.js?v=120"></script>
<script src="app.js?v=121"></script>
</body>
</html>
+3 -1
View File
@@ -186,7 +186,9 @@ main{display:grid;grid-template-columns:1fr 1fr;gap:11px;padding:11px}
.vd-mod{background:var(--bg);border:1px solid var(--border);border-radius:7px;
padding:6px 7px}
.vd-mod .m-top{display:flex;justify-content:space-between;align-items:center;gap:4px}
.vd-mod .m-name{font-size:14px;color:var(--dim);font-weight:600}
.vd-mod .vd-mod.mute{opacity:.45} /* Modul ohne Aussage (Gewicht 0) */
.m-w{font-size:11px;color:var(--dim);font-family:var(--mono);margin-left:4px}
.m-name{font-size:14px;color:var(--dim);font-weight:600}
.vd-mod .m-vote{font-size:17px;font-weight:800;color:var(--dim)}
.vd-mod .m-det{font-size:13px;color:var(--dim);margin-top:2px;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}