Chartmuster je Zeitebene als Bild + M1/M5 in der Meldungskarte (v=204)
User-Vorgabe: die hinterlegten Muster-Bilder sollen je Minuten-Chart das gerade entstehende Muster zeigen, M1 und M5 zusaetzlich in der Meldungskarte. BILDER: 15 Vorlagen aus /Chartmuster, 436-611 KB je Stueck (7,5 MB gesamt). Das Dashboard uebertraegt ~14 KB je Snapshot ueber WireGuard - ein Originalbild waere das 40-fache. Verkleinert auf 520 px / JPEG q80 nach web/muster/: 7,5 MB -> 379 KB (Faktor 20), im Frontend zusaetzlich loading="lazy". Die Originale sind gitignored, nur die Web-Fassungen kommen ins Repo. DETEKTOR: core/patterns.py bekommt die Zeitebene als PARAMETER. Der Default bleibt M30 und damit bitgleich - `PatternDetector()` speist das Verdict-Gewicht 0,25, eine stille Verschiebung dort waere eine Strategie-Aenderung durch die Hintertuer. Daneben laufen vier weitere Instanzen (M1/M5/M15/H1) mit gestaffelten Refresh-Zeiten (20/25/35/60 s), damit sie den mt5_lock nicht im Pulk belegen. Sie haben KEINE Stimme. Neu Snapshot `patterns_tf`: je Zeitebene das STAERKSTE Muster (bestaetigte vor sich bildenden, dann Symmetrie-Guete; ungueltige fallen raus) samt Bildnamen ueber die Karte `patterns.BILD`. Doppeltop/-boden, Dreifach-Top/-Boden und Flagge bull/baer teilen sich je ein Bild (die Vorlagen zeigen beide Richtungen); `inv_cup` bekommt KEINS - lieber kein Bild als ein falsches. ⚠⚠ REINE ANZEIGE, und das steht auch dran. Die Muster-Klasse ist als SIGNAL gemessen und verworfen (Ziel-Trefferquote nur 13-38 %, backtest_patterns.py), und auf M1 ist sie am schwaechsten: dort liegt der Spread bei 0,319xATR, ein Muster ueber wenige Bars ist weitgehend Rauschen. Kein Verdict-Gewicht, kein Trade-Trigger; die Tooltips nennen die Messung. ⚠ Beim Bau zwei eigene Fehler gefangen: (1) ich hatte die Feldnamen GERATEN - es heisst `type`, nicht `typ`, und der Status ist "confirmed"/"forming"/ "invalidated". Ein falscher Schluessel haette still None geliefert, die Karte waere leer geblieben ohne jede Fehlermeldung. (2) Der Emoji-Surrogat-Abbruch zum sechsten Mal - die .tmp-Regel hat app.js erneut gerettet (0 Bytes verhindert), danach mit literalen Zeichen statt \u-Escapes geschrieben. Live verifiziert: M1 Doppelboden, M5 Tasse+Henkel, M15 aufsteigendes Dreieck, M30 Doppeltop; alle Bilder HTTP 200 (22-28 KB). Pipeline A-G gruen, 97 Tests, 14 render*-Funktionen laufen durch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@@ -69,3 +69,8 @@ tools/autologon/
|
|||||||
|
|
||||||
# Zwischendateien des .tmp+os.replace-Schreibmusters (2026-08-19)
|
# Zwischendateien des .tmp+os.replace-Schreibmusters (2026-08-19)
|
||||||
*.tmp
|
*.tmp
|
||||||
|
|
||||||
|
# Original-Chartmuster-Vorlagen (7,5 MB JPEGs). Im Repo liegen nur die fuer
|
||||||
|
# das Web verkleinerten Fassungen unter `web/muster/` (379 KB gesamt).
|
||||||
|
/Chartmuster/
|
||||||
|
/chartmuster/
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ from core.market_hours import session_state
|
|||||||
from core.daily_levels import DailyLevels
|
from core.daily_levels import DailyLevels
|
||||||
from core.gaps import GapAnalyzer
|
from core.gaps import GapAnalyzer
|
||||||
from core.structure import MarketStructure
|
from core.structure import MarketStructure
|
||||||
from core.patterns import PatternDetector
|
from core.patterns import BILD as _PAT_BILD, PatternDetector
|
||||||
from core.candles import CandleAnatomy
|
from core.candles import CandleAnatomy
|
||||||
from core.cone import build as _cone_build
|
from core.cone import build as _cone_build
|
||||||
from core.candle_logger import CandleLogger
|
from core.candle_logger import CandleLogger
|
||||||
@@ -341,6 +341,24 @@ class TradingEngine:
|
|||||||
self.elliott = ElliottAnalyzer(mt5.TIMEFRAME_M5)
|
self.elliott = ElliottAnalyzer(mt5.TIMEFRAME_M5)
|
||||||
self.structure = MarketStructure() # Marktstruktur (M30) — reine Anzeige
|
self.structure = MarketStructure() # Marktstruktur (M30) — reine Anzeige
|
||||||
self.patterns = PatternDetector() # visuelle Chartmuster (M30) — reine Anzeige
|
self.patterns = PatternDetector() # visuelle Chartmuster (M30) — reine Anzeige
|
||||||
|
# ⚠⚠ ZUSAETZLICHE Detektoren je Zeitebene (User-Vorgabe 2026-08-20: das
|
||||||
|
# gerade entstehende Muster je Minuten-Chart als BILD zeigen).
|
||||||
|
# ⚠ `self.patterns` (M30) bleibt UNVERAENDERT — es speist das
|
||||||
|
# Verdict-Gewicht 0,25. Die neuen laufen daneben und haben KEINE
|
||||||
|
# Stimme; sonst waere es eine Strategie-Aenderung durch die Hintertuer.
|
||||||
|
# ⚠ Die Muster-Klasse ist als SIGNAL gemessen und verworfen
|
||||||
|
# (Ziel-Trefferquote nur 13-38 %, `backtest_patterns.py`) — und auf M1
|
||||||
|
# ist sie am schwaechsten: dort liegt der Spread bei 0,319×ATR, ein
|
||||||
|
# Muster ueber wenige Bars ist dort weitgehend Rauschen. Reine Anzeige.
|
||||||
|
# ⚠ Gestaffelte Refresh-Zeiten: fuenf Detektoren im selben Takt wuerden
|
||||||
|
# den `mt5_lock` im Pulk belegen.
|
||||||
|
self.patterns_tf = {
|
||||||
|
"M1": PatternDetector(mt5.TIMEFRAME_M1, "M1", 20.0, 240),
|
||||||
|
"M5": PatternDetector(mt5.TIMEFRAME_M5, "M5", 25.0, 240),
|
||||||
|
"M15": PatternDetector(mt5.TIMEFRAME_M15, "M15", 35.0, 240),
|
||||||
|
"M30": self.patterns,
|
||||||
|
"H1": PatternDetector(mt5.TIMEFRAME_H1, "H1", 60.0, 240),
|
||||||
|
}
|
||||||
# Kerzen-Anatomie (M5) — REINE ANZEIGE. Die Lehrbuch-Deutung ist gemessen
|
# Kerzen-Anatomie (M5) — REINE ANZEIGE. Die Lehrbuch-Deutung ist gemessen
|
||||||
# invertiert (`backtest_candles.py`), das Handelssignal fiel durch
|
# invertiert (`backtest_candles.py`), das Handelssignal fiel durch
|
||||||
# (`backtest_candle_fade.py`) → kein Verdict-Gewicht, kein Trigger.
|
# (`backtest_candle_fade.py`) → kein Verdict-Gewicht, kein Trigger.
|
||||||
@@ -845,7 +863,11 @@ class TradingEngine:
|
|||||||
except Exception as ee: log.warning(f"elliott.refresh_market: {ee}")
|
except Exception as ee: log.warning(f"elliott.refresh_market: {ee}")
|
||||||
try: self.structure.refresh_market(self.data.symbol)
|
try: self.structure.refresh_market(self.data.symbol)
|
||||||
except Exception as se2: log.warning(f"structure.refresh_market: {se2}")
|
except Exception as se2: log.warning(f"structure.refresh_market: {se2}")
|
||||||
try: self.patterns.refresh_market(self.data.symbol)
|
try:
|
||||||
|
# ⚠ `self.patterns` steckt als "M30" mit in der Karte —
|
||||||
|
# NICHT doppelt aufrufen (eigene Drossel je Instanz).
|
||||||
|
for _pd in self.patterns_tf.values():
|
||||||
|
_pd.refresh_market(self.data.symbol)
|
||||||
except Exception as pe2: log.warning(f"patterns.refresh_market: {pe2}")
|
except Exception as pe2: log.warning(f"patterns.refresh_market: {pe2}")
|
||||||
try: self.candle_anat.refresh_market(self.data.symbol)
|
try: self.candle_anat.refresh_market(self.data.symbol)
|
||||||
except Exception as ka: log.warning(f"candles.refresh_market: {ka}")
|
except Exception as ka: log.warning(f"candles.refresh_market: {ka}")
|
||||||
@@ -3017,6 +3039,47 @@ class TradingEngine:
|
|||||||
# ══════════════════════════════════════════════════════════════════════
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
# TRADE-AKTIONEN (für die Trade-Endpoints in server.py — Schritt 3)
|
# TRADE-AKTIONEN (für die Trade-Endpoints in server.py — Schritt 3)
|
||||||
# ══════════════════════════════════════════════════════════════════════
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
def _patterns_tf(self) -> dict:
|
||||||
|
"""Je Zeitebene das STAERKSTE gerade entstehende Muster (+ Bildname).
|
||||||
|
|
||||||
|
⚠ "Staerkste" = zuerst BESTAETIGT vor "bildet sich", dann hoehere Guete.
|
||||||
|
Ein bestaetigtes Muster ist die konkretere Aussage; bei gleichem Status
|
||||||
|
entscheidet die Symmetrie-Guete, die `_detect` ohnehin vergibt.
|
||||||
|
⚠ REINE ANZEIGE. Die Muster-Klasse ist als Signal gemessen und
|
||||||
|
verworfen (Ziel-Trefferquote 13-38 %) — hier wird nur sichtbar gemacht,
|
||||||
|
was ein Chartleser saehe, nichts gewichtet.
|
||||||
|
"""
|
||||||
|
out = {}
|
||||||
|
for lbl, det in (getattr(self, "patterns_tf", None) or {}).items():
|
||||||
|
try:
|
||||||
|
ps = (det.snapshot() or {}).get("patterns") or []
|
||||||
|
if not ps:
|
||||||
|
out[lbl] = None
|
||||||
|
continue
|
||||||
|
# ⚠ Das Feld heisst `type`, nicht `typ`, und der Status ist
|
||||||
|
# "confirmed"/"forming"/"invalidated" — beim ersten Entwurf
|
||||||
|
# hatte ich beides geraten statt nachgelesen. Ein falscher
|
||||||
|
# Schluessel haette hier still `None` geliefert: die Karte
|
||||||
|
# waere leer geblieben, ohne dass irgendwo ein Fehler steht.
|
||||||
|
ps = [m for m in ps if m.get("status") != "invalidated"]
|
||||||
|
if not ps:
|
||||||
|
out[lbl] = None
|
||||||
|
continue
|
||||||
|
best = sorted(ps, key=lambda m: (
|
||||||
|
0 if m.get("status") == "confirmed" else 1,
|
||||||
|
-float(m.get("quality") or 0)))[0]
|
||||||
|
out[lbl] = {
|
||||||
|
"type": best.get("type"), "name": best.get("name"),
|
||||||
|
"dir": best.get("dir"), "status": best.get("status"),
|
||||||
|
"quality": best.get("quality"), "trigger": best.get("trigger"),
|
||||||
|
"target": best.get("target"),
|
||||||
|
"height_atr": best.get("height_atr"),
|
||||||
|
"bild": _PAT_BILD.get(best.get("type")),
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
out[lbl] = None
|
||||||
|
return out
|
||||||
|
|
||||||
def _slot(self, source: str):
|
def _slot(self, source: str):
|
||||||
"""Welcher Trader fuer welche Quelle? (Zwei-Slot-Umbau 2026-08-19)
|
"""Welcher Trader fuer welche Quelle? (Zwei-Slot-Umbau 2026-08-19)
|
||||||
|
|
||||||
@@ -5098,6 +5161,9 @@ class TradingEngine:
|
|||||||
"gaps": self.gaps.snapshot(),
|
"gaps": self.gaps.snapshot(),
|
||||||
"structure": self.structure.snapshot(),
|
"structure": self.structure.snapshot(),
|
||||||
"patterns": _pat_snap,
|
"patterns": _pat_snap,
|
||||||
|
# ⚠ Je Zeitebene das STAERKSTE Muster + sein Bild. Reine Anzeige,
|
||||||
|
# kein Verdict-Gewicht (die Klasse ist als Signal verworfen).
|
||||||
|
"patterns_tf": self._patterns_tf(),
|
||||||
# Kerzen-Anatomie (M5) — reine Anzeige, KEIN Verdict-Gewicht (gemessen:
|
# Kerzen-Anatomie (M5) — reine Anzeige, KEIN Verdict-Gewicht (gemessen:
|
||||||
# Lehrbuch-Deutung invertiert, Handelssignal durchgefallen).
|
# Lehrbuch-Deutung invertiert, Handelssignal durchgefallen).
|
||||||
"candle_anat": self.candle_anat.snapshot(),
|
"candle_anat": self.candle_anat.snapshot(),
|
||||||
|
|||||||
@@ -314,22 +314,56 @@ def _detect(piv, cur, atr, last_idx):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
# ⚠⚠ BILD je Mustertyp (User-Vorgabe 2026-08-20: das gerade entstehende Muster
|
||||||
|
# als Bild zeigen). Die Dateien liegen verkleinert in `web/muster/` — die
|
||||||
|
# Originale in `Chartmuster/` sind 436-611 KB je Stueck; das Dashboard
|
||||||
|
# uebertraegt sonst ~14 KB je Snapshot, ein Originalbild waere das 40-fache.
|
||||||
|
# Verkleinert: 7,5 MB -> 379 KB fuer alle 15.
|
||||||
|
# ⚠ Doppeltop und Doppelboden teilen sich ein Bild (die Vorlage zeigt beide),
|
||||||
|
# ebenso Dreifach-Top/-Boden und Flagge bullisch/baerisch.
|
||||||
|
# ⚠ `inv_cup` hat KEIN eigenes Bild — lieber keins als ein falsches.
|
||||||
|
BILD = {
|
||||||
|
"double_top": "double", "double_bottom": "double",
|
||||||
|
"triple_top": "triple", "triple_bottom": "triple",
|
||||||
|
"hs": "hs", "inv_hs": "inv_hs",
|
||||||
|
"cup": "cup",
|
||||||
|
"flag": "flag", "pennant": "pennant",
|
||||||
|
"wedge_rise": "wedge_rise", "wedge_fall": "wedge_fall",
|
||||||
|
"tri_asc": "tri_asc", "tri_desc": "tri_desc", "tri_sym": "tri_sym",
|
||||||
|
"rect": "rect", "rounding_bottom": "rounding_bottom", "diamond": "diamond",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class PatternDetector:
|
class PatternDetector:
|
||||||
def __init__(self):
|
def __init__(self, tf=None, label: str = "M30", refresh_s: float = _REFRESH_S,
|
||||||
self._snap = {"patterns": [], "atr": None, "ts": 0.0}
|
bars: int = _N_BARS):
|
||||||
|
"""⚠ `tf=None` = M30, der bisherige Stand — der Default bleibt bitgleich.
|
||||||
|
|
||||||
|
Die Zeitebene ist seit dem 2026-08-20 ein PARAMETER (User-Wunsch: das
|
||||||
|
gerade entstehende Muster je Minuten-Chart anzeigen). Vorher war sie die
|
||||||
|
Modul-Konstante `_TF`; die bleibt als Default stehen, damit der
|
||||||
|
bestehende Aufruf `PatternDetector()` sich NICHT aendert — er speist
|
||||||
|
das Verdict-Gewicht 0,25, und eine stille Verschiebung dort waere eine
|
||||||
|
Strategie-Aenderung durch die Hintertuer.
|
||||||
|
"""
|
||||||
|
self._tf = _TF if tf is None else tf
|
||||||
|
self._label = label
|
||||||
|
self._refresh_s = float(refresh_s)
|
||||||
|
self._bars = int(bars)
|
||||||
|
self._snap = {"patterns": [], "atr": None, "ts": 0.0, "tf": label}
|
||||||
self._last_refresh = 0.0
|
self._last_refresh = 0.0
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
|
|
||||||
def refresh_market(self, sym: str):
|
def refresh_market(self, sym: str):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now - self._last_refresh < _REFRESH_S:
|
if now - self._last_refresh < self._refresh_s:
|
||||||
return
|
return
|
||||||
self._last_refresh = now
|
self._last_refresh = now
|
||||||
try:
|
try:
|
||||||
with mt5_lock(timeout=2) as got:
|
with mt5_lock(timeout=2) as got:
|
||||||
if not got:
|
if not got:
|
||||||
return
|
return
|
||||||
bars = mt5.copy_rates_from_pos(sym, _TF, 0, _N_BARS)
|
bars = mt5.copy_rates_from_pos(sym, self._tf, 0, self._bars)
|
||||||
if bars is None or len(bars) < 60:
|
if bars is None or len(bars) < 60:
|
||||||
return
|
return
|
||||||
H = [float(b["high"]) for b in bars]
|
H = [float(b["high"]) for b in bars]
|
||||||
@@ -342,7 +376,8 @@ class PatternDetector:
|
|||||||
patterns = _detect(piv, C[-1], atr, len(H) - 1)
|
patterns = _detect(piv, C[-1], atr, len(H) - 1)
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._snap = {"patterns": patterns, "atr": round(atr, 4),
|
self._snap = {"patterns": patterns, "atr": round(atr, 4),
|
||||||
"cur": round(C[-1], 3), "ts": now}
|
"cur": round(C[-1], 3), "ts": now,
|
||||||
|
"tf": self._label}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"patterns.refresh_market: {e}")
|
log.warning(f"patterns.refresh_market: {e}")
|
||||||
|
|
||||||
|
|||||||
@@ -715,6 +715,21 @@ function renderKontext(snap) {
|
|||||||
// dann wieder einschaltet"). Der Text kommt FERTIG aus dem Backend
|
// dann wieder einschaltet"). Der Text kommt FERTIG aus dem Backend
|
||||||
// (`trail_info`) — ihn hier nachzubauen waere die Divergenz-Falle, die im
|
// (`trail_info`) — ihn hier nachzubauen waere die Divergenz-Falle, die im
|
||||||
// Projekt dreimal zugeschlagen hat.
|
// Projekt dreimal zugeschlagen hat.
|
||||||
|
// M1/M5-Muster kompakt in der Meldungen-Karte (User 2026-08-20) — bewusst
|
||||||
|
// OHNE Bild: das hier ist der Live-Textstrom zum laufenden Trade.
|
||||||
|
const patN = $streng("pat-note");
|
||||||
|
if (patN) {
|
||||||
|
const ptf = (snap && snap.patterns_tf) || {};
|
||||||
|
const teile = ["M1", "M5"].map(tf => {
|
||||||
|
const m = ptf[tf];
|
||||||
|
if (!m) return `${tf} kein Muster`;
|
||||||
|
const p = m.dir === "bull" ? "▲" : m.dir === "bear" ? "▼" : "◆";
|
||||||
|
return `${tf} ${p} ${m.name}` + (m.status === "confirmed" ? " (bestätigt)" : "");
|
||||||
|
});
|
||||||
|
patN.textContent = "Muster: " + teile.join(" · ");
|
||||||
|
patN.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
const trN = $streng("trail-note");
|
const trN = $streng("trail-note");
|
||||||
if (trN) {
|
if (trN) {
|
||||||
const trTxt = snap && snap.trail_info;
|
const trTxt = snap && snap.trail_info;
|
||||||
@@ -978,6 +993,40 @@ function renderCone(cn) {
|
|||||||
+ "leicht zu eng. Kein Signal, kein Einfluss auf die Empfehlung.";
|
+ "leicht zu eng. Kein Signal, kein Einfluss auf die Empfehlung.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Je Zeitebene das gerade entstehende Muster als BILD (User-Wunsch 2026-08-20).
|
||||||
|
// ⚠ REINE ANZEIGE. Die Muster-Klasse ist als Signal gemessen und verworfen
|
||||||
|
// (Ziel-Trefferquote 13–38 %, `backtest_patterns.py`); auf M1 ist sie am
|
||||||
|
// schwächsten — dort liegt der Spread bei 0,319×ATR, ein Muster über wenige
|
||||||
|
// Bars ist dort weitgehend Rauschen. Kein Verdict-Gewicht, kein Trade-Trigger.
|
||||||
|
// ⚠ Bilder mit `loading="lazy"`: die 15 Vorlagen liegen verkleinert bei ~25 KB
|
||||||
|
// (Originale 436–611 KB), aber über WireGuard soll nur geladen werden, was
|
||||||
|
// wirklich sichtbar ist.
|
||||||
|
function renderPatternsTF(d) {
|
||||||
|
const el = $streng("pt-tfgrid");
|
||||||
|
if (!el) return;
|
||||||
|
const pt = (d && d.patterns_tf) || null;
|
||||||
|
if (!pt) { el.innerHTML = ""; return; }
|
||||||
|
el.innerHTML = ["M1", "M5", "M15", "M30", "H1"].map(tf => {
|
||||||
|
const m = pt[tf];
|
||||||
|
if (!m) {
|
||||||
|
return `<div class="pt-tfcell leer"><span class="pt-tflbl">${tf}</span>`
|
||||||
|
+ `<span class="dim">kein Muster</span></div>`;
|
||||||
|
}
|
||||||
|
const cls = m.dir === "bull" ? "up" : m.dir === "bear" ? "down" : "dim";
|
||||||
|
const arr = m.dir === "bull" ? "▲" : m.dir === "bear" ? "▼" : "◆";
|
||||||
|
const best = m.status === "confirmed";
|
||||||
|
const bild = m.bild
|
||||||
|
? `<img class="pt-img" src="muster/${m.bild}.jpg" alt="${m.name}" loading="lazy">`
|
||||||
|
: `<div class="pt-img leer">kein Bild</div>`;
|
||||||
|
const q = (m.quality != null ? m.quality : 0).toFixed(2);
|
||||||
|
return `<div class="pt-tfcell${best ? " best" : ""}">`
|
||||||
|
+ `<span class="pt-tflbl">${tf}</span>${bild}`
|
||||||
|
+ `<span class="pt-tfname ${cls}">${arr} ${m.name}</span>`
|
||||||
|
+ `<span class="pt-tfst">${best ? "bestätigt" : "bildet sich"} · Güte ${q}</span>`
|
||||||
|
+ `</div>`;
|
||||||
|
}).join("");
|
||||||
|
}
|
||||||
|
|
||||||
function renderPatterns(pt) {
|
function renderPatterns(pt) {
|
||||||
const el = $("pt-list");
|
const el = $("pt-list");
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
@@ -1198,6 +1247,7 @@ function render(d) {
|
|||||||
renderBrk(d);
|
renderBrk(d);
|
||||||
renderKontext(d);
|
renderKontext(d);
|
||||||
renderCircuitBreaker(d.circuit_breaker);
|
renderCircuitBreaker(d.circuit_breaker);
|
||||||
|
renderPatternsTF(d);
|
||||||
renderPatterns(d.patterns);
|
renderPatterns(d.patterns);
|
||||||
renderCandles(d.candle_anat);
|
renderCandles(d.candle_anat);
|
||||||
renderCone(d.cone);
|
renderCone(d.cone);
|
||||||
@@ -2397,7 +2447,7 @@ async function closeSlot(slot, label, pnl) {
|
|||||||
// und der Browser zeigte Altes. Statt das jedes Mal von Hand zu diagnostizieren,
|
// und der Browser zeigte Altes. Statt das jedes Mal von Hand zu diagnostizieren,
|
||||||
// sagt die Seite es jetzt selbst.
|
// sagt die Seite es jetzt selbst.
|
||||||
// ⚠ Bewusst SICHTBAR statt nur Konsole: die Konsole sieht am Handy niemand.
|
// ⚠ Bewusst SICHTBAR statt nur Konsole: die Konsole sieht am Handy niemand.
|
||||||
const _JS_V = "203";
|
const _JS_V = "204";
|
||||||
{
|
{
|
||||||
const htmlV = document.body && document.body.dataset
|
const htmlV = document.body && document.body.dataset
|
||||||
? document.body.dataset.htmlV : null;
|
? document.body.dataset.htmlV : null;
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard
|
⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard
|
||||||
und muss offline funktionieren. -->
|
und muss offline funktionieren. -->
|
||||||
<link rel="stylesheet" href="pico.min.css?v=1">
|
<link rel="stylesheet" href="pico.min.css?v=1">
|
||||||
<link rel="stylesheet" href="style.css?v=203">
|
<link rel="stylesheet" href="style.css?v=204">
|
||||||
</head>
|
</head>
|
||||||
<body data-html-v="203">
|
<body data-html-v="204">
|
||||||
<!-- Modul-Menü (ganz oben) -->
|
<!-- Modul-Menü (ganz oben) -->
|
||||||
<nav id="tabs">
|
<nav id="tabs">
|
||||||
<button class="tab active" data-view="dash">Dashboard</button>
|
<button class="tab active" data-view="dash">Dashboard</button>
|
||||||
@@ -178,6 +178,9 @@
|
|||||||
<!-- Trailing-Zustand im Klartext. Die Trailing-KARTE ist seit dem
|
<!-- Trailing-Zustand im Klartext. Die Trailing-KARTE ist seit dem
|
||||||
22.07. ausgeblendet; damit gab es keine Stelle mehr, an der man
|
22.07. ausgeblendet; damit gab es keine Stelle mehr, an der man
|
||||||
sah, WARUM der Stop nicht nachgezogen wird (User-Frage 20.08.). -->
|
sah, WARUM der Stop nicht nachgezogen wird (User-Frage 20.08.). -->
|
||||||
|
<!-- M1/M5-Muster kompakt (User 2026-08-20). Bewusst OHNE Bild:
|
||||||
|
die Meldungen-Karte ist der Live-Textstrom zum laufenden Trade. -->
|
||||||
|
<div class="hl-note hidden" id="pat-note" title="Das gerade entstehende Muster auf M1 und M5. Reine Anzeige — die Muster-Klasse ist als Signal gemessen und verworfen (Ziel-Trefferquote 13–38 %), auf M1 am schwaechsten."></div>
|
||||||
<div class="hl-note hidden" id="trail-note" title="Was das Trailing gerade tut. Zwei Gruende, warum es scheinbar nichts tut und beide richtig sind: es startet erst ab 1,3×ATR Gewinn (seit 20.08., vorher 0,3×), und es lockert einen bereits engeren Stop niemals."></div>
|
<div class="hl-note hidden" id="trail-note" title="Was das Trailing gerade tut. Zwei Gruende, warum es scheinbar nichts tut und beide richtig sind: es startet erst ab 1,3×ATR Gewinn (seit 20.08., vorher 0,3×), und es lockert einen bereits engeren Stop niemals."></div>
|
||||||
<!-- Auto-Flip-Close: schließt eine Position, wenn die Empfehlung dreht.
|
<!-- Auto-Flip-Close: schließt eine Position, wenn die Empfehlung dreht.
|
||||||
⚠ GEMESSEN in BEIDEN Halbjahren negativ (−92/−160 R,
|
⚠ GEMESSEN in BEIDEN Halbjahren negativ (−92/−160 R,
|
||||||
@@ -277,6 +280,11 @@
|
|||||||
`forming` zählt halb. Deshalb LIVE, nicht Anzeige. -->
|
`forming` zählt halb. Deshalb LIVE, nicht Anzeige. -->
|
||||||
<section class="card wide" id="card-patterns">
|
<section class="card wide" id="card-patterns">
|
||||||
<h2 title="Mechanisch erkannte Chartmuster auf M30 mit Trigger und Kursziel. Gewicht 0,25. Die Ziel-Trefferquote liegt gemessen nur bei 13–38 % — Kontext, keine Vorhersage.">Chartmuster <span class="pt-tf">M30 · Gewicht 0,25</span></h2>
|
<h2 title="Mechanisch erkannte Chartmuster auf M30 mit Trigger und Kursziel. Gewicht 0,25. Die Ziel-Trefferquote liegt gemessen nur bei 13–38 % — Kontext, keine Vorhersage.">Chartmuster <span class="pt-tf">M30 · Gewicht 0,25</span></h2>
|
||||||
|
<!-- Je Zeitebene das gerade entstehende Muster als BILD (User 2026-08-20).
|
||||||
|
⚠ REINE ANZEIGE: die Muster-Klasse ist als Signal gemessen und
|
||||||
|
verworfen (Ziel-Trefferquote 13–38 %); auf M1 ist sie am schwaechsten,
|
||||||
|
dort liegt der Spread bei 0,319×ATR. -->
|
||||||
|
<div class="pt-tfgrid" id="pt-tfgrid" title="Das jeweils staerkste Muster je Zeitebene, mit Bild. Bestaetigte gehen vor sich bildenden; bei gleichem Status entscheidet die Symmetrie-Guete."></div>
|
||||||
<div class="pt-list" id="pt-list" title="Erkannte Muster mit Skizze, Trigger und Kursziel."><span class="dim">—</span></div>
|
<div class="pt-list" id="pt-list" title="Erkannte Muster mit Skizze, Trigger und Kursziel."><span class="dim">—</span></div>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
@@ -425,6 +433,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="toast" class="toast hidden"></div>
|
<div id="toast" class="toast hidden"></div>
|
||||||
|
|
||||||
<script src="app.js?v=203"></script>
|
<script src="app.js?v=204"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 24 KiB |
@@ -710,3 +710,27 @@ main{padding-bottom:4.1111rem} /* Platz für die fixe Leiste */
|
|||||||
/* ⚠ Blinken uebernommen vom entfernten CLOSE-Knopf — sonst haette der
|
/* ⚠ Blinken uebernommen vom entfernten CLOSE-Knopf — sonst haette der
|
||||||
Close-Alarm seine Sichtbarkeit verloren und nur noch gepiept. */
|
Close-Alarm seine Sichtbarkeit verloren und nur noch gepiept. */
|
||||||
.px-close.blink{animation:blkClose .6s ease-in-out infinite}
|
.px-close.blink{animation:blkClose .6s ease-in-out infinite}
|
||||||
|
|
||||||
|
/* ── Chartmuster je Zeitebene (User 2026-08-20) ────────────────────────────
|
||||||
|
⚠ Bilder sind 520×284 und werden auf die Zellbreite skaliert. Das Raster
|
||||||
|
passt sich selbst an (auto-fit), damit es am Handy einspaltig und am
|
||||||
|
Desktop mehrspaltig laeuft — ohne zweite Media-Query. */
|
||||||
|
.pt-tfgrid{
|
||||||
|
display:grid; gap:0.5556rem; margin:0 0 0.6667rem;
|
||||||
|
grid-template-columns:repeat(auto-fit, minmax(9.4444rem, 1fr));
|
||||||
|
}
|
||||||
|
.pt-tfcell{
|
||||||
|
display:flex; flex-direction:column; gap:0.1667rem;
|
||||||
|
background:var(--surface-2); border:1px solid var(--border);
|
||||||
|
border-radius:var(--r); padding:0.3889rem;
|
||||||
|
}
|
||||||
|
.pt-tfcell.best{border-color:#4a3600}
|
||||||
|
.pt-tfcell.leer{opacity:.55; justify-content:center; min-height:4.4444rem;
|
||||||
|
align-items:center; text-align:center}
|
||||||
|
.pt-tflbl{font:600 0.7222rem/1 var(--mono); color:var(--dim); letter-spacing:.04em}
|
||||||
|
.pt-img{width:100%; height:auto; border-radius:var(--r-sm); display:block;
|
||||||
|
background:#0d1117}
|
||||||
|
.pt-img.leer{height:3.3333rem; display:flex; align-items:center;
|
||||||
|
justify-content:center; font-size:0.6667rem; color:var(--dim)}
|
||||||
|
.pt-tfname{font-size:0.7778rem; font-weight:600; line-height:1.2}
|
||||||
|
.pt-tfst{font-size:0.6667rem; color:var(--dim); font-family:var(--mono)}
|
||||||
|
|||||||