Meldungen entschlackt, Bild-Modalbox, neues Muster, kleinere Trade-Schrift, M15 eingeklappt (v=207)
Fuenf User-Vorgaben in einem Durchgang.
(1) HYPERLIQUID + MUSTER + TRAILING AUS DER MELDUNGEN-KARTE. Die drei Divs sind
ENTFERNT, nicht nur versteckt; die Render-Bloecke bleiben stehen (null-sicher),
damit ein Zurueckholen eine reine HTML-Aenderung ist. Backend rechnet
unveraendert weiter (hl_ctx, patterns_tf, trail_info stehen im Snapshot).
Stufe F bekam eine dokumentierte Ausnahmeliste - sonst haette sie drei
Dauer-Treffer gemeldet, und eine Pruefung mit Dauer-Treffern wird ignoriert.
⚠⚠ DABEI ZWEI ECHTE ABSTUERZE GEFUNDEN - und der Weg dahin ist der Punkt.
`hl-note` wurde ueber das Auffang-`$` geholt; nach dem Entfernen des Elements
griffen ZWEI Zweige ungeschuetzt auf `hlEl.classList` zu. Im Browser ein
TypeError mitten im Render = das GESAMTE Dashboard friert auf Altwerten ein.
Die Render-Probe blieb zunaechst GRUEN - ihr eigener blinder Fleck: die
DOM-Attrappe lieferte fuer JEDE ID ein Element, `$streng` konnte darin also NIE
`null` zurueckgeben. Genau der Fall, fuer den es `$streng` gibt, war ungeprueft.
Behoben: die Attrappe kennt jetzt die echten IDs aus index.html
(`getElementById: id => HTML_IDS.has(id) ? el() : null`) - und meldete die
beiden Zweige im selben Lauf. Danach beide Waechter ergaenzt.
(2) BILD-MODALBOX. Klick auf ein Chartmuster oeffnet es gross.
⚠⚠ BEWUSST OHNE BIBLIOTHEK: ein Lightbox-Paket waere 15-40 KB plus ein zweites
Bedienkonzept fuer ~30 Zeilen Code. Das Dashboard laeuft nur ueber WireGuard,
ein CDN ist gar nicht erreichbar; das Projekt hat aus demselben Grund Tailwind
abgelehnt und am 05.08. die vendorte lightweight-charts (164 KB) entfernt.
Delegierter Klick auf `document`, weil die Kacheln bei JEDEM Render neu erzeugt
werden - ein Handler am <img> waere nach dem naechsten Snapshot weg.
Grosse Fassung (1408 px, ~133 KB) liegt getrennt unter muster/gross/ und wird
ERST beim Klick geladen; die Kachel bleibt bei 25 KB. Esc und Klick auf den
Hintergrund schliessen.
(3) NEUES BILD gescannt: "Inverse Tasse+Henkel.jpg". `inv_cup` hatte bis dahin
ausdruecklich KEIN Bild ("lieber keins als ein falsches") - jetzt zugeordnet.
Damit 16 Muster, keins ohne Datei, keine Datei ohne Zuordnung.
(4) SCHRIFT IN DER TRADE-LEISTE um 2 px (bezogen auf die 18-px-Referenz)
verkleinert - 4 Regeln: Ueberschrift, Feldbeschriftungen, Werte/Eingaben,
Knoepfe.
⚠ Erster Versuch nahm alles zwischen `.tb-title{` und `.mg-row` und traf 80
Regeln statt 4 - die halbe Oberflaeche. Zurueckgesetzt und namentlich
aufgezaehlt. Ein Zeilenbereich ist kein Selektor.
(5) M15-KARTE PER DEFAULT EINGEKLAPPT. Das `open` am ersten <details> ist weg.
⚠ Zusaetzlich der localStorage-Schluessel gebumpt (oil_m15_akk -> oil_m15_akk2):
bei jedem, der die Karte je aufgeklappt hatte, stand dort `true`, und das
gewinnt gegen den neuen Default. Ein neuer Schluessel setzt genau EINMAL
zurueck; danach merkt sich die Karte die eigene Wahl wie bisher.
Pipeline A-G gruen, 97 Tests, 14 render*-Funktionen (Fixture UND leerer
Snapshot), deployt und am ausgelieferten HTML/JS verifiziert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@@ -321,12 +321,16 @@ def _detect(piv, cur, atr, last_idx):
|
|||||||
# Verkleinert: 7,5 MB -> 379 KB fuer alle 15.
|
# Verkleinert: 7,5 MB -> 379 KB fuer alle 15.
|
||||||
# ⚠ Doppeltop und Doppelboden teilen sich ein Bild (die Vorlage zeigt beide),
|
# ⚠ Doppeltop und Doppelboden teilen sich ein Bild (die Vorlage zeigt beide),
|
||||||
# ebenso Dreifach-Top/-Boden und Flagge bullisch/baerisch.
|
# ebenso Dreifach-Top/-Boden und Flagge bullisch/baerisch.
|
||||||
# ⚠ `inv_cup` hat KEIN eigenes Bild — lieber keins als ein falsches.
|
# ⚠ `inv_cup` hat seit dem 20.08. ein eigenes Bild (vorher bewusst keins).
|
||||||
BILD = {
|
BILD = {
|
||||||
"double_top": "double", "double_bottom": "double",
|
"double_top": "double", "double_bottom": "double",
|
||||||
"triple_top": "triple", "triple_bottom": "triple",
|
"triple_top": "triple", "triple_bottom": "triple",
|
||||||
"hs": "hs", "inv_hs": "inv_hs",
|
"hs": "hs", "inv_hs": "inv_hs",
|
||||||
"cup": "cup",
|
"cup": "cup",
|
||||||
|
# ⚠ Am 2026-08-20 nachgereicht (User: "scanne neue Bilder"). Bis dahin
|
||||||
|
# stand hier ausdruecklich KEIN Eintrag — lieber kein Bild als ein
|
||||||
|
# falsches; die normale Tasse zu zeigen waere die falsche Richtung gewesen.
|
||||||
|
"inv_cup": "inv_cup",
|
||||||
"flag": "flag", "pennant": "pennant",
|
"flag": "flag", "pennant": "pennant",
|
||||||
"wedge_rise": "wedge_rise", "wedge_fall": "wedge_fall",
|
"wedge_rise": "wedge_rise", "wedge_fall": "wedge_fall",
|
||||||
"tri_asc": "tri_asc", "tri_desc": "tri_desc", "tri_sym": "tri_sym",
|
"tri_asc": "tri_asc", "tri_desc": "tri_desc", "tri_sym": "tri_sym",
|
||||||
|
|||||||
@@ -478,7 +478,15 @@ def _stufe_ids():
|
|||||||
js = re.sub(r"(?m)^\s*//.*$", "", js)
|
js = re.sub(r"(?m)^\s*//.*$", "", js)
|
||||||
html_ids = re.findall(r'id="([^"]+)"', html)
|
html_ids = re.findall(r'id="([^"]+)"', html)
|
||||||
doppelt = sorted({i for i in html_ids if html_ids.count(i) > 1})
|
doppelt = sorted({i for i in html_ids if html_ids.count(i) > 1})
|
||||||
fehlt = sorted(set(re.findall(r'\$(?:streng)?\("([^"]+)"\)', js)) - set(html_ids))
|
# ⚠⚠ ABSICHTLICH ausgeblendete Elemente. Der JS-Block bleibt stehen —
|
||||||
|
# er ist null-sicher (`$streng` + Waechter) — damit ein Zurueckholen eine
|
||||||
|
# reine HTML-Aenderung ist. Ohne diese Liste meldete die Stufe drei
|
||||||
|
# Dauer-Treffer, und eine Pruefung mit Dauer-Treffern wird ignoriert (die
|
||||||
|
# Lehre vom Linter). ⚠ Wer hier etwas eintraegt, MUSS den Zugriff im JS
|
||||||
|
# auf `$streng` + Waechter umstellen — sonst bricht der GESAMTE Render.
|
||||||
|
AUSGEBLENDET = {"hl-note", "pat-note", "trail-note"} # User-Wunsch 2026-08-20
|
||||||
|
fehlt = sorted(set(re.findall(r'\$(?:streng)?\("([^"]+)"\)', js))
|
||||||
|
- set(html_ids) - AUSGEBLENDET)
|
||||||
for i in fehlt:
|
for i in fehlt:
|
||||||
befunde.append(f"ID verwaist: $(\"{i}\") ohne Element in index.html")
|
befunde.append(f"ID verwaist: $(\"{i}\") ohne Element in index.html")
|
||||||
print(f" FEHLER verwaiste ID: {i}")
|
print(f" FEHLER verwaiste ID: {i}")
|
||||||
|
|||||||
@@ -26,6 +26,14 @@ import vm from "node:vm";
|
|||||||
const pfad = process.argv[2] || "tests/fixtures/snapshot.json";
|
const pfad = process.argv[2] || "tests/fixtures/snapshot.json";
|
||||||
const snap = JSON.parse(fs.readFileSync(pfad, "utf-8"));
|
const snap = JSON.parse(fs.readFileSync(pfad, "utf-8"));
|
||||||
const src = fs.readFileSync("web/app.js", "utf-8");
|
const src = fs.readFileSync("web/app.js", "utf-8");
|
||||||
|
// Die real vorhandenen Element-IDs aus der ausgelieferten HTML.
|
||||||
|
const HTML_IDS = new Set(
|
||||||
|
[...fs.readFileSync("web/index.html", "utf-8").matchAll(/id="([^"]+)"/g)]
|
||||||
|
.map((m) => m[1]));
|
||||||
|
if (HTML_IDS.size < 50) {
|
||||||
|
console.log(` ❌ nur ${HTML_IDS.size} IDs aus index.html gelesen — Probe greift nicht`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
function el() {
|
function el() {
|
||||||
const o = {
|
const o = {
|
||||||
@@ -44,7 +52,15 @@ function el() {
|
|||||||
const ctx = {
|
const ctx = {
|
||||||
console,
|
console,
|
||||||
document: {
|
document: {
|
||||||
getElementById: () => el(), querySelector: () => el(),
|
// ⚠⚠ NUR IDs, die es in `index.html` wirklich gibt (Fix 2026-08-20).
|
||||||
|
// Vorher lieferte die Attrappe fuer JEDE ID ein Element — damit konnte
|
||||||
|
// `$streng()` in der Probe NIE `null` zurueckgeben, und genau der Fall,
|
||||||
|
// fuer den es `$streng` gibt, war ungeprueft. Real aufgefallen beim
|
||||||
|
// Ausblenden von `#hl-note`: der Hauptzweig griff ungeschuetzt auf
|
||||||
|
// `hlEl.classList` zu, die Probe blieb gruen, im Browser haette es den
|
||||||
|
// GESAMTEN Render abgebrochen.
|
||||||
|
getElementById: (id) => (HTML_IDS.has(id) ? el() : null),
|
||||||
|
querySelector: () => el(),
|
||||||
querySelectorAll: () => [], createElement: () => el(),
|
querySelectorAll: () => [], createElement: () => el(),
|
||||||
addEventListener() {}, body: el(), documentElement: el(),
|
addEventListener() {}, body: el(), documentElement: el(),
|
||||||
activeElement: null, hidden: false, visibilityState: "visible",
|
activeElement: null, hidden: false, visibilityState: "visible",
|
||||||
|
|||||||
@@ -748,9 +748,19 @@ function renderKontext(snap) {
|
|||||||
trN.textContent = trTxt || "";
|
trN.textContent = trTxt || "";
|
||||||
trN.classList.toggle("hidden", !trTxt);
|
trN.classList.toggle("hidden", !trTxt);
|
||||||
}
|
}
|
||||||
const hlEl = $("hl-note");
|
// ⚠ `$streng`, nicht das Auffang-`$`: das Element ist am 2026-08-20 auf
|
||||||
|
// User-Wunsch aus der Meldungen-Karte ENTFERNT. Mit dem Auffang-`$` liefe
|
||||||
|
// der Block ins Leere und meldete bei jedem Laden eine Konsolen-Warnung —
|
||||||
|
// ein Dauer-Fehlalarm fuer einen gewollten Zustand.
|
||||||
|
const hlEl = $streng("hl-note");
|
||||||
const h = snap && snap.hl_ctx;
|
const h = snap && snap.hl_ctx;
|
||||||
if (h && h.premium != null) {
|
// ⚠⚠ `hlEl` MUSS mitgeprueft werden. Das Element ist am 2026-08-20 auf
|
||||||
|
// User-Wunsch entfernt worden; ohne diese Bedingung wirft `hlEl.classList`
|
||||||
|
// einen TypeError — und der bricht den GESAMTEN Render ab (Dashboard
|
||||||
|
// friert auf Altwerten ein). Die else-Zweige hatten den Waechter, der
|
||||||
|
// Hauptzweig nicht. Gefunden von der Render-Probe, nachdem deren eigener
|
||||||
|
// blinder Fleck behoben war (die Attrappe gab fuer JEDE ID ein Element).
|
||||||
|
if (hlEl && h && h.premium != null) {
|
||||||
const fr = (h.funding != null) ? (h.funding * 100).toFixed(4) + " %" : "—";
|
const fr = (h.funding != null) ? (h.funding * 100).toFixed(4) + " %" : "—";
|
||||||
const oi = (h.open_interest != null)
|
const oi = (h.open_interest != null)
|
||||||
? (h.open_interest / 1e6).toFixed(2) + " Mio" : "—";
|
? (h.open_interest / 1e6).toFixed(2) + " Mio" : "—";
|
||||||
@@ -761,7 +771,7 @@ function renderKontext(snap) {
|
|||||||
+ "der Perp notiert über dem Index, Longs zahlen. ⚠ Gemessen über 5.095 "
|
+ "der Perp notiert über dem Index, Longs zahlen. ⚠ Gemessen über 5.095 "
|
||||||
+ "Funding-Stundenwerte (7 Monate) gegen den CFD: kein belastbarer "
|
+ "Funding-Stundenwerte (7 Monate) gegen den CFD: kein belastbarer "
|
||||||
+ "Prognosewert.";
|
+ "Prognosewert.";
|
||||||
} else {
|
} else if (hlEl) {
|
||||||
hlEl.classList.add("hidden");
|
hlEl.classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1013,6 +1023,49 @@ function renderCone(cn) {
|
|||||||
// ⚠ Bilder mit `loading="lazy"`: die 15 Vorlagen liegen verkleinert bei ~25 KB
|
// ⚠ Bilder mit `loading="lazy"`: die 15 Vorlagen liegen verkleinert bei ~25 KB
|
||||||
// (Originale 436–611 KB), aber über WireGuard soll nur geladen werden, was
|
// (Originale 436–611 KB), aber über WireGuard soll nur geladen werden, was
|
||||||
// wirklich sichtbar ist.
|
// wirklich sichtbar ist.
|
||||||
|
// ══ BILD-MODALBOX für die Chartmuster (User-Wunsch 2026-08-20) ═════════════
|
||||||
|
// ⚠⚠ BEWUSST OHNE BIBLIOTHEK — Begründung im Markup (`#bildbox`). Kurz: ein
|
||||||
|
// Lightbox-Paket wäre 15–40 KB plus ein zweites Bedienkonzept, das Dashboard
|
||||||
|
// läuft nur über WireGuard (kein CDN erreichbar), und das Projekt hat aus
|
||||||
|
// demselben Grund Tailwind abgelehnt und die vendorte Chart-Bibliothek
|
||||||
|
// entfernt. Was hier fehlt, sind ~30 Zeilen.
|
||||||
|
// ⚠ Delegierter Klick auf `document`: die Kacheln werden bei JEDEM Render neu
|
||||||
|
// erzeugt (`innerHTML = …`). Ein Handler direkt am `<img>` wäre nach dem
|
||||||
|
// nächsten Snapshot weg — genau die Sorte Fehler, die nur sporadisch auffällt.
|
||||||
|
function bildboxOeffnen(bild, text) {
|
||||||
|
const box = $streng("bildbox"), img = $streng("bildbox-img");
|
||||||
|
if (!box || !img) return;
|
||||||
|
// Grosse Fassung (~133 KB) erst JETZT laden, nicht mit dem Dashboard.
|
||||||
|
img.src = `muster/gross/${bild}.jpg`;
|
||||||
|
img.alt = text || "";
|
||||||
|
const t = $streng("bildbox-txt");
|
||||||
|
if (t) t.textContent = text || "";
|
||||||
|
box.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function bildboxSchliessen() {
|
||||||
|
const box = $streng("bildbox");
|
||||||
|
if (box) box.classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("click", (e) => {
|
||||||
|
const t = e.target;
|
||||||
|
if (t && t.classList && t.classList.contains("pt-img") && t.dataset
|
||||||
|
&& t.dataset.bild) {
|
||||||
|
bildboxOeffnen(t.dataset.bild, t.dataset.txt || t.alt || "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Klick auf den Hintergrund ODER den Schliessen-Knopf schliesst.
|
||||||
|
const box = $streng("bildbox");
|
||||||
|
if (box && !box.classList.contains("hidden")
|
||||||
|
&& (t === box || (t && t.id === "bildbox-zu"))) {
|
||||||
|
bildboxSchliessen();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Escape") bildboxSchliessen();
|
||||||
|
});
|
||||||
|
|
||||||
function renderPatternsTF(d) {
|
function renderPatternsTF(d) {
|
||||||
const el = $streng("pt-tfgrid");
|
const el = $streng("pt-tfgrid");
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
@@ -1028,7 +1081,9 @@ function renderPatternsTF(d) {
|
|||||||
const arr = m.dir === "bull" ? "▲" : m.dir === "bear" ? "▼" : "◆";
|
const arr = m.dir === "bull" ? "▲" : m.dir === "bear" ? "▼" : "◆";
|
||||||
const best = m.status === "confirmed";
|
const best = m.status === "confirmed";
|
||||||
const bild = m.bild
|
const bild = m.bild
|
||||||
? `<img class="pt-img" src="muster/${m.bild}.jpg" alt="${m.name}" loading="lazy">`
|
? `<img class="pt-img" src="muster/${m.bild}.jpg" alt="${m.name}" `
|
||||||
|
+ `data-bild="${m.bild}" data-txt="${tf} · ${m.name}" loading="lazy" `
|
||||||
|
+ `title="Klicken öffnet das Bild in voller Größe.">`
|
||||||
: `<div class="pt-img leer">kein Bild</div>`;
|
: `<div class="pt-img leer">kein Bild</div>`;
|
||||||
const q = (m.quality != null ? m.quality : 0).toFixed(2);
|
const q = (m.quality != null ? m.quality : 0).toFixed(2);
|
||||||
return `<div class="pt-tfcell${best ? " best" : ""}">`
|
return `<div class="pt-tfcell${best ? " best" : ""}">`
|
||||||
@@ -2405,7 +2460,12 @@ async function setMgGlobal() {
|
|||||||
// Ein zugeklapptes <details> ist nur nicht sichtbar - `textContent` wird
|
// Ein zugeklapptes <details> ist nur nicht sichtbar - `textContent` wird
|
||||||
// weiter gesetzt, und die naechste Oeffnung zeigt den aktuellen Stand.
|
// weiter gesetzt, und die naechste Oeffnung zeigt den aktuellen Stand.
|
||||||
{
|
{
|
||||||
const KEY = "oil_m15_akk";
|
// ⚠⚠ SCHLUESSEL GEBUMPT (2026-08-20). Der Default ist jetzt
|
||||||
|
// "eingeklappt" — aber im `localStorage` steht bei jedem, der die Karte je
|
||||||
|
// aufgeklappt hatte, noch `true`, und das gewinnt gegen den neuen Default.
|
||||||
|
// Ein neuer Schluessel setzt genau EINMAL zurueck; danach merkt sich die
|
||||||
|
// Karte die eigene Wahl wie bisher.
|
||||||
|
const KEY = "oil_m15_akk2";
|
||||||
let stand = {};
|
let stand = {};
|
||||||
try { stand = JSON.parse(localStorage.getItem(KEY) || "{}"); } catch (e) { stand = {}; }
|
try { stand = JSON.parse(localStorage.getItem(KEY) || "{}"); } catch (e) { stand = {}; }
|
||||||
document.querySelectorAll("details.m15-akk").forEach(el => {
|
document.querySelectorAll("details.m15-akk").forEach(el => {
|
||||||
@@ -2459,7 +2519,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 = "205";
|
const _JS_V = "207";
|
||||||
{
|
{
|
||||||
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=205">
|
<link rel="stylesheet" href="style.css?v=207">
|
||||||
</head>
|
</head>
|
||||||
<body data-html-v="205">
|
<body data-html-v="207">
|
||||||
<!-- 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>
|
||||||
@@ -139,7 +139,11 @@
|
|||||||
dann zu diesen Preisen. -->
|
dann zu diesen Preisen. -->
|
||||||
<div class="m15-setup" id="m15-setupblock" hidden></div>
|
<div class="m15-setup" id="m15-setupblock" hidden></div>
|
||||||
<div class="m15-grid">
|
<div class="m15-grid">
|
||||||
<details class="m15-akk" data-akk="prüfkette" open>
|
<!-- ⚠ KEIN `open` mehr (User-Wunsch 2026-08-20): alle Punkte der
|
||||||
|
M15-Karte sind per Default EINGEKLAPPT. Der zuletzt vom
|
||||||
|
Nutzer gewaehlte Zustand wird weiterhin gemerkt — nur der
|
||||||
|
Ausgangszustand ist jetzt zu. -->
|
||||||
|
<details class="m15-akk" data-akk="prüfkette">
|
||||||
<summary>① – ④ Prüfkette</summary>
|
<summary>① – ④ Prüfkette</summary>
|
||||||
<div class="m15-item" id="m15-bias" title="Uebergeordneter Trend M30 + H1. Der EINZIGE belegte Filter: gegen den HTF zu handeln halbiert den Edge (gemessen ×2).">—</div>
|
<div class="m15-item" id="m15-bias" title="Uebergeordneter Trend M30 + H1. Der EINZIGE belegte Filter: gegen den HTF zu handeln halbiert den Edge (gemessen ×2).">—</div>
|
||||||
<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-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>
|
||||||
@@ -174,14 +178,20 @@
|
|||||||
gemessen (`analyze_hl_funding.py`, 5.095 Stundenwerte, 7 Monate)
|
gemessen (`analyze_hl_funding.py`, 5.095 Stundenwerte, 7 Monate)
|
||||||
hält entüberlappt KEIN Bucket die 2-Stichproben-Regel. Kein
|
hält entüberlappt KEIN Bucket die 2-Stichproben-Regel. Kein
|
||||||
Verdict-Gewicht, kein Trigger. -->
|
Verdict-Gewicht, kein Trigger. -->
|
||||||
<div class="hl-note hidden" id="hl-note"></div>
|
<!-- ⚠ AUSGEBLENDET am 2026-08-20 (User-Wunsch): Hyperliquid-Kontext
|
||||||
|
(`#hl-note`), M1/M5-Muster (`#pat-note`) und Trailing-Zustand
|
||||||
|
(`#trail-note`). Die Elemente sind ENTFERNT, nicht nur versteckt —
|
||||||
|
die drei Render-Bloecke greifen ueber `$streng` zu und ueberspringen
|
||||||
|
sich damit sauber. Das Backend rechnet unveraendert weiter
|
||||||
|
(`hl_ctx`, `patterns_tf`, `trail_info` stehen im Snapshot), die
|
||||||
|
Muster-Bilder haben ihre eigene Karte im Dashboard.
|
||||||
|
Zurueckholen = die drei <div class="hl-note hidden" id="..."> hier
|
||||||
|
wieder einsetzen; am JS ist nichts zu aendern. -->
|
||||||
<!-- 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:
|
<!-- M1/M5-Muster kompakt (User 2026-08-20). Bewusst OHNE Bild:
|
||||||
die Meldungen-Karte ist der Live-Textstrom zum laufenden Trade. -->
|
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>
|
|
||||||
<!-- 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,
|
||||||
backtest_flipclose2.py), läuft auf ausdrücklichen User-Wunsch. Hatte
|
backtest_flipclose2.py), läuft auf ausdrücklichen User-Wunsch. Hatte
|
||||||
@@ -431,6 +441,24 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Modal (Bestätigung / Token-Eingabe) -->
|
<!-- Modal (Bestätigung / Token-Eingabe) -->
|
||||||
|
<!-- ══ BILD-MODALBOX (User-Wunsch 2026-08-20) ═══════════════════════════════
|
||||||
|
Klick auf ein Chartmuster-Bild öffnet es in voller Größe.
|
||||||
|
⚠⚠ BEWUSST OHNE BIBLIOTHEK. Ein Lightbox-Paket (GLightbox, PhotoSwipe …)
|
||||||
|
wäre 15–40 KB plus ein zweites Bedienkonzept — für ~40 Zeilen Markup und
|
||||||
|
CSS. Das Projekt hat aus demselben Grund Tailwind abgelehnt (WireGuard,
|
||||||
|
offline, kein npm) und am 05.08. die vendorte lightweight-charts (164 KB)
|
||||||
|
wieder entfernt. Ein CDN scheidet ohnehin aus: das Dashboard läuft nur
|
||||||
|
über WireGuard, ohne Weg ins Internet.
|
||||||
|
⚠ Die große Fassung (`muster/gross/…`, ~133 KB) wird ERST beim Klick
|
||||||
|
geladen — die Kachel zeigt die 25-KB-Fassung. -->
|
||||||
|
<div id="bildbox" class="bildbox hidden">
|
||||||
|
<button id="bildbox-zu" class="bildbox-zu" title="Schließen (Esc)">✕</button>
|
||||||
|
<figure class="bildbox-inner">
|
||||||
|
<img id="bildbox-img" alt="">
|
||||||
|
<figcaption id="bildbox-txt"></figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="modal" class="modal hidden">
|
<div id="modal" class="modal hidden">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-title" id="modal-title"></div>
|
<div class="box-title" id="modal-title"></div>
|
||||||
@@ -445,6 +473,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="toast" class="toast hidden"></div>
|
<div id="toast" class="toast hidden"></div>
|
||||||
|
|
||||||
<script src="app.js?v=205"></script>
|
<script src="app.js?v=207"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -215,7 +215,7 @@ main{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
|
|||||||
.trade-bar{padding:0.4444rem 0.6667rem;border-color:#2d4a2d}
|
.trade-bar{padding:0.4444rem 0.6667rem;border-color:#2d4a2d}
|
||||||
.trade-bar.hidden{display:none}
|
.trade-bar.hidden{display:none}
|
||||||
#card-trail.hidden{display:none} /* Trailing-Kachel ausgeblendet, 2026-07-22 */
|
#card-trail.hidden{display:none} /* Trailing-Kachel ausgeblendet, 2026-07-22 */
|
||||||
.tb-title{margin:0 0 0.4444rem;font-size:0.7778rem;letter-spacing:1.5px;text-transform:uppercase;
|
.tb-title{margin:0 0 0.4444rem;font-size:0.6667rem;letter-spacing:1.5px;text-transform:uppercase;
|
||||||
color:var(--dim);font-weight:600}
|
color:var(--dim);font-weight:600}
|
||||||
/* Laufzeit-Uhr: im Titel mitlaufend (kompakt) + eigenes Feld in der Zeile */
|
/* Laufzeit-Uhr: im Titel mitlaufend (kompakt) + eigenes Feld in der Zeile */
|
||||||
.tb-runtime{margin-left:0.4444rem;color:var(--bright);font-family:var(--mono);
|
.tb-runtime{margin-left:0.4444rem;color:var(--bright);font-family:var(--mono);
|
||||||
@@ -225,7 +225,7 @@ main{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
|
|||||||
/* EINHEITLICHES Feld-Design: Anzeige-Boxen (Richtung/Lots/Einsatz) und Eingabe-
|
/* EINHEITLICHES Feld-Design: Anzeige-Boxen (Richtung/Lots/Einsatz) und Eingabe-
|
||||||
Felder (SL/TP/Gewinn-Close/Notfall-Close) sehen identisch aus (Label über Box). */
|
Felder (SL/TP/Gewinn-Close/Notfall-Close) sehen identisch aus (Label über Box). */
|
||||||
.tb-field{display:flex;flex-direction:column;gap:2px}
|
.tb-field{display:flex;flex-direction:column;gap:2px}
|
||||||
.tb-field label{font-size:0.7222rem;color:var(--dim);letter-spacing:.5px}
|
.tb-field label{font-size:0.6111rem;color:var(--dim);letter-spacing:.5px}
|
||||||
/* ⚠ Feste 88px waren zu schmal (User 2026-08-19: „das Einsatz-Feld ist nicht
|
/* ⚠ Feste 88px waren zu schmal (User 2026-08-19: „das Einsatz-Feld ist nicht
|
||||||
breit genug"). Der Einsatz zeigt Betraege wie „712.84 €" — bei fett und
|
breit genug"). Der Einsatz zeigt Betraege wie „712.84 €" — bei fett und
|
||||||
0,94rem passt das nicht in 88px. `ch` = Breite einer Ziffer, haengt also
|
0,94rem passt das nicht in 88px. `ch` = Breite einer Ziffer, haengt also
|
||||||
@@ -236,7 +236,7 @@ main{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
|
|||||||
/* ⚠ font-weight 700 -> 400 (User-Wunsch 2026-08-20): alle Felder der
|
/* ⚠ font-weight 700 -> 400 (User-Wunsch 2026-08-20): alle Felder der
|
||||||
Trade-Leiste normal, nicht fett — Eingaben UND Anzeige-Boxen, damit
|
Trade-Leiste normal, nicht fett — Eingaben UND Anzeige-Boxen, damit
|
||||||
die Leiste einheitlich bleibt. */
|
die Leiste einheitlich bleibt. */
|
||||||
font-size:0.9444rem;font-weight:400;padding:0.3333rem 0.3889rem;text-align:center;
|
font-size:0.8333rem;font-weight:400;padding:0.3333rem 0.3889rem;text-align:center;
|
||||||
transition:border-color .15s, box-shadow .15s}
|
transition:border-color .15s, box-shadow .15s}
|
||||||
.tb-field .tb-val{display:block;white-space:nowrap}
|
.tb-field .tb-val{display:block;white-space:nowrap}
|
||||||
.tb-field input{width:8ch} /* Eingaben: feste, aber mitskalierende Basis */ /* nur Anzeige, kein Input */
|
.tb-field input{width:8ch} /* Eingaben: feste, aber mitskalierende Basis */ /* nur Anzeige, kein Input */
|
||||||
@@ -253,7 +253,7 @@ main{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
|
|||||||
/* ── Gesamtempfehlung ───────────────────── */
|
/* ── Gesamtempfehlung ───────────────────── */
|
||||||
/* Circuit Breaker Banner (Tagesverlust-Stopp) — ganz oben, rot bei ausgelöst */
|
/* Circuit Breaker Banner (Tagesverlust-Stopp) — ganz oben, rot bei ausgelöst */
|
||||||
.circuit{margin:0 0 0.6111rem;padding:0.6111rem 0.7778rem;border-radius:var(--r);font-weight:700;
|
.circuit{margin:0 0 0.6111rem;padding:0.6111rem 0.7778rem;border-radius:var(--r);font-weight:700;
|
||||||
font-size:0.8889rem;text-align:center;letter-spacing:.2px}
|
font-size:0.7778rem;text-align:center;letter-spacing:.2px}
|
||||||
.circuit.hidden{display:none}
|
.circuit.hidden{display:none}
|
||||||
.circuit.tripped{background:#3d1a1a;color:#ff6b6b;border:1px solid var(--down);
|
.circuit.tripped{background:#3d1a1a;color:#ff6b6b;border:1px solid var(--down);
|
||||||
box-shadow:0 0 0 1px var(--down)}
|
box-shadow:0 0 0 1px var(--down)}
|
||||||
@@ -734,3 +734,21 @@ main{padding-bottom:4.1111rem} /* Platz für die fixe Leiste */
|
|||||||
justify-content:center; font-size:0.6667rem; color:var(--dim)}
|
justify-content:center; font-size:0.6667rem; color:var(--dim)}
|
||||||
.pt-tfname{font-size:0.7778rem; font-weight:600; line-height:1.2}
|
.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)}
|
.pt-tfst{font-size:0.6667rem; color:var(--dim); font-family:var(--mono)}
|
||||||
|
|
||||||
|
/* ── Bild-Modalbox fuer die Chartmuster (User 2026-08-20) ──────────────────
|
||||||
|
⚠ Ohne Bibliothek, s. Begruendung im Markup. `dvh` statt `vh`, damit die
|
||||||
|
Adressleiste am Handy die Box nicht abschneidet; `vh` als Rueckfall zuerst. */
|
||||||
|
.bildbox{position:fixed; inset:0; z-index:60; display:flex;
|
||||||
|
align-items:center; justify-content:center; padding:0.8889rem;
|
||||||
|
background:#000000e6; cursor:zoom-out}
|
||||||
|
.bildbox.hidden{display:none}
|
||||||
|
.bildbox-inner{margin:0; max-width:100%; cursor:default; text-align:center}
|
||||||
|
.bildbox-inner img{max-width:100%; max-height:80vh; max-height:80dvh;
|
||||||
|
border-radius:var(--r); display:block; background:#0d1117}
|
||||||
|
.bildbox-inner figcaption{margin-top:0.4444rem; color:var(--dim);
|
||||||
|
font-size:0.7778rem; font-family:var(--mono)}
|
||||||
|
.bildbox-zu{position:absolute; top:0.6667rem; right:0.8889rem; z-index:61;
|
||||||
|
background:var(--surface-2); border:1px solid var(--border);
|
||||||
|
border-radius:var(--r-sm); color:var(--bright); cursor:pointer;
|
||||||
|
font-size:1.1111rem; line-height:1; padding:0.3333rem 0.5556rem}
|
||||||
|
.pt-img[data-bild]{cursor:zoom-in}
|
||||||
|
|||||||