📖 Trading System – Wiki

← Startseite
🔍
Stand: …

01 Überblick & Architektur

Wall-Street-Bulle als Sinnbild für das Marktregime dieses Systems

Das System besteht aus zehn eigenständigen Apps plus einer verbindenden Startseite (siehe Kurzüberblick-Tabelle in Abschnitt 3): den vier Kern-Web-Apps Hauptapp, Minervini Coach 2, Minervini Markets 360 und Daily Signal Hub (jeweils eine einzige HTML-Datei), fünf ergänzenden Screening-/Analyse-Tools Price-Action-Hub, Rotation-Dashboard, Markets360 Screener (Streamlit), dem Lokalen Trend-Screener und dem app-übergreifenden Backtest-Vergleich (seit 2026-08-17), sowie einer kuratierten externen Link-Sammlung TraderFox KI-Agenten. Alle projekteigenen Dateien liegen im iCloud-Ordner Trading-System/Maick Trading System und sind damit auf allen Macs synchron; die beiden Streamlit-/Screener-Tools haben ihren Quellcode bewusst in eigenen, externen Projektordnern (siehe deren Abschnitte). Der Mac mini ist ein optionaler Bonus, kein Dauerläufer (oft aus oder offline; die lokalen Server starten nicht automatisch, sondern nur bei Bedarf von Hand): Läuft er, liefert er alle Apps per HTTP aus und stellt die Daten-Endpunkte bereit — dadurch funktioniert alles auch auf iPhone und iPad. Zwingend nötig ist er trotzdem nicht: Startseite, Hauptapp, Coach, Signal-Hub, Price-Action-Hub, Rotation-Dashboard, Backtest-Vergleich und TraderFox KI-Agenten laufen komplett ohne ihn über die Cloudflare-Pages-Version (Abschnitt 2); nur die beiden reinen Scan-Werkzeuge (Markets360 Screener, Trend-Screener) sind als rechenintensive lokale Tools ohne eigenen Cloud-Fallback ausgelegt — ihre Cloud-Nachtläufe (GitHub Actions) speisen ihre Ergebnisse aber ohnehin automatisch in den Signal-Hub ein.

iPhone / iPad / MacBook ──HTTP──► Mac mini (optional, oft aus) │ Port 8090 MTS-Server ────────┤ Startseite, Hauptapp, Coach, Wiki, │ Price-Action-Hub, Rotation-Dashboard, │ Preis-Proxy, /data Port 8080 Markets-360-Server ┤ /markt /fred /sector-perf /status Port 8091 Signal-Hub-Server ─┤ Dashboard, Screening-Lauf, Push Port 8501 Markets360 Screener┤ Streamlit: VCP/RS-Rating-Vollscan Port 8765 Trend-Screener ────┤ Dashboard + /api/* (RS/VCP/Fundamentals) Port 8502 Streamlit-Cockpit ─┘ (optional, erweiterte Analyse) │ iCloud-Ordner = eine Datenquelle für alle Geräte

Live-Status der Server (wird beim Öffnen dieser Seite geprüft):

MTS-Server (8090) … Markets 360 (8080) … Signal-Hub (8091) …
Grundprinzip: Eine App = eine Datei = eine Aufgabe. Die Apps bleiben bewusst getrennt (Entflechtung) und verlinken sich gegenseitig — gemergt wird nichts. Inhalte existieren nur an einem Ort, andere Apps verlinken dorthin.
Minervini Trading Learning: Disziplin schlägt Intuition – täglich.
Leitgedanke: „Disziplin schlägt Intuition – täglich." Genau dafür sind Marktampel, Coach und Regelwerk in diesem System da.

02 Zugriff von allen Geräten

SituationAdresse
Offline-App / unterwegs (empfohlen)https://mts-hub.pages.dev/ — Cloudflare Pages, immer online (HTTPS). Auf dem iPhone in Safari öffnen → „Zum Home-Bildschirm". Läuft offline und ohne WLAN (Service-Worker-Cache); Live-Kurse/FRED über den Cloudflare Worker. Siehe cloudflare-pages/.
Zuhause im WLAN (Mac mini an)http://mac-mini-von-maick.local:8090/
Unterwegs zum Mac (Tailscale)https://mac-mini-von-maick.tail02bccd.ts.net/ — ohne :8090 (Port), mit https://, ohne „-2" im Namen; tailscale serve leitet HTTPS/443 auf den lokalen 8090-Server um (siehe Abschnitt „HTTPS für die Offline-App" unten). Nur wenn der Mac mini online ist. Korrektur 2026-07-18: zwei Fehler nacheinander behoben — die alte Zeile zeigte http://…:8090/ (falsches Schema/Port), die erste Korrektur hatte zusätzlich einen falschen Hostnamen mit „-2" (existiert im Tailnet nicht, tailscale cert nennt den echten Namen); die tailscale serve-Konfiguration war zudem auf den falschen „-2"-Namen eingerichtet und wurde auf den echten Hostnamen umgestellt.

Die Pages-Version ist der robuste Offline-/Mobil-Zugang (unabhängig vom Mac). Der Mac mini wird noch für KI-Committee und geräteübergreifende Daten-Sync gebraucht; ist er per Tailscale erreichbar, nutzen die Apps ihn automatisch, sonst läuft alles aus Cache + Worker. Die Signal-Hub-Neuberechnung läuft seit 2026-07 zusätzlich vollautomatisch in der Cloud (GitHub Actions, siehe Signal Hub) — braucht den Mini dafür nicht mehr; dessen Zeitplan läuft vorerst nur noch zur Kontrolle parallel. Nach Änderungen an den Apps neu deployen: cloudflare-pages/deploy.command (die Cloud-Pipeline deployt Signal-Hub-Ergebnisse zusätzlich automatisch nach jedem Lauf).

🚦 Echte 404 statt SPA-Fallback (seit 2026-09-10, Befund der Systemprüfung). Cloudflare Pages beantwortete bis dahin jeden unbekannten Pfad mit der index.html und Status 200. Für die Host-Erkennung in den Apps sah eine Anfrage an /status, /markt/… oder /Signal-Hub/api/status damit wie eine erfolgreiche Antwort aus (r.ok war true) — sie fiel erst durch, weil anschließend JSON.parse an <!DOCTYPE scheiterte. Gemessen auf der Startseite: 102 von 103 KB des gesamten Netzwerk-Traffics gingen an drei solche Fehlanfragen, bei jedem Aufruf neu, auch über Mobilfunk. Derselbe Fallback hatte zuvor schon zweimal echte Fehler verdeckt (fehlende Ruhestands-Fahrplan-Datei, fehlende .json-Dateien — siehe die Kommentare in deploy.command).

cloudflare-pages/static/_redirects beantwortet die Mac-mini-Server-Routen jetzt mit einem echten 404 (/status, /data, /ticker_map, /markt/*, /fred/*, /breadth, /marktampel, /sector-perf, /m360/*, /*/api/* u. a.), 404.html daneben erklärt Besuchern den Unterschied zwischen „kaputt" und „braucht den Mac mini". Bewusst kein /*-Catch-all: Pages' Clean-URL-Auflösung (/wiki.html → /wiki) und alle echten Dateien bleiben unberührt; /data steht ohne Splat, damit /Signal-Hub/data/… weiter ausgeliefert wird. Beide Dateien werden von deploy.command mitkopiert und gelten damit für den lokalen wie den Cloud-Deploy.

Passend dazu die Host-Erkennung entschärft: Läuft eine Seite selbst über HTTPS (Pages oder Tailscale), blockt der Browser jeden http://…:PORT-Aufruf als Mixed Content, bevor er das Netz erreicht — die Kandidaten waren dort also nie erreichbar, erzeugten aber pro Seitenaufruf 12–14 harte Konsolenfehler, in denen echte Fehler untergingen. index.html (kandidaten() + Signal-Hub-Check), Signal-Hub/signal-hub.html (ladeMarktCheck(), zumLiveServer()) und Minervini Dashboard.html (MTS_8090_ORIGIN) überspringen diese Kandidaten jetzt auf HTTPS. Markets 360 nutzt dort stattdessen einen same-origin-Pfad: über Tailscale erreicht der den 8090-Reverse-Proxy, auf Pages läuft er sauber in den neuen 404. Die Hauptapp machte das mit _looksLikeJson() schon vorher richtig und blieb unverändert.
Cloud-Fallback-Hinweis auf der Startseite (seit 2026-07-17). Ist der Mac mini über keinen der Host-Kandidaten (location.hostname / Mac-mini-von-Maick.local / localhost) erreichbar, zeigt index.html jetzt zusätzlich zum „Offline"-Badge einen direkten Link auf https://mts-hub.pages.dev/ — kein automatischer Redirect (ein stiller Host-Wechsel im Hintergrund wäre verwirrend), nur ein sichtbarer Ausweg, damit andere Geräte nicht in einer reinen Fehleranzeige hängen bleiben. Erscheint nicht, wenn die Seite selbst schon auf *.pages.dev läuft.
Zurück zur Startseite. Jede App hat oben links einen 🏠 Start-Button (Link auf /index.html) — funktioniert über Mini, Tailscale und Pages. Die Startseite selbst und das Wiki haben ihn nicht (Wiki hat den „← Startseite"-Link).
Fix 2026-08-15: Nur-Lese-Modus der Hauptapp auf der Cloud-Version entfernt — voll bearbeitbar von jedem Gerät, mit oder ohne laufenden Mac mini. Bisher blockte eine pauschale Origin-Sperre (*.pages.dev) in Maick's Trading System.html jedes Speichern auf der Cloud-Version komplett, obwohl der dafür nötige Cross-Device-Mechanismus — derselbe private GitHub-Gist-Sync wie beim Coach (Token gh_gist_token, Funktionen mtsGistSave/mtsGistLoad) — im Code bereits fertig eingebaut war. Widersprach dem Grundprinzip „Mac mini ist optionaler Bonus, kein Muss" (siehe oben). Die Sperre ist jetzt entfernt: jeder Origin (Mac mini, Tailscale, Pages) speichert gleich — localStorage auf dem jeweiligen Gerät + Gist-Push (falls Token eingerichtet) + optionaler POST /data an den Mini (existiert der Endpunkt nicht, z. B. auf Pages, läuft der Versuch harmlos ins Leere). Damit dabei kein älterer Cloud-Stand frischere lokale Edits verschluckt, vergleicht mtsGistLoad() jetzt Zeitstempel (_meta.lastSaved) vor jedem Überschreiben — genau wie der bereits bestehende /data-Abgleich mit dem Mac mini. Ohne eingerichteten Gist-Token bleiben Änderungen weiterhin nur im localStorage dieses einen Geräts (kein Cross-Device-Sync möglich) — dafür zeigt die App dann oben statt des alten 🔒-Banners einen ☁-Hinweis, den Token im ☁-Panel einzurichten (gleicher Token wie im Coach, einmal auf dem Gerät hinterlegt → in beiden Apps aktiv).
Ein Daten-Gist für alle Geräte (seit 2026-10-10). Am 08.10. war ein zweiter Gist „Maick Trading System Daten“ entstanden; ein Gerät schrieb seitdem dorthin, die anderen in den ursprünglichen — Journal-Einträge fehlten so auf einem Gerät. Die Hauptapp prüft deshalb bei jedem Start, ob es mehrere Gists mit maick-trading-system.json gibt, nimmt immer den zuerst angelegten und mischt den Stand des Geräts vorher pro Trade dort ein (Meldung „Auf den gemeinsamen Daten-Gist umgestellt“). Auch die Ersteinrichtung wählt diesen Gist. Ein überzähliger Gist kann danach von Hand gelöscht werden.
Fix 2026-08-09: Tailscale-Zugriff von einem neuen Gerät zeigte leere Daten statt des echten Mac-mini-Stands. Ursache war kein Netzwerkproblem, sondern ein Timing-Bug beim Start der Hauptapp: Ein Fix, der bei leerer Trade-Liste den Kontostand zurücksetzt, rief dafür sofort MTS.save() auf und stempelte damit _meta.lastSaved auf „jetzt" — auch bei einem komplett frischen Browser, dessen localStorage für diese Origin noch nie beschrieben wurde (z. B. erster Aufruf der Tailscale-Adresse von einem neuen Gerät). Der spätere Server-Sync (holt /data vom Mini und übernimmt es nur, wenn dessen Zeitstempel neuer ist als der lokale) hielt den gerade erst gestempelten leeren lokalen Stand danach für immer für „neuer" als die echten Mac-mini-Daten und übernahm sie nie. Fix: Der Reset-und-Save-Schritt läuft jetzt nur noch, wenn auf diesem Gerät/Origin bereits einmal gespeichert wurde (_meta.lastSaved vorhanden). War ein Gerät vor dem Fix schon betroffen (leerer localStorage-Eintrag bereits „vergiftet"), hilft einmalig in der Browser-Konsole auf der Seite: localStorage.removeItem('mts_v2'); location.reload(); — danach lädt der Server-Sync den echten Stand.
Echte Live-Status-Badges auf der Cloud-Version (seit 2026-07-18). Die Status-Prüfungen auf der Startseite (index.html) versuchen jetzt zusätzlich zu den LAN-Kandidaten (location.hostname, Mac-mini-von-Maick.local, localhost) den Tailscale-Hostnamen mac-mini-von-maick.tail02bccd.ts.net (Konstante TAILSCALE_HOST, Helfer mtsUrl(host) baut je nach Host https://… ohne Port oder http://…:8090). Läuft die Seite auf *.pages.dev und ist der Mac mini per Tailscale erreichbar, zeigen 8090-/Markets-360-/Signal-Hub-/Trend-Screener-Badges jetzt „✓ Live-Daten" statt pauschal „Offline"/„iCloud-Version" — funktioniert, weil server_mts.py für die gelesenen Endpunkte (/status, /markt/*, /Signal-Hub/api/*, /Trend-Screener/*) bereits Access-Control-Allow-Origin: * setzt. Reihenfolge: erst same-origin/LAN (schnell), Tailscale nur als letzter Fallback (etwas höheres Timeout, 3–3,5 s statt 1,5–2 s). Ist der Mac mini auch per Tailscale nicht erreichbar, bleibt es beim bisherigen Offline-Zustand inkl. Cloud-Fallback-Link (siehe oben). Ausnahme seit 2026-07-28: die „Signale"-Kachel zeigt in diesem Fall trotzdem eine echte Zahl statt „–" — ladeSignaleStatisch() liest per Range-Request (nur die ersten ~400 Bytes, nicht die vollen >10 MB) direkt Signal-Hub/data/signals.json, die derselbe Cloud-Deploy ohnehin schon mitbringt, und zeigt die Anzahl mit Badge „☁️ Cloud-Daten" (Tooltip: Stand des letzten Cloud-Laufs) statt „iCloud-Version" ohne Zahl. (Die „Server"/„Preis-Proxy"-Kacheln, die hier ursprünglich standen, gibt es seit 2026-08-12 nicht mehr — siehe Fix unten.)
Umbau 2026-08-12: „Server"/„Preis-Proxy"-Kacheln durch „Cloud-Daten" ersetzt. Die beiden ursprünglichen KPI-Kacheln auf der Startseite prüften ausschließlich Mac-mini-Eigenschaften (Erreichbarkeit, ob yfinance lokal installiert ist) und zeigten dadurch auf *.pages.dev bzw. ohne erreichbaren Mac mini dauerhaft „Offline"/„–" — ein rotes Warnsignal für einen Zustand, der laut Systemdesign (Mac mini ist optionaler Bonus, keine Voraussetzung) gar kein Problem ist. Das sorgte wiederholt für Verunsicherung („warum Server offline, sonst funktioniert doch alles"). Ersetzt durch eine einzige Kachel „Cloud-Daten", die die tatsächlich relevante Information zeigt — ist die Datenlage frisch? — statt eine Geräte-Eigenschaft: „Live" (grün), wenn gerade ein Server (Mac mini/Tailscale) live antwortet, sonst die Uhrzeit des letzten Cloud-Pipeline-Laufs mit Ampel-Farbe nach Alter (<6 h grün, 6–24 h gelb, >24 h rot, exakt dieselben Schwellen wie der Top-Setups-„Stand"-Banner). Funktioniert identisch von jedem Gerät, unabhängig vom Mac-mini-Status — index.html, Funktion setzeCloudFrische(). Das KPI-Grid hat dadurch nur noch 3 statt 4 Kacheln (Cloud-Daten, Signale, S&P heute). Die detaillierten Mac-mini-Infos (LAN-Adresse, Preis-Proxy-Status) stehen weiterhin im Bereich „Netzwerk & Zugriff" weiter unten auf der Startseite — dort als reine technische Zusatzinfo, nicht mehr als Alarm-Kachel oben.
Apps öffnen im neuen Tab — außer in der installierten PWA. Im normalen Browser-Tab öffnet jede App in einem eigenen Tab/Sheet (target="_blank"), damit die Startseite als Absprung erhalten bleibt. Läuft der Hub als installierte Standalone-App (Home-Bildschirm-Icon), navigiert er stattdessen im selben Fenster (siehe Fix 2026-07-05 unten).
Fix 2026-07-05: Service Worker registrierte sich auf Pages nie. Die SW-Registrierung (Manifest/Icon/Theme-Color-Injektion) passierte bisher nur serverseitig im 8090-Server (server_mts.py) — die auf *.pages.dev deployten Dateien enthielten den Registrierungscode gar nicht, wodurch dort nie gecacht wurde und echtes Offline-Verhalten (ohne jede Internetverbindung) unzuverlässig war. cloudflare-pages/deploy.command injiziert das jetzt beim Bauen des Publish-Verzeichnisses selbst in alle HTML-Dateien — ab dem nächsten Deploy cacht der Service Worker auf Pages zuverlässig.
Fix 2026-07-05b: Apps starteten offline nicht, Hub sprang immer zurück zum Start. Der Offline-Cache selbst funktionierte bereits — das eigentliche Problem war target="_blank" auf den App-Karten (index.html): Die installierte Standalone-PWA hat nur ein Fenster, kann also kein „neues Fenster" öffnen. iOS brach die Navigation deshalb ab und blieb auf dem Hub stehen. Fix: target="_blank" wird nur noch im normalen Browser-Tab gesetzt (Prüfung per display-mode: standalone / navigator.standalone); die installierte App navigiert jetzt im selben Fenster.
Fix 2026-08-08: 🔍-Detaillink landete auf Cloudflare Pages im Hub statt in der Setup-Analyse. Signal-Hub/setup-detail.html fehlte in der Kopier-Whitelist von cloudflare-pages/deploy.command — die Datei existierte auf mts-hub.pages.dev schlicht nicht. Ohne eigene _redirects/404.html-Konfiguration liefert Cloudflare Pages für unbekannte Pfade automatisch den Inhalt der Startseite mit Status 200 statt eines echten 404 zurück; der Browser blieb dabei aber auf dem angeforderten /Signal-Hub/…-Pfad stehen, wodurch zusätzlich alle relativen Pfade der zurückgelieferten Startseite doppelt/kaputt wurden. Mit dem Mac mini hatte das nichts zu tun — setup-detail.html liest ausschließlich aus den ohnehin deployten signals.json/pivot.json/priceaction.json und ist wie der Rest bewusst ohne Mac-mini-Abhängigkeit gebaut. Fix: deploy.command kopiert die Datei jetzt mit (neben signal-hub.html/backtest-report.html).
Fix 2026-08-06: Top-Setups-Detaillink (🔍) sprang in der Standalone-PWA ins Leere. Derselbe target="_blank"-Fehler wie beim Fix 2026-07-05b, nur an einer zweiten, übersehenen Stelle: Der 🔍-Link auf die Minervini-Treiber-Analyse (Signal-Hub/setup-detail.html?ticker=…) in den Top-Setups auf der Startseite wird erst nach Seitenaufbau dynamisch erzeugt (renderTopSetups()) und war deshalb von der ursprünglichen Standalone-Prüfung nicht erfasst — er hatte weiterhin fest target="_blank". In der installierten Standalone-PWA (iPhone) brach iOS die Navigation deshalb ab, die App fiel auf den zuletzt gecacheten Stand zurück, statt zur Analyse des angetippten Werts zu springen. Fix: index.html ermittelt IST_STANDALONE_PWA jetzt zentral am Skriptanfang und nutzt dieselbe Prüfung auch beim dynamisch erzeugten Detaillink — target="_blank" nur noch außerhalb der Standalone-PWA.
Fix 2026-07-05c: „Response served by service worker has redirections". Cloudflare Pages leitet jede *.html-URL per 308 auf die endungslose Clean-URL um (/index.html → /, /wiki.html → /wiki usw.) — solange kein Service Worker eingreift, folgt der Browser dem Redirect normal. Sobald der SW aber (seit Fix 2026-07-05) sauber registriert ist, fängt er die Navigation ab; die per fetch() gefolgte Redirect-Response hat redirected=true, und genau das darf laut Fetch-Spec nicht per respondWith() an eine Navigation zurückgegeben werden — Safari brach mit obiger Fehlermeldung ab. Fix in sw.js: stripRedirectFlag() baut vor Cache-Ablage und vor respondWith() eine frische, redirect-freie Response (new Response(res.body, res)); betrifft Precache und Live-Navigation gleichermaßen (Cache Storage übernimmt das Redirect-Flag sonst mit).
Fix 2026-08-03: Service-Worker-Cache-Version jetzt automatisch. Vorher musste nach jeder Änderung an einer Precache-Datei (index.html u. a.) die Zahl in sw.js (const CACHE = 'mts-pwa-vNN') von Hand hochgezählt werden — sonst blieb die installierte App/PWA für immer auf dem alten Dateistand hängen (dreimal vergessen, zuletzt mit sichtbarem Effekt: veraltete Startseite, Links landeten wieder auf dem alten Cache-Fallback statt der Zielseite). Neues sw_version.py berechnet die Version stattdessen aus einem Hash über den tatsächlichen Inhalt aller Precache-Dateien — ändert sich irgendeine davon, ändert sich automatisch sw.js selbst, der Browser erkennt das über seinen eingebauten Byte-Vergleich und installiert die neue Version ganz ohne manuelles Zutun. Zwei Serving-Wege nutzen dieselbe Logik: server_mts.py (neue Route /sw.js, rendert live pro Anfrage) und cloudflare-pages/deploy.command (neuer Schritt nach dem Kopieren/Injizieren, rendert gegen den finalen Publish-Ordner). Der Wert im Quelltext von sw.js selbst ist nur noch ein Platzhalter.
Fix 2026-08-12: Top-Setups auf der Startseite blieben tagelang auf altem Stand hängen, obwohl die Cloud-Pipeline lief. Server-seitig war nichts kaputt — jeder Cloud-Lauf produzierte pünktlich frische Daten. Ursache war sw.js: Live-Daten unter Signal-Hub/data/… (und den drei Schwester-Ordnern) werden „network-first" behandelt (immer erst frisch vom Netz versuchen), aber die Prüfung griff nur bei Dateiendung .json. Die kleine Zusammenfassungsdatei, die den „Stand"-Banner der Top-Setups speist (top_setups.js), fiel dadurch stattdessen unter die „stale-while-revalidate"-Regel für gewöhnliche Assets: Die zeigt immer zuerst die vorletzte gecachte Version an und aktualisiert den Cache nur im Hintergrund für den nächsten Aufruf. Bei einer installierten Standalone-PWA, die aus dem Hintergrund resumt statt komplett neu zu laden, feuerte dieser Hintergrund-Fetch praktisch nie — der Banner blieb dadurch tage- statt stundenaktuell hängen, auf Mac und iPhone gleichermaßen. Fix: top_setups.js ist jetzt explizit von der network-first-Regel erfasst, genau wie die .json-Dateien.
Fix 2026-08-13: deploy.command fror den Datenstand tagelang ein. Ein Fix vom Vortag ließ deploy.command Signal-Hub/Price-Action-Hub/Rotation-Dashboard-Datendateien immer zuerst vom aktuell live deployten Stand holen (Grund: eine lokale Datei auf einem Mac war tagealt und hatte bei einem manuellen Deploy den frischen Cloud-Stand überschrieben). Das griff aber auch beim automatischen Cloud-Deploy — der ruft dasselbe Skript per GitHub Actions auf, nachdem er die frischen Dateien per rclone aus R2 gezogen hat (siehe deploy.yml). Dort war die „lokale" Kopie die frische, und „Live bevorzugen" hat sie bei jedem automatischen Deploy durch die bereits veraltete Live-Version ersetzt — ein sich selbst verewigender Stillstand, der die Startseite ab dem ursprünglichen Fix für Stunden auf demselben Zeitstempel einfror, obwohl die Pipeline daneben weiterlief und immer wieder „erfolgreich" deployte. Fix: deploy.command unterscheidet jetzt per $GITHUB_ACTIONS-Umgebungsvariable (von Actions automatisch gesetzt) den Kontext — in der Cloud immer die lokale (frische) Kopie nehmen, nur bei einem manuellen Deploy auf einem Mac weiterhin die Live-Version bevorzugen.
Fix 2026-08-21: Trend-Screener-Dashboard zeigte nach einem frischen Cloud-Lauf beim ersten Laden noch den alten Stand. Derselbe Fehler wie beim Fix 2026-08-12, nur an einer zweiten, damals übersehenen Stelle: Trend-Screener/signals.js speist den „Stand"-Zeitstempel im Trend-Screener-Dashboard direkt (analog zu top_setups.js auf der Startseite), liegt aber im App-Root und nicht unter einem …/data/-Pfad — die isProxiedData-Prüfung in sw.js griff dadurch nicht, die Datei fiel unter die „stale-while-revalidate"-Regel und zeigte beim ersten Laden nach einem Deploy die vorletzte gecachte Version. Fix: signals.js ist jetzt wie top_setups.js explizit von der network-first-Regel erfasst.
🚦 Test-Gate vor jedem Cloudflare-Deploy (seit 2026-09-13, Systemprüfung Punkt 10): deploy.yml lässt vor dem eigentlichen Deploy-Schritt zwei Prüfungen laufen — tests/test_*.py (Pytest, alle acht Testdateien, wenige Sekunden) und tests/smoke-audit.js (JS-Laufzeitfehler, tote Links, iPhone-Tap-Targets, per file:// ohne eigenen Server). Schlägt eine der beiden fehl, bricht der Workflow ab, bevor „Nach Cloudflare Pages deployen" erreicht wird — das ist der einzige Punkt, an dem alle Wege zur Auslieferung zusammenlaufen (manueller Dispatch, Signal-Hub-Pipeline, die beiden schlanken deploy-trigger.yml von Price-Action-Hub/Rotation-Dashboard). Zusätzlich ein eigener, schlanker tests.yml nur mit dem Pytest-Teil bei jedem Push in dieses Repo, für schnelles Feedback ohne auf einen Deploy zu warten. Die Testdateien selbst liegen bewusst in diesem (privaten) Root-Repo statt in signal-hub — sie testen zwar Code aus den drei nested Repos (sys.path.insert auf Signal-Hub/src usw.), aber Root ist der einzige Ort, an dem alle drei Codebasen und die Deploy-Logik zusammenkommen.

Gleich beim ersten echten Lauf zwei reale Befunde, kein Fehlalarm: Der Smoke-Audit-Gate schlug fehl, bevor er grün wurde — Signal-Hub/kauf-review.html definierte ladeFehlgeschlagen() in einem <script>-Block nach dem <script src="data/top_setups.js" onerror="ladeFehlgeschlagen()">-Tag, das genau diese Funktion aufruft — fehlt top_setups.js (jeder frische Checkout ohne Pipeline-Lauf), scheiterte der Fehlerhandler selbst mit „is not defined" und die Seite blieb für immer auf „Lädt Top-Setups …" hängen. Lokal nie aufgefallen, weil im Arbeitsverzeichnis längst eine echte top_setups.js aus vergangenen Pipeline-Läufen liegt. Zweiter Befund war kein App-Fehler: Markets 360s Wochenanalyse-Link ruft api.github.com bewusst ohne Token ab (siehe dortiger Wiki-Absatz) — auf dem geteilten GitHub-Actions-Runner-Pool greift dabei zuverlässig GitHubs anonymes Rate-Limit (HTTP 403), reproduzierbar in CI, nicht lokal; jetzt gezielt (nicht pauschal für alle HTTP-403-Meldungen) in IGNORIERTE_FEHLER gefiltert.
GitHub-Token nur einmal. Hauptapp und Coach teilen sich denselben GitHub-Token über den gemeinsamen localStorage-Schlüssel gh_gist_token (gleiche Origin). Einmal in einer der beiden Apps eingeben → in der anderen automatisch aktiv; fehlt er, fragt das ☁-Banner danach. Der Token bleibt ein Secret und liegt nur im Browser des Geräts — nie im Code, nie auf Pages (sonst wäre er auf der öffentlichen Seite auslesbar).
Falle 2026-08-17: Gist-Token pro Origin, nicht pro Gerät — und Safari räumt ihn auf pages.dev von selbst wieder weg. Zwei getrennte Effekte, die zusammen wie Datenverlust aussahen, es aber nicht waren: (1) localStorage ist pro Origin isoliert — ein auf mac-mini-von-maick.local:8090/der Tailscale-Adresse eingerichteter Token liegt in einem anderen Speicherbereich als auf mts-hub.pages.dev und muss dort separat eingetragen werden, sonst zeigt genau dieser Origin dauerhaft den ☁-Hinweis „Kein Cloud-Sync eingerichtet" und einen leeren/veralteten Stand (leere trades[] lösen zusätzlich _resetBalancesIfEmpty() aus, Kontostände zeigen dann 0, obwohl mts_data.json auf dem Mac mini unberührt und korrekt ist — MTS.save() pusht zwar bei jedem Origin automatisch debounced an den Gist, aber eben nur, wenn dort überhaupt ein Token hinterlegt ist). War der Token bislang nur auf pages.dev eingerichtet, nie auf dem tatsächlich bearbeiteten Mac-mini-/Tailscale-Origin, blieb der Gist entsprechend auf einem alten Stand stehen. (2) Safaris „Intelligent Tracking Prevention" räumt localStorage selten besuchter Origins nach rund einer Woche automatisch leer — trifft pages.dev besonders hart, weil das laut Konzept genau der Fallback-Pfad ist, der nur bei ausgeschaltetem Mac mini überhaupt aufgerufen wird, also unregelmäßig. Fix/Vorgehen: Token auf jedem tatsächlich genutzten Origin einmal eintragen (auch auf dem Mac-mini-/Tailscale-Origin selbst, nicht nur auf Pages!) — beim Einrichten sucht mtsGistSetup() aktiv nach einem bereits existierenden Gist mit der richtigen Datei (gists.find(...)) statt blind einen neuen anzulegen, das Verbinden ist also gefahrlos wiederholbar. Gegen das ITP-Wegräumen speziell auf pages.dev in Safari hilft „Zum Dock hinzufügen" (macOS Sonoma+) bzw. „Zum Home-Bildschirm" (iOS) für genau diese Adresse — eine installierte Web-App bekommt einen eigenen, von der 7-Tage-Bereinigung ausgenommenen Speicherbereich (apple-mobile-web-app-capable ist in Maick's Trading System.html bereits gesetzt, kein Code nötig). Nur auf Geräten/Browsern nötig, die pages.dev unregelmäßig als Fallback nutzen — der Mac mini selbst braucht das nicht (der ist ja an, wenn er benutzt wird), Chrome/Firefox kennen dieses ITP-Verhalten ohnehin nicht.
Fix 2026-08-23: „Retry" im Gist-Sync-Fehler-Banner führte in eine Sackgasse ohne Eingabefeld. Schlug mtsGistSetup() fehl (z. B. leeres Token-Feld oder ungültiger Token), ersetzte _mtsGistBannerError() das komplette Banner-HTML durch Warnhinweis + „Retry"-Button — und entfernte dabei das Token-Eingabefeld mit. Der „Retry"-Button rief aber weiterhin mtsGistSetup() auf, das genau dieses (nun fehlende) Feld ausliest — ein Klick lieferte dadurch sofort wieder denselben Fehler, ohne je wieder eine Möglichkeit zur Eingabe zu zeigen. Fix: „Retry" ruft jetzt _mtsGistBannerSetup() auf und zeigt damit wieder die Eingabemaske.
Fix 2026-09-21: „☁ Cloud-Sync einrichten" in der Sperrfläche wirkte funktionslos. Der Button sitzt oben im gesperrten Cockpit-Terminal, das Token-Eingabefeld aber ganz unten in „Datenspeicherung & Sync" am Dashboard-Ende. mtsGistSetup() liest #mts-gist-token-input aus und schrieb bei leerem Feld nur „Bitte Token eingeben" in das Banner dort unten — also weit ausserhalb des Sichtbereichs. Für den Nutzer passierte beim Klick sichtbar nichts, der einzige angebotene Weg zur Einrichtung war damit eine Sackgasse. Fix: neue Hilfsfunktion mtsGistPanelZeigen() scrollt das ☁-Panel ins Bild, setzt den Fokus ins Token-Feld und hebt es kurz hervor; mtsGistSetup() ruft sie bei leerem Feld auf statt der unsichtbaren Fehlermeldung. Der Sperrflächen-Text nennt jetzt zusätzlich das Ziel („springt zum Token-Feld am Ende des Dashboards"). Zusammen mit der Origin-Falle oben war das der Grund, warum ein Gerät ohne Token nicht mehr aus der Sperrfläche herausfand — die Depotdaten lagen die ganze Zeit vollständig im Gist.
Falle 2026-09-21: der Gist-Sync braucht einen klassischen Token — ein fine-grained Token meldete „Sync aktiv" und lud trotzdem nichts. GitHubs Gists-API unterstützt fine-grained PATs (github_pat_…) nicht, antwortet auf GET /gists aber mit einer leeren Liste statt mit einem Fehler. Für mtsGistSetup() sah das aus wie „es gibt noch keinen Gist": es speicherte den Token, rief mtsGistSave() zum Neuanlegen auf — dessen POST scheiterte still, weil der Fehlschlag nur nach #mts-gist-status geschrieben wurde, ein Element, das bei der Ersteinrichtung noch gar nicht existiert — und meldete anschließend „☁ Sync aktiv". Da nie eine Gist-ID gespeichert wurde, stieg mtsGistLoad() danach bei if (!token || !id) return; sofort aus: grünes Banner, leeres Depot, kein neuer Gist, der echte Gist unberührt. Vier Korrekturen: (1) mtsGistSetup() weist Token mit dem Präfix github_pat_ sofort mit klarer Begründung ab; (2) mtsGistSave() liefert jetzt true/false zurück statt den Fehlschlag zu verschlucken, mtsGistSetup() wertet das aus und verwirft den Token bei Misserfolg wieder; (3) mtsGistInit() zeigt „Sync aktiv" nur noch mit Token und Gist-ID; (4) derselbe Test entscheidet in renderCockpitTerminal(), ob der Sperrflächen-Button „Vom Cloud-Sync laden" (lief sonst ins Leere) oder „Cloud-Sync einrichten" anbietet. Richtiger Token: klassischer Token mit gist-Scope — nicht zu verwechseln mit dem fine-grained gh_actions_token für die Cloud-Trigger-Buttons, der bewusst ein anderer ist.
☁️ Cloud-Scan manuell anstoßen — von jedem Gerät (seit 2026-07-17, Repo-Struktur seit 2026-07-28 geändert). Signal Hub und Price-Action-Hub (je 🔄 Aktualisieren/☁️ Cloud-Scan starten), Markets 360 (☁️ Cloud-Scan starten, im Kontrollbereich oben) und der Trend-Screener (☁️ Cloud (überall), in der Update-Leiste) haben je einen Button, der den passenden GitHub-Actions-Workflow direkt per REST-API anstößt (POST .../actions/workflows/nightly.yml/dispatches bzw. pipeline.yml) — läuft ohne Mac mini, funktioniert also auch vom iPhone/iPad/MacBook unterwegs. Signal Hub UND Price-Action-Hub lösen beide denselben Workflow aus (Miksch52/signal-hub, pipeline.yml), da Price-Action-Hub dort als Schritt direkt nach Signal-Hub mitläuft. Signal Hub versucht bei erreichbarem Mac mini weiterhin zuerst den lokalen, sofortigen Lauf; erst wenn das fehlschlägt oder die Seite über *.pages.dev läuft, kommt der Cloud-Trigger.

Einmalige Einrichtung pro Gerät: eigener, separater Token nötig (nicht der gh_gist_token — der hat nur Gist-Rechte). Unter github.com/settings/personal-access-tokens/new ein fine-grained Personal Access Token erstellen: Resource owner Miksch52, „Only select repositories" → signal-hub, minervini-markets-360, lokaler-trend-screener auswählen (nicht mehr maick-trading-system — dessen Deploy läuft seit dem Repo-Split server-seitig, kein Browser-Klick nötig), unter Permissions → Repository permissions → „Actions": Read and write setzen (sonst nichts). Beim ersten Klick auf einen der Buttons fragt die App danach und merkt sich ihn in localStorage (Schlüssel gh_actions_token) — pro Gerät einmal einfügen, genau wie beim Gist-Token. Bei 401/403 (Token abgelaufen/falscher Scope) sowie 404 (Fix 2026-08-14 — GitHub meldet fehlende Repo-Berechtigung eines Fine-grained-PATs bei workflow_dispatch bewusst als 404 statt 403, siehe Abschnitt 7) wird der Token automatisch gelöscht und beim nächsten Klick neu abgefragt.

iPhone/iPad als Offline-App (PWA) einrichten

Damit eine App auch bei ausgeschaltetem Mini startet, braucht es einen Service Worker — und der läuft nur über HTTPS. Deshalb dafür die https://…ts.net/-Adresse verwenden. Die http://…-Adressen funktionieren weiter, aber nur solange der Server erreichbar ist.

  1. Die https://…ts.net/-Adresse in Safari öffnen (Mini an, Tailscale aktiv)
  2. Teilen-Symbol → „Zum Home-Bildschirm"
  3. Beim ersten Start cacht der Service Worker die App-Dateien auf dem Gerät
  4. Danach startet die App auch unterwegs ohne Mini: Kurse kommen direkt von Yahoo (CORS-Proxy), das Portfolio aus dem localStorage
Tailscale auf dem iPhone aktiv lassen. Die *.ts.net-Adresse löst nur auf, wenn der Tailscale-Client läuft — sonst startet die installierte App unterwegs nicht. Tailscale ist für genau diesen Dauerbetrieb gemacht.

Mac: „Zum Dock hinzufügen" — eine App pro Fenster

Safari kann die Startseite als eigenständige Mac-App ablegen (Teilen → „Zum Dock hinzufügen"). Zwei Eigenheiten sind dabei wichtig:

  • Eigener Speicher. Die Dock-App teilt localStorage nicht mit Safari — nur Cookies werden beim Anlegen einmal kopiert. Der Gist-Token muss dort also separat eingetragen werden (gleiche Origin-Regel wie oben). Vorteil derselben Trennung: Safaris 7-Tage-Aufräumen (ITP) fasst diesen Speicher nicht an, der Token bleibt dort dauerhaft liegen.
  • Nur ein Fenster. Das Manifest läuft mit display:standalone und scope:"/" — jede geöffnete App ersetzt damit die vorige im selben Fenster. Einen Tab-Modus für Web-Apps gibt es im Manifest zwar (display:tabbed), das ist aber ein WICG-Entwurf und in Safari nicht verfügbar; über das Manifest ist das nicht lösbar.
Fix 2026-09-21: Am Mac öffnet jede App ein eigenes Fenster. Ein Klick-Handler am Ende von index.html fängt Links auf interne App-Seiten ab und öffnet sie per window.open(…, '_blank'). In der Dock-App landen sie dadurch im Standard-Browser (echte Tabs), in Safari selbst in einem neuen Tab — in beiden Fällen lassen sich mehrere Apps gleichzeitig offen halten. Bewusst ohne Abfrage auf „läuft installiert": macOS-Web-Apps melden matchMedia('(display-mode: standalone)') falsch (WebKit-Bug 257806 — Dock-Apps halten sich für einen normalen Tab), die Erkennung wäre also unzuverlässig. Unangetastet bleiben Anker auf der Seite selbst, fremde Domains, mailto:, Downloads, bereits gesetzte target="_blank" sowie Cmd-/Mittelklick (das macht der Browser schon richtig). Auf iPhone/iPad absichtlich unverändert — dort wäre ein ständiger Wechsel nach Safari unbequemer als die Navigation im selben Fenster.
Safari statt Dock-App ist gleichwertig. Die Dock-App ist kein Muss: https://mts-hub.pages.dev/ direkt in Safari öffnen bietet alle Funktionen inklusive Depot-/Kursaktualisierung, und jede App geht in einem eigenen Tab auf. Einzige Einrichtung: der Gist-Token muss in Safari eingetragen sein (☁-Panel der Hauptapp), da Safari und Dock-App getrennten Speicher haben — danach holt Safari den aktuellen Depotstand automatisch aus dem Gist. Safaris 7-Tage-Aufräumen (ITP) kann den Token löschen, wenn die Seite eine Woche lang nicht geöffnet wird; dann einfach neu eintragen, die Daten selbst liegen sicher im Gist.
Wichtig: Auf iPhone/iPad nie die HTML-Datei aus der Dateien-App öffnen — dort läuft kein JavaScript, nichts funktioniert. Immer eine http(s)://…-Adresse verwenden.

03 Die Apps

Kurzüberblick — alle zehn Apps in der Reihenfolge, in der sie auf der Startseite erscheinen. Details siehe jeweilige Karte unten.

AppKurzbeschreibungPort / Zugriff
🎯 HauptappPortfolio, Risiko, Trade-Planer, FIRE-Kalkulator8090 (statisch)
🧠 Minervini Coach 2Lernfortschritt, Quiz, Academy, Mental8090 (statisch)
📊 Minervini Markets 360Marktampel, Technik, Breite, Sentiment, Makro8080
🛰️ Daily Signal HubTägliche Score-Ideenliste aus 5 Quellen + Push8091 (via 8090-Proxy)
📐 Price-Action-HubCandlestick-Muster auf denselben Signal-Hub-Tickern8090 (statisch)
🔄 Rotation-DashboardSektor-/Themen-Rotation mit IBD-Relative-Strength8090 (statisch)
📊 Backtest-VergleichFilterbarer Forward-Test-Vergleich über alle fünf Backtest-Engines8090 (statisch)
🔍 Markets360 ScreenerVCP/RS-Rating/Fab-5-Vollscan (Streamlit)8501
📈 Lokaler Trend-ScreenerRS-Ranking/VCP/Code-33-Fundamentalfilter8765 (via 8090-Proxy)
🤖 TraderFox KI-AgentenKuratierte Links zu 7 externen TraderFox-Research-Agenten8090 (statisch)
📡 13F-RadarWas die großen Adressen laut SEC-13F wirklich zugekauft haben — quartalsweise8090 (statisch)

🏠 Startseite / Hub (index.html)

Verteiler zu allen Apps mit Live-Status-Badges. Jede Karte prüft beim Laden, ob der zugehörige Server läuft („✓ Live-Daten") und schaltet dann automatisch auf die Live-Version um; ohne Server bleibt die statische iCloud-Version verlinkt (Badge „iCloud-Version").

📐 Responsives Karten-Raster statt fest 640 px (seit 2026-08-23, Systemprüfung). .container war auf feste 640 px Breite gedeckelt, .app-grid hatte gar keine grid-template-columns-Regel — bei 1.280 px Fensterbreite blieben dadurch 50 % der Fläche ungenutzt, während alle Karten (Trading Apps/Externe Quellen/Dokumentation/Persönliches, alle vier Abschnitte teilen sich .app-grid) bei jeder Breite einspaltig untereinander liefen — Signal-Hub und Price-Action-Hub nutzten die Breite längst über ein mehrspaltiges Grid. Jetzt responsiv in zwei gekoppelten Haltepunkten (Container und Grid-Spalten immer im selben Schritt): mobil einspaltig (Standard, Container 640 px), ab 768 px zweispaltig (Container 800 px), ab 1.100 px dreispaltig (Container 1.180 px). Hero, KPI-Kacheln und „Setups heute" bleiben in der Breite unverändert mitskaliert (kein eigener Deckel), .app-descs bestehende Ellipsis-Kürzung (white-space:nowrap;text-overflow:ellipsis) greift bei drei schmaleren Spalten etwas früher als vorher — bewusst hingenommen, der volle Kartenname bleibt immer lesbar.

🚦 Einheitliche Marktampel: Startseite, Signal-Hub, Markets 360 und die Hauptapp (Markt-Chip, Trade-Gate, Exposure-Deckel) zeigen exakt dieselbe volle Minervini-Marktampel — eine geteilte Datei assets/marktampel.js (1:1-Portierung von minervini360.scoring.market: Index-Trend, Distribution Days, VIX, Small Caps, 52-W-Abstand, Follow-Through-Day, 10-Jahres-Rendite, Sentiment-Dämpfung), die alle vier Apps live über den Cloudflare-Worker rechnen (^GSPC/^IXIC/^VIX/^RUT/^TNX) — funktioniert ohne Mac mini. Der Signal-Hub-Chip zeigt die volle Ampel nur für US, EU bleibt der einfache Index-Check. Wird der Signal-Hub per zumLiveServer() auf den 8091-Server umgeleitet, liefert dieser /assets/ mit aus (eigene Route in Signal-Hub/src/server.py). 🌙 10-Monats-EMA (seit 2026-08-29): Neben den vier SMA-Kriterien prüft jeder Leitindex zusätzlich, ob er über seinem 10-Perioden-EMA auf Monatsbasis liegt — Minervinis Marktfilter, der bis dahin als einziges konkret benanntes Kriterium seiner Methodik im ganzen System fehlte. Er wirkt nur als Bremse: Ein sonst grüner Index wird auf gelb herabgestuft, wenn er unter dem Monats-EMA notiert; er kann niemals ein Grün erzeugen und niemals Rot auslösen. Die Prozentzahl der Ampel bleibt unberührt (sie zählt weiterhin die vier SMA-Kriterien), damit alte und neue Werte vergleichbar bleiben. Umgesetzt an drei Stellen gleichzeitig, die bewusst 1:1-Portierungen voneinander sind: Signal-Hub/src/scorer.py, assets/marktampel.js und minervini360.scoring.market — tests/monats-ema-parity.js prüft bei jedem Lauf, dass alle drei bitgenau dasselbe rechnen. Da alle bestehenden Index-Abrufe nur 1–2 Jahre Tagesdaten holen (zu wenig für 10 Monatskerzen plus Glättung), holt jede Fassung die Monatskerzen separat (interval=1mo&range=10y, ~120 Werte, eigener Cache).

Zwei Datenfallen, die dabei behoben wurden. (1) Yahoo hängt bei Monatsabrufen die Live-Notierung als zusätzliche Zeile an — der laufende Monat steht doppelt im Array und würde den EMA verzerren; alle drei Fassungen fassen deshalb je Kalendermonat zusammen. (2) Die Monatskerzen sind auf den Monatsersten in Börsenzeit datiert. Als UTC gelesen rutscht die August-Kerze des ^STOXX auf den 31. Juli und damit in den Vormonat — die Europa-Ampel hätte im Cloud-Lauf (GitHub-Runner läuft auf UTC) anders gerechnet als auf dem Mac mini (Europe/Berlin), ohne Fehlermeldung. Alle drei Fassungen verschieben den Zeitstempel deshalb um den mitgelieferten gmtoffset und werten fest in UTC aus; gegengeprüft unter vier Zeitzonen mit identischem Ergebnis.

In Markets 360 zeigt ein „Schnell-Check" unter der Haupt-Ampel zusätzlich die strengere Positionsgrößen-Ampel (grün nur bei Kurs > MA50, dieselbe Logik wie die Startseiten-Exposure) mit den konkreten S&P-Werten und einer Erklärung, wo und warum sie von der vollen Ampel abweicht.

Bei Änderungen an der Python-Engine muss assets/marktampel.js manuell nachgezogen werden (wie schon die frühere Dashboard-JS-Portierung).

▸ Verlauf & Begründung (3 Einträge)

Vorher (bis 2026-07-25): Startseite/Signal-Hub nutzten einen simplen MA50/MA200-Check, die Hauptapp einen eigenen, strengeren S&P/VIX-Check ohne Cloud-Fallback (nur bei erreichbarem 8080-Server); Markets 360 rechnete zwar schon die volle Ampel — je nach App zeigten dieselben Daten dadurch unterschiedliche Farben. Jetzt ist die Farbe überall gleich; die konservative MA50-Vorsicht steckt nur noch in der Exposure-Bandbreite (grün, aber Kurs unter MA50 → „~75 %" statt „100 %").

Fix 08/2026 — Markets 360 bindet assets/marktampel.js jetzt ebenfalls ein: Bis dahin hatte Minervini Dashboard.html eine eigene, komplett duplizierte Kopie der Fallback-Berechnung fest einprogrammiert (griff, wenn /marktampel vom 8080-Server nicht erreichbar ist, z. B. auf Cloudflare Pages) — seit der Follow-Through-Day/10-Jahres-Rendite-Erweiterung nicht nachgezogen, zeigte auf Pages deshalb still weniger Faktoren als das Server-Backend und keine „Tendenz Richtung X"-Erkennung. Minervini/server.py liefert jetzt ebenfalls /assets/ aus dem Projekt-Root aus, der Fallback ruft nur noch MTSAmpel.berechne() statt der eigenen Kopie.

Der zuvor separate Button „Ampel neu laden" wurde entfernt — redundant zu „Live Daten abrufen" (aktualisiert die Ampel ohnehin mit) und ohne sichtbares Feedback bei Klick, der 5-Minuten-Cache im 8080-Server ließ ihn oft wirkungslos wirken.

⚠️ Tendenz Richtung Gelb/Grün/Rot (MTSAmpel.tendenz() in assets/marktampel.js, seit 08/2026 auf allen vier Grenzen): Frühwarnung auf der Startseite (Hero-Box unter der Ampel + „Heute wichtig"-Text) und seit 08/2026 auch in Markets 360 (Box unter der Score-Leiste, #ampelTendenz) — erscheint nur, wenn die Ampel wirklich auf der Kippe steht, nicht bei jedem einzelnen Warnfaktor (sonst wäre der Hinweis dauerhaft aktiv und würde seinen Frühwarnwert verlieren). Deckt alle vier Übergänge ab, jeder über die Größe, die den jeweiligen Kipppunkt tatsächlich bestimmt:

  • Grün → Gelb: Ampel steht auf Grün, aber es fehlt nur noch ein einziger roter Sentiment-Faktor bis zur Dämpfungsschwelle (aktuell 3) — „Tendenz Richtung Gelb – N/3 Warnfaktoren aktiv" mit den konkreten Faktornamen.
  • Gelb → Grün: Ampel ist nur wegen der Sentiment-Dämpfung gelb (der Leitindex-Trend selbst wäre grün) und genau ein roter Faktor müsste sich erholen, um die Dämpfungsschwelle zu unterschreiten.
  • Gelb → Rot: einer der beiden Leitindizes (S&P 500/Nasdaq) ist bereits im Abwärtstrend (Index-Kriterien-Status „Rot"), der andere nur neutral statt bullisch — der exakte Kipppunkt der Trend-Kombinationslogik.
  • Rot → Gelb: beide Leitindizes im Abwärtstrend, aber mindestens einer erfüllt trotzdem schon wieder 2 von 4 Einzelkriterien — näher an der Erholung als ein Markt, in dem beide Indizes vollständig gebrochen sind.

Dieselbe Funktion ist über window.MTSAmpel für alle Apps nutzbar, aktuell verdrahtet auf Startseite und Markets 360.

Exposure-Empfehlung (Hero): Die Ampel oben zeigt zusätzlich zur Stufe (🟢 Aggressiv / 🟡 Neutral / 🔴 Defensiv) die empfohlene Aktienquote als konkreten Handlungswert: bis 100 % (Aggressiv, Kurs > MA50 > MA200 & MA50 steigt), bis ~75 % (Aggressiv, aber MA50 flach/fällt — Aufwärtstrend noch fragil), max. ~50 % (Neutral, Kurs über MA200 aber unter MA50), 0–25 % (Defensiv, Kurs unter MA200). Das ist die schnelle Cockpit-Sicht (ein Blick genügt); die harte Kappung der Positionsgröße macht weiterhin der „📊 Exposure-Deckel nach Marktampel" in der Hauptapp (siehe dort) — die Prozentbänder sind bewusst konsistent gehalten.

🧩 Setups heute (Panel unter „Heute wichtig"): zeigt die Schnittmenge aus Score ≥ 70 (Signal-Hub), ARMED/BREAKOUT (Pivot-Detektor) und Price-Action bullisch — automatisch aus einer kleinen Zusammenfassungsdatei gebaut, kein Cross-App-Import. Grünes Marktregime verlinkt aktive Setups direkt in den Trade-Planner, gelb/rot bzw. Earnings-Sperre führt stattdessen zur SEPA-Analyse. Jede Zeile zeigt ein 📊-Badge mit der echten, unverzerrten Trefferquote (inkl. Konfidenzintervall) statt einer Auszeichnung ohne Datengrundlage — ⭐ Kern-Setup gibt es erst ab 100 gereiften Fällen mit belegter Untergrenze über 50 %. Aktuell erreicht keine der beiden Pivot-Kohorten diese Schwelle: ARMED 48,8 % (n=1.005), BREAKOUT 35,2 % (n=108) — ARMED bleibt in der Sortierung vorn, aber ohne belegten Vorteil. Jede Zeile öffnet zusätzlich per 🔍 die Setup-Analyse-Detailseite (alle Score-Faktoren, Pivot-Kennzahlen, Price-Action-Muster, TradingView-Chart) sowie eine live nachgeladene, rein informative KI-Fundamentalanalyse (Cloudflare Workers AI).

📋 Liste / 📈 Chart-Umschalter (seit 2026-09-06, oben rechts im Panel): schaltet die Textliste auf ein Kachel-Raster um, das jedes angezeigte Setup als kleine TradingView-Live-Kachel zeigt — echte Kerzen, GD50/150/200 (MASimple@tv-basicstudies, dieselben Studies wie die SEPA-Analyse in der Hauptapp) und Volumen, Toolbars/Legende ausgeblendet. Gedacht für den schnellen Blick über alle Setups, um zu erkennen, welcher Wert eine nähere Analyse verdient, statt jede Zeile einzeln über 🔍 aufzuklappen. tv.js (~150 KB) wird bewusst erst beim ersten Umschalten auf „Chart" nachgeladen, nicht beim Seitenaufruf — wer nur die Liste nutzt, lädt es nie mit (Geräteunabhängigkeit/Handy-Datenvolumen). Die gewählte Ansicht merkt sich localStorage geräteweise. Kacheln in der eingeklappten „N weitere anzeigen"-Sektion mounten erst beim Aufklappen (TradingViews autosize kann in einem display:none-Element nicht sinnvoll rechnen). Lade-Gate: die eigentlichen TradingView-Kacheln mounten nicht automatisch beim Umschalten, sondern erst nach einem expliziten Antippen von „📈 N Charts laden" (mit Datenvolumen-Hinweis) — ohne dieses Gate würden bei gespeicherter Chart-Ansicht schon beim bloßen Öffnen der Startseite bis zu 20 Live-Iframes ungefragt Mobilfunk-Daten ziehen. Die Bestätigung wird ebenfalls geräteweise in localStorage gemerkt (mts_topsetups_charts_ok), danach lädt die Chart-Ansicht bei künftigen Besuchen direkt.

DatumÄnderungGrund
07-24Panel „Top-Setups heute" eingeführt (Schnittmenge Score/Pivot/Price-Action)ersetzt manuelles Kreuzen über drei Dashboards
08-02Setup-Analyse-Detailseite + Fundamentalanalyse/Story per KI ergänztsofortiger Ticker-Überblick statt Sprung durch mehrere Apps; TradingView-Chart ersetzt den anfänglichen SVG-Chart
08-09Push „X neue Setups" ergänzteigener ntfy-Push bei jedem Pipeline-Lauf, nicht an feste Slots gekoppelt
08-17Anzeige-Limit auf 20 erhöht (vorher 5), Konfluenz-/Kern-Setup-Badges eingeführtAufklappliste sollte an treffer-reichen Tagen mehr zeigen; „mehrere unabhängige Bestätigungen schlagen ein Einzelsignal"
08-20Konfluenz-Score-Zählweise korrigiert (nur noch 2 wirklich unterscheidende Kriterien)vorher landete fast jedes Setup bei „3/5" und täuschte Differenzierung vor, die es kaum gab
08-23Panel umbenannt „Setups" (vorher „Top-Setups"); Beleg-Regel verschärft: größter Datentopf statt längster Horizont (KERN_REIFE_N 8→100 + Wilson-Untergrenze > 50 %), echte Trefferquote statt Auszeichnung, 🔥-Leader an eigenen Forward-Test gekoppeltdie alte Regel zeichnete bei n=8 faktisch Rauschen aus (95-%-KI 29–85 %); Leader hatte bei n=0 trotzdem voll in den Score gezählt
09-06Liste/Chart-Umschalter ergänzt: TradingView-Live-Mini-Charts (Kerzen + GD50/150/200 + Volumen) je Setup, mit Lade-Gate vor dem tatsächlichen Mountschnelles visuelles Sichten aller Setups statt Zeile für Zeile per 🔍 nachzuschlagen; Gate verhindert ungefragtes Mobilfunk-Datenvolumen bei gespeicherter Chart-Ansicht

🗒️ Watchlist (kleiner Button neben dem Panel-Titel, seit 16.08.2026): exportiert die aktuell angezeigten Top-Setups (der Zweig, der auch in der Liste zu sehen ist — aktive Setups, oder falls keine im grünen Regime die wartenden Kandidaten) nach Watchlisten/Hauptapp/ im MTS-Hauptordner, gleiches Dateiformat wie bei Signal-Hub/Price-Action-Hub/Trend-Screener (TradingView-.txt, lesbare Zusammenfassung, reine Tickerliste). Nutzt denselben neuen Endpunkt POST /api/watchlist in server_mts.py, den auch der Price-Action-Hub verwendet (kein eigener Port für die Startseite) — funktioniert nur mit erreichbarem lokalem Server, nicht auf der reinen Pages-Version.

📰 Katalysator-Layer (seit 2026-08-28, Price-Action-Hub/src/katalysator.py): keine der drei Screening-Engines sagt bislang, warum ein Wert gerade technisch reif ist — der Score bewertet nur die Chartstruktur, nie den fundamentalen Anlass dahinter (Minervini/O'Neil verlangen beides zusammen). Für jedes aktuelle Top-Setup fragt ein neuer, täglich höchstens einmal laufender Pipeline-Schritt die Perplexity-Sonar-API (Modell sonar, JSON-Schema-Antwort, search_recency_filter: week, auf seriöse Finanzquellen begrenzt) nach einem konkreten Anlass der letzten 7 Tage — Quartalszahlen, angehobene/gesenkte Guidance, Großauftrag, Zulassung, Analysten-Up-/Downgrade, Indexaufnahme oder Übernahme. Ergebnis landet als eigene, winzige Datei (Signal-Hub/data/top_setups_katalysator.json/.js) und zeigt sich als Badge 📰 Guidance (o. ä.) in der Zeile — nur, wenn tatsächlich ein Anlass gefunden wurde, sonst kein Badge (kein „0 Katalysatoren"-Rauschen). Tooltip zeigt den Zwei-Satz-Text mit Quellenangabe.

Bewusst nicht sofort im Score — die CLAUDE.md-Backtest-Pflicht gilt auch hier: katalysator_backtest.py sammelt zunächst nur eine unverzerrte Forward-Test-Kohorte „mit Katalysator" vs. „ohne Katalysator" (dieselben 4W/8W/12W-Horizonte wie die übrigen Backtests). Erst wenn diese Kohorte einen echten Vorteil zeigt, wird daraus ein siebter Score-Faktor. Kostentreiber ist die Request-Gebühr, nicht die Token — deshalb das Ein-Lauf-pro-Tag-Gate (katalysator_state.json, R2-gesichert wie die übrigen Zustandsdateien) statt eines Aufrufs bei jedem der vier täglichen Slots. Braucht ein einmalig vom Nutzer zu setzendes GitHub-Repo-Secret PERPLEXITY_API_KEY im signal-hub-Repo (analog DEPLOY_TRIGGER_TOKEN) — ohne Secret überspringt der Schritt sich bewusst still, keine Pipeline-Blockade. Der zurückkommende Text ist Fremdinhalt aus dem Netz (längenbegrenzt, nach fester Klassenliste geprüft) und wird im Frontend ausschließlich HTML-escaped angezeigt, nie als Anweisung interpretiert.

🥇 Code 33 + zusammengeführte Fundamentaldaten (seit 2026-08-09): die vorher getrennten Abschnitte „Fundamentale Treiber" und „Fundamentalanalyse & Story" stehen jetzt unter einer gemeinsamen Überschrift „💵 Fundamentaldaten & Story" direkt hintereinander statt an zwei Stellen der Seite — alle Fundamentaldaten zu einem Ticker an einem Ort. Neu darin: eine Ampelkarte „Code 33" nach Mark Minervini (Quelle: aktienmagazin.de „Die Strategie zur ersten Million") — grün bei allen drei, gelb bei 1–2, rot bei keinem erfüllten Kriterium:

  • EPS-Wachstum Quartal ggü. Vorjahr > 40 %
  • Umsatzwachstum Quartal ggü. Vorjahr > 7 %
  • Nettogewinnmarge um mehr als 2 Prozentpunkte verbessert (2–3 Quartale zurückgerechnet, erfüllt wenn einer der beiden Vergleichszeiträume die Schwelle reißt). Ausnahme einmaliger Sonderverlust: Ein Vergleichsquartal mit negativer Marge zählt nicht, wenn die Reihe davor schon ein profitables Quartal hatte — gegen ein einzelnes Verlustquartal sieht jede normale Marge wie eine Verbesserung aus. Turnarounds aus durchgehenden Verlusten zählen weiter.
Datenqualitäts-Fix 2026-09-24 (Befund HALO): HALO hatte im Quartal 31.12.2025 einen einmaligen Sonderverlust (Marge −31,3 %, davor 49,5 %). Gegen dieses Quartal ergab die aktuelle Marge von 47,8 % ein Plus von 79 Pp, gegen das Quartal davor aber −1,7 Pp — Code 33 zeigte 3/3 statt 2/3. Messung am Stand 24.09.: Von 173 Treffern mit erfülltem Margenkriterium hingen 49 nur an einem Verlustquartal, davon 17 an einem einmaligen Ausreißer. Diese 17 verlieren den Margenpunkt (Code-33-Gewicht 6 → etwa 2 Score-Punkte), 3/3 wird nur bei HALO, ARX und LFUS zu 2/3. Schwelle, Vergleichsquartale, Gewicht, EPS- und Umsatzkriterium sind unverändert; der statistik-skeptiker stufte das als Korrektur der Messung ein, nicht als Regeländerung (Code 33 hat noch keine gereifte Forward-Episode). Die Setup-Analyse nennt das ausgeschlossene Quartal in der Code-33-Karte (Feld marge_basis_ausgeschlossen). Regel identisch in scorer.py::yahoo_code33() und mts-cors-proxy.js::fetchCode33(), Test tests/test_code33_marge.py.

Bewusst nicht mitgerechnet: die in derselben Quelle als „optional" markierte „Stabilität Nettogewinnmarge" — eine proprietäre TraderFox-Kennzahl ohne öffentlich dokumentierte Formel, die zudem 3 Jahre Quartalshistorie bräuchte.

Berechnet wird das serverseitig im mts-cors-Worker (fetchCode33()) über Yahoos fundamentals-timeseries-Endpunkt statt des einfachen quoteSummary-Moduls „earnings" — Letzteres liefert nur 4 Quartale ohne Vorjahresbasis für einen echten YoY-Vergleich, während fundamentals-timeseries trotz angefragtem 3-Jahres-Fenster in der Praxis nur ca. 5 Quartale zurückgibt (gegen mehrere Ticker verifiziert), aber genau das für einen einzelnen Vorjahresvergleich reicht. Die drei Kennzahlen-Reihen sind dabei nicht zwingend gleich datiert — deshalb überall über das Quartalsdatum statt über die Arrayposition verknüpft. Kommt als Feld fundamentals.code33 mit der ohnehin schon vorhandenen /story-Antwort (parallel per Promise.allSettled, kein zusätzlicher Request-Roundtrip). Kein passendes Vorjahresquartal bei Yahoo gefunden (z. B. junge Börsengänge, manche Nicht-US-Werte) zeigt einen Hinweis statt einer falschen Ampel. Seit 2026-09-30 gibt es zusätzlich den schlanken Endpunkt /fundamentals?ticker=… (gleiche Yahoo-Abfrage und Code-33-Berechnung, aber ohne KI-Text und News, Antwort in unter 1 s, 20-Minuten-Edge-Cache) — ihn nutzt die SEPA-Analyse der Hauptapp, damit dort dieselben Zahlen stehen wie hier.

🎨 Neues Design „Tagesordnung" (seit 2026-08-09, setup-detail.html): die Setup-Analyse hatte bis dahin eine dunkle Glas-Optik (Navy-Hintergrund, transluzente Karten). Umgestellt auf eine helle Papier-Basis (#F6F3EC) mit weichen, schattierten Karten (weiß, dezenter Schlagschatten statt Rahmen-Glas) und Teal (#1F6E66) als einzigem Marken-Akzent statt der vorherigen Türkis/Gold-Mischung. Serif-Schrift (Iowan Old Style/Palatino) ist bewusst auf eine einzige Stelle beschränkt — das KI-Story-Zitat in Fundamentaldaten & Story erscheint jetzt kursiv mit Teal-Randlinie als eigene .story-quote, der Rest der Seite bleibt konsequent Sans/Mono, damit dieser Akzent nicht verwässert. Neue Eyebrow-Zeile („SETUP-ANALYSE · Börse") über dem Ticker im Hero. Das TradingView-Chart-Widget läuft jetzt mit theme:'light' statt 'dark', passend zum Rest der Seite. Ampel-/Badge-Farben (Tier, Pivot-Status, Regime, Price-Action, Marktstadium) wurden komplett auf helle, semantisch gleichbleibende Varianten umgestellt (kräftige Volltöne auf Dunkel → dezente Farbtöne auf Hell, gleiche Zuordnung Grün/Gelb/Rot/Teal). War eines von vier vorgeschlagenen Design-Konzepten (Kontrollraum, Analystennotiz, Nachtsitzung, Tagesordnung) — der Nutzer wählte „Tagesordnung" aus.

🔀 Ticker wechseln, ohne die Seite zu verlassen (seit 2026-08-16): neues Eingabefeld im Header („Ticker wechseln …") lädt bei Enter denselben Datensatz (signals.json/pivot.json/priceaction.json) für einen anderen Ticker neu ein und baut die komplette Ansicht (inkl. Story/Code 33/Chart) neu auf, statt die Seite zu verlassen. Die drei bis zu 10 MB großen Dateien werden dafür nur einmal geladen und für jeden weiteren Wechsel im Speicher neu gejoint. Die URL wird per history.pushState mitgeführt (Zurück-Button/Teilen-Link bleiben korrekt) — ein wirklich ungültiger Ticker zeigt einen „Kein Treffer"-Hinweis wie beim direkten Aufruf, ein gültiger, aber (noch) nicht gescannter Ticker seit 2026-09-10 stattdessen die 🔎 Direktanalyse (siehe unten). Grund für das Eingabefeld selbst: der Lokale Trend-Screener verlinkt seit demselben Tag ebenfalls hierher (siehe dortiger Abschnitt) — bei einem breiteren, teils abweichenden Ticker-Universum ist ein schneller Wechsel zwischen mehreren Kandidaten in derselben Ansicht deutlich praktischer als Tab für Tab neu zu öffnen.

🔎 Direktanalyse für Ticker ohne Signal-Hub-Treffer (seit 2026-09-10): bis dahin endete jeder Ticker außerhalb des Scan-Universums in „Kein Treffer" — eine Sackgasse, obwohl fast der ganze Rest der Seite (Chart, Fundamentaldaten, Code 33, Quartalszahlen, Dividendenkontinuität, Innerer Wert, KI-Vertiefung) ohnehin schon live pro Ticker läuft, unabhängig von signals.json. Findet zeigeTicker() keinen Signal-Hub-Treffer, holt sie jetzt zusätzlich einen echten Live-Kurs direkt von Yahoo (ladeDirektkurs(), v8/finance/chart — erst direkter Browser-Fetch, sonst über den ohnehin vorhandenen generischen ?url=-Proxy des Workers als CORS-Fallback) und rendert eine reduzierte Ansicht (renderDirekt()) statt der bisherigen Sackgasse. Markt (USA/Europa) wird exakt wie scorer.py::markt_von_meta aus Börsenkürzel/Währung bestimmt, damit z. B. das US-Gate von Business Quant identisch bleibt.

Bewusst nicht nachgebaut: Score/Tier, Pivot-Armed-Status, Price-Action-Muster, das Momentum-Dashboard und die 🎓 Minervini-Analyse (fünf Regel-Gates) — die brauchen relative Kennzahlen (RS-Rating, Branchenrang) aus dem Vergleich gegen das ganze gescannte Universum und lassen sich für einen Einzeltitel nicht nachbauen, ohne den kompletten Scan client-seitig zu duplizieren (widerspräche „Entflechtung", siehe CLAUDE.md). Eine eigene Hinweisbox auf der Seite erklärt das direkt vor Ort, damit klar bleibt: fehlende Abschnitte sind eine Systemgrenze, keine Bewertung der Aktie. Nebeneffekt: funktioniert auch, wenn signals.json gerade nicht erreichbar ist — der Live-Yahoo-Abruf ist davon unabhängig.

🟢 Erfüllt/nicht-erfüllt + aufklappbare Themenblöcke + Fundamentalanalyse (seit 2026-08-16): drei zusammenhängende Überarbeitungen auf Nutzerwunsch:

  • Visuelle Hervorhebung: jede Kriterien-Karte und jede Quickscan-Kachel bekommt zusätzlich zum Ampel-Punkt eine eigene Rand-/Hintergrundfarbe je Ampelstand — grün erfüllte Kriterien stechen mit Teal-Tint hervor, rote/nicht verfügbare sind bewusst blasser, ohne unlesbar zu werden.
  • Sortierung + Zusammenfassung: die Kriterien-Karten sortieren sich nach Ampelstand (grün → gelb → rot/n. v.) statt fester Faktoren-Reihenfolge. Seit 2026-09-28 stehen Sofort-Überblick, Technik- und Fundamental-Karten gemeinsam im Block „📋 Alle Kriterien", dessen Kopf „X/Y grün" zeigt (Y variiert je Ticker zwischen 11 und 14).
  • Aufklappbare Themenblöcke: Bewusst ein eigenes, schlankes Akkordeon-CSS-Muster statt Wiederverwendung des wiki.html-Accordions (drei getrennte Anleitungsseiten-Design-Systeme, siehe CLAUDE.md — passt nicht zur hellen „Tagesordnung"-Optik dieser Seite). Seit 2026-09-28 (Nutzerwunsch: zu viel Scrollen am iPhone) ist unter den 🔑 Key Points alles zugeklappt: „📊 Momentum · Geldfluss · Substanz", „🎓 Minervini-Analyse", „📋 Alle Kriterien" und „💵 Fundamentalanalyse". Einzige Ausnahme ist die „🕯️ Charttechnik", die auf früheren ausdrücklichen Nutzerwunsch offen bleibt; der Chart ist am iPhone dafür 360 statt 480 px hoch.

Neu: Fundamentalanalyse nach Dividendenkontinuität + Buffett/Munger/Graham — zwei zusätzliche Kartenreihen, beide rein informativ, kein Score-Bestandteil (dieses System ist sonst konsequent Wachstums-/Momentum-orientiert nach Minervini/CANSLIM, hier bewusst eine wertorientierte Zweitmeinung): „💰 Dividendenkontinuität" berechnet aus der Yahoo-Dividendenhistorie der letzten 10 Jahre (bestehender ?url=-Proxy im mts-cors-Worker, kein neuer Endpunkt) die Zahl aufeinanderfolgender Jahre mit Zahlung sowie den Trend der letzten abgeschlossenen Jahre (das laufende Jahr fließt bewusst nicht ein, sonst sähe eine unvollständige Jahressumme fälschlich wie eine Kürzung aus). „🏛️ Buffett/Munger/Graham-Kennzahlen" zeigt Eigenkapitalrendite, Nettomarge, Verschuldungsgrad, Liquidität, Kurs-Buchwert-Verhältnis, Dividendenrendite und Ausschüttungsquote — alle aus denselben quoteSummary-Modulen, die der /story-Endpunkt ohnehin schon abruft (kein zusätzlicher Yahoo-Aufruf), mit festen, dokumentierten Heuristik-Schwellen je Kennzahl (z. B. ROE ≥ 15 % grün) statt einer weiteren Scorekomponente.

Fix 2026-08-17: „Sofort-Überblick"-Zähler stimmte nicht mit den sichtbaren Kacheln überein. Die Kopfzeile nutzte für Zähler und Nenner bisher ausschließlich alleKeys (die 10 TECH_KEYS + 1 FUND_KEYS, macht 11) — die Kachel-Liste selbst hängt aber, falls vorhanden, bis zu drei weitere Kriterien dynamisch an (Pivot-Status, Price-Action, Marktstadium), die dabei nie mitgezählt wurden. Sichtbares Symptom (User-Report anhand HUM): „6 von 11" in der Überschrift, obwohl 14 Kacheln sichtbar waren und spürbar mehr als 6 davon grün leuchteten. Fix: qsAmpeln sammelt jetzt parallel zu qsItems die Ampel jedes tatsächlich gerenderten Kachel-Eintrags (inklusive der drei dynamischen); Zähler und Nenner werden erst danach daraus berechnet, statt vorab nur aus alleKeys. Betraf nur die Kopfzeile — die einzelnen Kachel-Farben selbst waren immer korrekt.

🎯 Innerer Wert — Graham Number, Owner-Earnings-DCF, Dividend Discount Model (seit 2026-08-16, Nutzerwunsch „gibt es Möglichkeiten, eine klassische Buffett-Unterbewertung festzustellen"): dritte neue Kartenreihe in der Fundamentalanalyse — drei unabhängige, bewusst einfache Bewertungsheuristiken statt eines einzelnen „richtigen" Werts (Graham selbst empfahl, mehrere Ansätze gegenzurechnen statt einem Modell blind zu vertrauen), ebenfalls rein informativ, kein Score-Bestandteil. Ampel je Karte nach Sicherheitsmarge: grün ab 20 %, gelb 0–20 %, rot wenn der Kurs über dem Schätzwert liegt.

  • 📐 Graham Number: Benjamin Grahams klassische Defensiv-Formel, √(22,5 × EPS trailing × Buchwert je Aktie).
  • 🧮 Owner-Earnings-DCF: vereinfachtes 2-Phasen-DCF nach Buffetts „Owner Earnings" — freier Cashflow je Aktie 5 Jahre mit einer aus Gewinn-/Umsatzwachstum abgeleiteten, auf 0–15 % gedeckelten Rate fortgeschrieben, mit festen 9 % abgezinst, danach Terminalwert mit 2,5 % ewigem Wachstum. Beide Karten brauchen nur drei zusätzliche Felder aus denselben quoteSummary-Modulen, die /story ohnehin schon abruft.
  • 💵 Dividend Discount Model: Gordon-Wachstumsmodell auf Basis der ohnehin geladenen Dividendenhistorie — letzte volle Jahresdividende mit der historischen Wachstumsrate fortgeschrieben, ebenfalls mit 9 % abgezinst; zeigt „nicht anwendbar" statt eines falschen Werts, wenn das Dividendenwachstum den Diskontsatz übersteigt oder die Historie zu dünn ist.
▸ Verlauf & Begründung (1 Eintrag)

Bugfix noch am selben Tag: die erste CAGR-Implementierung nutzte das älteste Jahr im 10-Jahres-Fenster als Startwert — range=10y schneidet aber fast nie exakt am 1. Januar, das älteste Kalenderjahr ist deshalb fast immer unvollständig und verzerrte die Wachstumsrate nach oben (live an JNJ verifiziert: 13,8 % mit verkürztem Startjahr, 5,6 % ohne — Letzteres passt zur bekannten JNJ-Dividendenwachstumsrate). Fix: das älteste Jahr im Fenster wird für die CAGR-Berechnung jetzt verworfen.

🔬 Tiefenanalyse (seit 2026-10-04, aufklappbarer Block unter der Minervini-Analyse bzw. in der Direktanalyse vor dem Chart; ersetzt die vier früheren Knöpfe Erweiterter Bericht, 🧬 Tiefere Analyse, 🏦 Business Quant und ⚡ Katalysator): ein Knopf, ein Bericht. Der Worker-Endpunkt /tiefenanalyse (cloudflare-worker/mts-cors-proxy.js::handleTiefenanalyse) holt alle Quellen einmal parallel — Yahoo-Kennzahlen und -Schlagzeilen, Code-33-Quartalsserie, SEC EDGAR samt Ergebnis-Pressemitteilung (siehe unten), bei US-Werten Business Quant (Institutionelle, Insider, EPS-Schätzungen), den Katalysator aus dem Tageslauf oder, falls dort keiner steht, eine eigene Perplexity-Websuche — und schickt sie zusammen mit den Systemwerten der Seite (Score, Trend-Template, Pivot, Price-Action, Markt-Regime, Innerer Wert mit vorgerechnetem Abstand zum Kurs; sammleKontext()) an gpt-oss-120b (OpenAI, offenes Modell, Cloudflare Workers AI, kostenloses Kontingent). Antwort als festes JSON-Schema: Kurzfazit, Was das Management sagt, Was die Zahlen zeigen, Was leicht übersehen wird, Risiken, Offene Fragen, CAN SLIM je Buchstabe mit Note 1–10 und Begründung; darunter die Business-Quant-Tabellen und alle Quellen als Links. Keine Kaufempfehlung, keine Kursziele, keine Kurszonen — Systemwerte darf die KI einordnen, aber nicht überstimmen. Gespeichert statt neu gerechnet: jeder Bericht liegt in R2 unter signalhub-magazine/tiefenanalyse/<TICKER>/<Tag>.json (bewusst nicht unter _deploy/, also nicht öffentlich auf Pages). Beim Öffnen der Seite lädt der Block still den neuesten gespeicherten Bericht — auf jedem Gerät derselbe Stand; der Blockkopf zeigt sein Datum. „Erstellen“ läuft je Ticker und Tag einmal (rund 20–40 s, etwa 250–400 der 10.000 kostenlosen Neuronen/Tag), „Für heute neu erstellen“ überschreibt den Tagesstand, frühere Tage bleiben als Knöpfe abrufbar. Ist ein Bericht höchstens acht Tage alt, übernimmt die Story-Zeile der Key Points sein Kurzfazit. Ist das kostenlose KI-Tageskontingent aufgebraucht, sagt der Block das statt eines Fehlers.

🗣️ Was das Management sagt (seit 2026-10-04): der Worker holt aus SEC EDGAR die jüngste Ergebnis-Pressemitteilung des Unternehmens — bei US-Meldern die Anlage EX-99.1 der 8-K mit Punkt 2.02, bei ausländischen Meldern die passende 6-K (Anlage EX-99.1 oder, z. B. bei Novo Nordisk, das Hauptdokument selbst). Welche 6-K eine Ergebnismeldung ist, entscheidet der Textanfang (ERGEBNIS_ANFANG, etwa „Quarterly Statement Q2 2026“), damit Hauptversammlungs- oder Produktmeldungen nicht mitzählen; geprüft werden bis zu zwölf Meldungen, je vier gleichzeitig. Aus der Mitteilung geht ein Auszug von höchstens 6.000 Zeichen in den KI-Kontext: Anfang mit den Kernzahlen, wörtliche Zitate des Managements und der Abschnitt zum Ausblick (pressemitteilungAuszug()). Die Tiefenanalyse fasst daraus sinngemäß zusammen, was das Management zu Geschäftsentwicklung, Treibern und Ausblick sagt, nennt das Datum der Mitteilung und verlinkt sie. Ohne Mitteilung steht dort fest „keine offizielle Mitteilung gefunden“ — die KI darf diesen Abschnitt dann nicht füllen, weil kleinere Modelle sonst Aussagen erfanden. Europäer mit US-Listing (SAP, ASML, Novo Nordisk) findet der Worker seit demselben Tag über den Firmennamen, wenn das Heimat-Symbol (SAP.DE) bei der SEC nicht vorkommt (secCikFinden()): nur bei genau einem passenden Unternehmen und nur, wenn es als ausländischer Melder 20-F/40-F/6-K einreicht. Europäer ohne US-Listing (Rheinmetall, Siemens, Nestlé) melden nicht bei der SEC. Für sie durchsucht die Tiefenanalyse seit 2026-10-04 per Perplexity nur die Website des Unternehmens (Adresse aus Yahoos Firmenprofil, search_domain_filter auf diese Domain, Berichte der letzten vier Monate; managementWebsuche()) nach dem jüngsten Quartals-, Halbjahres- oder Jahresbericht. Der Text wird unverändert übernommen, nicht von der KI umformuliert, und als „laut rheinmetall.com, Bericht vom …, Websuche, sinngemäß zusammengefasst“ gekennzeichnet; als Quellen zählen nur Links auf der Unternehmensdomain. Kostet wie der Katalysator rund einen halben Cent und läuft nur, wenn keine SEC-Mitteilung gefunden wurde. Der eigentlich geplante EU-Pflichtweg ESEF (filings.xbrl.org) scheidet aus: Deutschland, die Schweiz, Spanien und Schweden fehlen dort seit 2024 ganz, und ein Jahresbericht ist als Inline-XBRL 40–50 MB groß, zu viel für den Worker.

📅 Mehrjahreszahlen (seit 2026-10-04, Fundamentalanalyse unter den Quartalsergebnissen, Worker-Endpunkt /mehrjahr, kostenlos, 24 h Edge-Cache): Umsatz und verwässerter Gewinn je Aktie je Geschäftsjahr mit Veränderung zum Vorjahr, darüber die jährliche Wachstumsrate des Gewinns je Aktie über drei Jahre und ob er in jedem dieser Jahre gestiegen ist — die Grundlage für CAN SLIM „A“ (O'Neil: rund 25 % pro Jahr). Reine Anzeige, kein Score-Bestandteil. Quellen: für US-Melder und Europäer mit US-Listing die Jahresberichte bei der SEC über companyconcept (je Kennzahl eine kleine Datei statt des mehrere MB großen companyfacts; mehrere Umsatz-Kennzahlen nach US-GAAP und IFRS werden zusammengeführt), bis acht Jahre, bei US-Meldern zusätzlich bis acht Quartale im Vorjahresvergleich (das vierte Quartal steht bei der SEC nur im Jahresbericht). Alle anderen bekommen vier Geschäftsjahre von Yahoo. Split-Bereinigung: ältere Jahre stehen bei der SEC so, wie sie damals gemeldet wurden (Nvidia 2018: 6,63 $ statt 0,17 $ nach den Splits 4:1 und 10:1); der Worker teilt den Gewinn je Aktie deshalb durch alle Splits aus Yahoos Split-Historie, die nach dem Meldedatum lagen. Fehlt die Split-Historie, zeigt er nur die jüngsten drei Jahre. Die Tiefenanalyse bekommt dieselben Zahlen und begründet „A“ damit.

Schlagzeilen und Modell hinter Story und Tiefenanalyse: beide holen ihre Schlagzeilen bei Yahoo Finance (nicht Google News — das sperrt Cloudflare-Worker) über dieselbe Funktion fetchYahooNews() im mts-cors-Worker. Zu Symbolen mit Börsensuffix (SAP.DE, ASML.AS) findet Yahoos Suche nichts; seit 2026-09-27 sucht der Worker dann über den Firmennamen, ohne Rechtsform (SAP SE → „SAP“), und behält nur Titel, die den Namen enthalten, ohne die üblichen Marktstudien-Pressemitteilungen. Bleibt trotzdem nichts übrig, bekommt die KI ausdrücklich „Datenlücke“ statt „keine gefunden“ — vorher schrieb sie daraus „SAP ist derzeit nicht im Fokus“. Die Story läuft auf Llama 3.1 8B, die Tiefenanalyse auf gpt-oss-120b (beide Cloudflare Workers AI, kostenlos). Gewünscht war Kimi K2.6; das und die übrigen großen Modelle (DeepSeek, GLM) stehen im kostenlosen Workers-AI-Plan nicht zur Verfügung, gpt-oss-120b schnitt im Vergleich mit Nemotron besser ab.

DatumÄnderungGrund
08-0812 statt 6 Yahoo-Schlagzeilen als Basis, plus optionale Reddit-Anbindung über offiziellen OAuth-WegPerplexity (seit 02/2026 kostenpflichtig), Reddit-JSON, StockTwits und Google-News-RSS scheiterten alle live am Cloudflare-Worker (403/503)
08-09SEC-EDGAR-Anbindung ergänzt (10-K/10-Q/8-K, nur US-Ticker)direktes IR-Seiten-Scraping scheiterte an denselben Cloudflare-Bot-Sperren; EDGAR ist die einzige kostenlose, einheitliche Alternative
10-04Ergebnis-Pressemitteilung mit Aussagen des Managements statt 1.500 Zeichen 8-K-Deckblatt; Europäer mit US-Listing über 20-F/6-KNutzerwunsch „Aussagen des Managements aus offiziellen Berichten, auch für Europa“; Vorschläge 4 und 5 des Setup-Analyse-Fahrplans

🔍 Unabhängige Gegenprüfung (seit 2026-08-09; Button „Trend-Template frisch nachrechnen" seit 2026-09-28 in der Minervini-Analyse unter den fünf Stufen): rechnet die 8 Minervini-Stage-2-Trend-Template-Kriterien unabhängig neu aus einem frischen Yahoo-Kursabruf im mts-cors-Worker (neuer Endpunkt /verify) und vergleicht das Ergebnis mit dem in signals.json gespeicherten Pipeline-Stand — Prinzip „wer etwas berechnet hat, prüft es nicht selbst nach" (dieselbe Pipeline, die einen Fehler gemacht hat, findet ihn beim Nachrechnen selten). Bewusst ohne KI-Aufruf, anders als Story/Tiefenanalyse: die 8 Kriterien sind harte Zahlenvergleiche (Kurs vs. SMA50/150/200, 52-Wochen-Hoch/-Tief), ein LLM würde nur Latenz/Kosten hinzufügen, ohne die Prüfung zuverlässiger zu machen. Formel 1:1 aus Signal-Hub/src/scorer.py::f_stage2 portiert, nur die Datenquelle ist unabhängig. Zeigt „✓ bestätigt den Pipeline-Stand" bei Übereinstimmung, eine Abweichungswarnung bei Diskrepanz, oder einen Hinweis auf zu kurze Kurshistorie — je Kriterium einzeln farbcodiert. 20-Minuten-Edge-Cache je Ticker.

🏦 Institutionelle Daten, Insider & Analysten-Schätzungen (seit 2026-08-30, seit 2026-10-04 Teil der 🔬 Tiefenanalyse, nur US-Werte): ergänzt die automatische, kostenlose institutionelle Trendkennzahl (siehe oben) um echte Insider-Transaktionen (SEC Form 4) und Analysten-EPS-Schätzungen — beides liefert die kostenlose SEC-Route nicht. Datenquelle Business Quant (Free-Plan, Worker-Funktion businessQuantDaten()). Bewusst nur im Einzelabruf: der Free-Plan erlaubt insgesamt 30 Requests/Tag (nicht pro Ticker), eine Tiefenanalyse braucht drei davon — genug für rund zehn Berichte am Tag, für hunderte Screener-Kandidaten nicht. Ist das Kontingent erschöpft, läuft der Bericht ohne diese Daten und sagt das. Rein informativ, kein Score-Bestandteil.

🔑 Key Points (Kopf der Seite, seit 2026-09-28): direkt unter dem Ticker die Punkte, die für die Entscheidung zählen, in einem iPhone-Bildschirm. Oben das Urteil der Minervini-Analyse (erste harte Hürde oder „sauberes Setup"), darunter fünf Zeilen: Setup (Pivot-Status, Pivot, Abstand, Stop), Risiken (Markt-Regime gelb/rot, Earnings ≤ 10 Tage, 50/80-Klimax, > 10 % über SMA50, ab 3. Basis, Stop > 8 %, Forward-Test der Tier-Kohorte hinter dem Index), Stärken und Schwächen (die drei Säulen mit ihrer Leitzahl, Trend-Template, Sektor-Thema samt Rang und Leader-Status) und Story. Alles außer der Story wird aus Werten berechnet, die die Seite ohnehin hat (keyPointsBlock()), mit denselben Schwellen wie Minervini-Stufen und Säulen, damit oben und unten nie etwas anderes steht. Die Story-Zeile füllt sich nachträglich, in dieser Rangfolge: das Kurzfazit einer höchstens acht Tage alten 🔬 Tiefenanalyse, sonst der Katalysator aus dem täglichen Top-Setups-Lauf (Websuche, als unverifizierter Fremdinhalt gekennzeichnet), sonst die ersten Sätze der KI-Zusammenfassung von /story. Vier Sprungknöpfe (Minervini-Stufen, Säulen & Sektor, Tiefenanalyse, Story & Zahlen) klappen den passenden Block auf und springen hin. Die Direktanalyse hat keine Key Points, weil ihr die Scan-Daten fehlen.

🚀 Momentum-Dashboard (aufklappbar unter den Key Points): beantwortet die Leitfrage „wie aussichtsreich ist der weitere Trendverlauf?" ausführlich. Der Blockkopf zeigt die drei Säulen als Ampelpunkte. Innen ein Verdikt-Satz (die Risiko-Chips stehen seit 2026-09-28 in den Key Points), darunter drei Säulen in genau den Dimensionen, nach denen dieses System sucht: 🚀 Momentum (Leitzahl RS-Rating 1–99, dazu Abstand zum 52-Wochen-Hoch, Basis-Verengung und -Nummer; das Trend-Template steht nur noch in der Minervini-Analyse), 🏦 Geldfluss (Leitzahl: jüngste Quartalsveränderung des institutionellen Besitzes als Mini-Verlauf über drei Quartale, dazu Up/Down-Volumen, Chaikin Money Flow, Volumen 5T/50T) und 💵 Substanz (Leitzahl Code 33, dazu EPS-/Umsatzwachstum YoY und Nettomarge). Speist sich ausschließlich aus Feldern, die signals.json ohnehin liefert — rs_rating, institutional, institutional_trend, basis_nr, extended_pct und trend_template standen bis dahin im Datensatz, wurden auf dieser Seite aber nirgends angezeigt, obwohl sie die Leitfrage am direktesten beantworten. Kein zusätzlicher Datenabruf, kein KI-Aufruf. Fehlen einem Ticker die 13F-Zahlen (viele EU-Werte), fällt die Geldfluss-Säule auf den Chaikin Money Flow zurück.

🏦 Institutioneller Besitz (Info-Block in der Geldfluss-Säule, seit 2026-09-15): zeigt % gehalten | % vom Float | Anzahl Institutionen | Label plus — wo Yahoo die Veränderung je Top-Holder mitliefert — „Top-Holder: X aufgestockt, Y reduziert". Label niedrig / normal / hoch je Größenklasse nach Marktkapitalisierung in USD: Small Cap (< 2 Mrd) niedrig < 20 %, hoch > 60 % · Mid Cap (2–10 Mrd) < 40 % / > 75 % · Large Cap (> 10 Mrd) < 55 % / > 85 %, bezogen auf „% gehalten". Die Schwellen stehen als Konstanten INSTITUTIONAL_SCHWELLEN/INSTITUTIONAL_SMALL_BIS_USD/INSTITUTIONAL_MID_BIS_USD in scorer.py. Werte über 100 % werden nicht gekappt, sondern als „Daten unplausibel" markiert; fehlen Daten (z. B. ETFs), steht „n/a". Hinweis im Block: 13F-Daten, bis ~45 Tage verzögert. Reine Information — fließt weder in Score, Filter, Ranking noch in die Ampel der Säule. Die frühere Zeile „Institutioneller Anteil" mit Grün/Gelb nach 30–70 % ist dafür entfallen.

🧭 Sektor-Kontext (Narrativ-Bezug, seit 2026-09-06): Streifen unter den drei Säulen, der die Frage beantwortet, die keine der drei Engines bisher stellt: Läuft diese Aktie mit einem führenden Thema oder als Einzelfall gegen ihren eigenen Sektor? (O'Neil: „buy leaders in leading groups"). Zeigt das Thema des Tickers samt Themen-ETF, dessen RS-Rating und Rang unter den 21 Themen, die Monatsperformance des Themas und ob es über der SMA50 notiert — dazu einen Klartext-Satz in vier Ausprägungen: Marktführer im führenden Thema, Marktführer im schwachen Thema, Mitläufer mit Rückenwind, oder Einzelläufer gegen den Sektor. Reiner Frontend-Join auf Rotation-Dashboard/data/rotation.json (7,8 KB, längst in der Deploy-Whitelist) — kein neuer Datenabruf, keine KI. Zuordnung in zwei Stufen: steht der Ticker in der Leader-Liste des Rotation-Dashboards, ist sein Thema direkt bekannt, sonst läuft sie über den GICS-Sektor. Für Werte ohne Themen-Gegenstück (Real Estate — es gibt keinen Immobilien-ETF unter den 21 Themen) erscheint ein ehrlicher Hinweis statt einer erzwungenen Zuordnung. Seit 2026-09-17 zeigt der Streifen zusätzlich eine Branche-Kachel (feinere Einordnung als der grobe GICS-Sektor/Themen-ETF, z. B. „Industrial Machinery/Components" statt nur „Industrials") — kein neues Feld, signals.json liefert branche je Treffer ohnehin schon, stand bislang nur nicht auf dieser Karte.

Dabei gefundener Bug (2026-09-06): 142 von 496 Treffern liefen bei der Sektor-Zuordnung ins Leere. Rotation-Dashboard/src/sektor_mapping.py kannte nur ältere GICS-Schreibweisen — „Healthcare" und „Consumer Cyclical", aber nicht die von Yahoo tatsächlich gelieferten „Health Care" (76 Treffer!), „Consumer Discretionary" (50), „Consumer Staples" und „Telecommunications". Betraf nicht nur die neue Anzeige: diese Aktien konnten im Rotation-Dashboard nie als Leader ihres Themas auftauchen, weil die Zuordnung schon vorher scheiterte. Die vier Aliasse sind in sektor_mapping.py und in der Frontend-Tabelle der Setup-Analyse ergänzt — Zuordnungsquote von 354 auf 367 Werte, übrig bleiben nur noch 6 (Real Estate 5, Miscellaneous 1), die tatsächlich kein Themen-Gegenstück haben.

⚡ Katalysator: „Warum gerade jetzt?" (Stufe 2 des Narrativ-Fahrplans, seit 2026-09-09, unter dem Sektor-Kontext): zeigt den Anlass aus dem täglichen Katalysator-Layer (siehe 📰 Katalysator-Layer weiter oben), wenn der Ticker heute unter den Top-Setups war (window.TOP_SETUPS_KATALYSATOR, derselbe Ladepfad wie auf der Startseite) — kostenlos, ohne Knopf. Steht dort nichts, sucht die 🔬 Tiefenanalyse beim Erstellen selbst danach (Worker-Funktion katalysatorSuchen(); Prompt, Klassenliste, Domain-Filter und search_recency_filter: week 1:1 aus katalysator.py) und nennt das Ergebnis in ihren Quellen; den eigenen Katalysator-Knopf gibt es seit 2026-10-04 nicht mehr. PERPLEXITY_API_KEY liegt dafür als eigenes Worker-Secret (wrangler secret put PERPLEXITY_API_KEY, gleicher Schlüsselwert wie das GitHub-Repo-Secret des Batch-Laufs, aber getrennt zu setzen), nie im Browser, weil je Abruf echte Kosten entstehen. Das Ergebnis ist Fremdinhalt aus dem Netz: 280 Zeichen Längenlimit, Fußnotenmarken entfernt, Klasse gegen feste Liste geprüft, nur https-Quellen, ausschließlich escaped angezeigt.

🎓 Minervini-Analyse (seit 2026-09-07; seit 2026-09-28 steht ihr Urteilssatz in den Key Points, die fünf Stufen im aufklappbaren Block, dessen Kopf das Ergebnis „sauber / beobachten / kein Kauf" zeigt): beantwortet die Frage „was würde Mark Minervini zu diesem Setup sagen?“ — und zwar so, wie er tatsächlich entscheidet: nicht als Punktesumme, sondern als Abfolge von fünf Gates (1. Marktphase → 2. Trend-Template → 3. Basisreife/VCP → 4. Einstiegspunkt → 5. Risiko & Timing). Die erste verletzte Stufe ist der Ausschlussgrund; alle Stufen dahinter werden abgeblendet dargestellt, weil sie für die Entscheidung dann keine Rolle mehr spielen. Jede Zeile nennt den gemessenen Wert und die dahinterstehende Regel samt Schwelle. Rein regelbasiert aus Feldern, die die Seite ohnehin lädt (trend_template, basis_nr, extended_pct, klimax_warnung, Pivot-Status/-Stop, earnings, Markt-Regime) — kein KI-Aufruf, kein Netzabruf, reproduzierbar. Bewusst nicht als Zitat oder simulierte Aussage formuliert: das wäre eine erfundene Äußerung einer realen, lebenden Person zu einer konkreten Aktie — Überschrift und Text sagen durchgehend „nach seinen publizierten Regeln“. Beim Einbau gegen den vollen Datensatz geprüft (Stand 09.09.2026, 497 Treffer): 59 grün, 116 gelb, 322 rot — häufigste erste Hürde ist das Trend-Template, danach die Basisreife. Die Verteilung wandert naturgemäß mit dem Markt; sie belegt hier nur, dass die Stufen über das ganze Universum sauber greifen.

💬 Zitat-Karte als Ergänzung darunter: zeigt seinen echten, datierten neuesten Marktkommentar aus dem manuell gepflegten Minervini-Lexikon (siehe Minervini-Lexikon im Coach) samt Alter in Tagen. Klar gekennzeichnet als Aussage zum Gesamtmarkt, nicht zu diesem Ticker — ein undatiertes Zitat neben einer Einzelaktie würde sonst so aussehen, als hätte er sich zu ihr geäußert. Datenquelle seit 2026-09-17 der private Gist, nicht mehr eine Datei: setup-detail.html::ladeLexikonAusGist() lädt client-seitig aus demselben privaten Gist wie der Coach-Lernfortschritt (localStorage-Token gh_gist_token) — ohne diesen Token auf dem jeweiligen Gerät bleibt die Karte einfach weg, der Regelabgleich darüber ist davon unberührt. Kein separater Upload-Schritt nötig: was im Gist steht (auf der Seite im Coach erfasst oder vom Mac per Abgleich hochgeladen, siehe dort), zieht diese Karte direkt von da, auf jedem Gerät mit hinterlegtem Token. Im Coach gelöschte Posts verschwinden auch hier. Direkt unter der Minervini-Analyse folgt der Block 🔬 Tiefenanalyse (siehe oben).

Sicherheitsfix 2026-09-17: die Zitat-Karte lud Minervinis Original-Posts vorher 10 Tage lang öffentlich mit aus. Der ursprüngliche Transportweg (Datei Signal-Hub/data/minervini-lexikon/minervini_lexikon.json, per run.py nach R2 _deploy/ hochgeladen, von dort per cloudflare-pages/deploy.command-Whitelist mit auf mts-hub.pages.dev deployt) war seit dem Bau dieser Karte am 09-07 unbemerkt öffentlich erreichbar — bewusst nie gewollt, siehe die Rechte-Entscheidung im Minervini-Lexikon-Absatz. Fix: Whitelist-Zeile und R2-Upload entfernt, R2-Objekt gelöscht, Karte liest seither ausschließlich über den privaten Gist (siehe Absatz oben). Der Cloudflare-Edge-Cache der alten URL wurde nicht aktiv geleert (keine eigene Zone für mts-hub.pages.dev in diesem Account) — lief spätestens 09-24 von selbst ab, bewusst akzeptiertes Restrisiko (nicht verlinkt, nur bei exakter URL-Kenntnis erreichbar). Regel jetzt zusätzlich in CLAUDE.md, damit sie auch künftigen Sitzungen bekannt ist.
Dabei gefundener Bug (2026-09-07/09): ein Datenausfall verwandelte sich still in eine Aktienschwäche. Aufgefallen an einem lokalen Lauf, in dem alle 495 Treffer Kriterium 8 des Trend-Templates (RS-Rating ≥ 70) verfehlten — nicht wegen Marktschwäche, sondern weil der marktweite RS-Post-Pass (scorer.py::f_rs_marktweit, Quelle Markets 360) in genau diesem Lauf ausfiel und nur der Rang im heutigen Kandidatenpool übrig blieb. Drei Fehler, alle behoben: (1) Die Zählweise widersprach der eigenen Semantik. scorer.py behandelt ein fehlendes marktweites Rating ausdrücklich als „nicht bewertbar" (pass: null), zählte es im Zähler erfuellt aber trotzdem als Minuspunkt — 7/8 war damit das erreichbare Maximum, „Trend-Template vollständig" systemweit unerreichbar. Jetzt kennt kriterien drei Zustände (true erfüllt, false durchgefallen, null nicht bewertbar), gesamt zählt nur die bewertbaren, die volle Zahl bleibt als kriterien_gesamt sichtbar — im Ausfall steht dann ehrlich 7/7 statt 0/8. (2) Kein Fallback. lade_markt_rs() wählte per os.path.exists() genau einen der beiden Datenpfade — war die erste Datei vorhanden, aber unlesbar oder ohne RS-Spalte, wurde der zweite nie angesehen. Beide werden jetzt nacheinander probiert. (3) Stille. Der Ausfall stand als beiläufige Zeile im Log, ununterscheidbar von „Datei nicht vorhanden". Jetzt nennt die Meldung den konkreten Grund je Pfad, meldet einen Totalausfall deutlich und warnt zusätzlich, wenn mehr als die Hälfte der Treffer ohne marktweites Rating bleibt. Zur Einordnung: der Cloud-Lauf war nicht betroffen — die Live-Daten wiesen durchgehend gültige Ratings aus (242 direkt aus Markets 360, 356 kalibriert, 0 Pool), und auch der lokale Stand ist inzwischen wieder intakt. Die Ursache des einen Ausfalls ließ sich nachträglich nicht mehr reproduzieren; genau deshalb zielt der Fix darauf, dass ein solcher Ausfall künftig auffällt und die Bewertung nicht mehr verfälscht.

📊 Belegzeile darunter — der Forward-Test als Gegengewicht zum Score: zeigt für die Tier-Kohorte des Tickers die unverzerrte Trefferquote nach 4 Wochen und den Vorsprung/Rückstand gegenüber dem Markt-Index (data/score_backtest.json), dazu aus data/score_faktoren_backtest.json den Faktor mit dem höchsten gemessenen Vorhersagewert neben dem Faktor mit dem höchsten Score-Gewicht. Bewusst auch dann sichtbar, wenn der Forward-Test dem Score widerspricht — ein Score ohne Trefferquote daneben liest sich sonst wie ein Versprechen. Beide Dateien sind winzig (zusammen ~9 KB gegen 4,5 MB signals.json) und standen längst in der Deploy-Whitelist.

📊 KGV/KUV nach Geschäftsjahr, Free Cashflow, Quartalsergebnisse: Die Kennzahlenkarte zeigt statt „trailing"/„forward" das echte Geschäftsjahr (z. B. „KGV 2025"/„KGV 2026 (Schätzung)", „KUV 2025"/„KUV 2026 (Schätzung)") — wichtig bei Unternehmen mit vom Kalenderjahr abweichendem Geschäftsjahr. Dazu Free Cashflow als absoluter Wert und als FCF-Marge (% vom Umsatz, aus marketCap/priceToSales zurückgerechnet). Der Abschnitt „📊 Quartalsergebnisse" zeigt Umsatz, Gewinn je Aktie und Nettomarge der letzten ~5 Quartale (QoQ, neuestes Quartal oben) — ergänzt Code 33, das nur das jüngste Quartal gegen das Vorjahresquartal (YoY) prüft, um dieselben Werte über den gesamten verfügbaren Verlauf.

DatumÄnderungGrund
08-24KGV/KUV nach Geschäftsjahr statt trailing/forward, Quartalsergebnisse-Tabelle ergänztwichtig bei Unternehmen mit abweichendem Geschäftsjahr (z. B. Apple); nutzt dieselbe Quartalsserie wie Code 33, kein zusätzlicher Yahoo-Aufruf
08-27KUV bekommt ebenfalls eine Jahreszahl statt einer einzelnen Kennzahl; Quartalsergebnisse zeigen das neueste Quartal obenNutzerfeedback; Yahoo liefert kein fertiges Forward-KUV-Feld, daher aus der Analysten-Umsatzschätzung berechnet
09-04Free Cashflow als Wert + FCF-Marge ergänzt; Nettomarge als sechste Spalte in den QuartalsergebnissenNutzerwunsch; FCF-Marge nutzt denselben aus KUV zurückgerechneten Umsatz, Nettomarge dieselbe Code-33-Grundlage — kein zusätzlicher Yahoo-Aufruf
09-06Momentum-Dashboard mit Verdikt-Satz, drei Säulen (Momentum/Geldfluss/Substanz) und Forward-Test-Belegzeile als neuer Kopf der SeitePrüfung des Seitenaufbaus gegen das Systemziel „führendes Momentum + institutioneller Zufluss + fundamentale Substanz": RS-Rating, institutioneller Quartalsverlauf, Basis-Nummer und Extended-Abstand lagen bereits im Datensatz, waren aber nirgends sichtbar; die Antwort auf die Leitfrage stand verstreut in drei zugeklappten Blöcken
09-07Minervini-Analyse (fünf Gates in seiner Entscheidungsreihenfolge) + Zitat-Karte aus dem Lexikon als neuer Block vor dem Sofort-Überblick; „Tiefere Analyse“-Button von ganz unten dorthin verschobenNutzerwunsch „was würde Minervini zu diesem Wert sagen“; die Gate-Reihenfolge macht sichtbar, warum ein Setup ausfällt — eine Punktesumme kann das nicht. Dabei fiel auf, dass Kriterium 8 (RS-Rating) systemweit nie erfüllt ist (siehe Hinweis oben)
09-17Zitat-Karte liest jetzt aus dem privaten Gist statt aus einer öffentlich deploybaren Datei (Sicherheitsfix, siehe Hinweis oben)die Datei lag seit 09-07 unbemerkt 10 Tage öffentlich auf mts-hub.pages.dev — bewusst nie gewollt; Fix entfernt den öffentlichen Transportweg vollständig
09-24Code 33: einmaliges Verlustquartal zählt nicht mehr als Vergleichsbasis für die Margenverbesserung; die Karte nennt das ausgeschlossene QuartalBefund HALO: 3/3 nur durch einen Sonderverlust im Vergleichsquartal (Details im Hinweis bei den Code-33-Kriterien)
09-28🔑 Key Points als neuer Kopf der Seite; Dashboard, Minervini-Analyse, „Alle Kriterien" (Sofort-Überblick + Technik- und Fundamental-Karten zusammengelegt) und Fundamentalanalyse zugeklappt; Trend-Template nur noch in der Minervini-Analyse, Gegenprüfung dorthin verschoben; Chart am iPhone niedrigerNutzerwunsch „zu viel Scrollen am iPhone"; Vorschläge 1 und 2 aus dem Setup-Analyse-Fahrplan. Mehrere Themen standen bis zu fünfmal auf der Seite
10-04📅 Mehrjahreszahlen (SEC bis acht Jahre, split-bereinigt, sonst Yahoo vier Jahre) in der Fundamentalanalyse; Management-Aussagen für Europäer ohne US-Listing per Websuche auf der UnternehmenswebsiteSetup-Analyse-Fahrplan Nr. 10 und 6: CAN SLIM „A“ war mit fünf Quartalen nicht belegbar, Rheinmetall und Siemens hatten keinen Management-Abschnitt. ESEF fiel aus (Deutschland fehlt, Berichte 40–50 MB)
10-04🔬 Tiefenanalyse ersetzt Erweiterten Bericht, Tiefere Analyse, Business-Quant- und Katalysator-Knopf; Modell gpt-oss-120b statt Llama; Berichte in R2 gespeichert und auf jedem Gerät abrufbarSetup-Analyse-Fahrplan Nr. 3, 8 und 9: vier Knöpfe mit sich überschneidenden Inhalten waren am iPhone unübersichtlich, Llama 8B erfand Aussagen, und jeder Bericht war nach dem Neuladen weg. Kimi K2.6 war gewünscht, ist im kostenlosen Workers-AI-Plan aber nicht verfügbar

🎯 Visuelle Priorisierung + Alters-Ampel (seit 2026-08-08): Das Panel hob sich bisher kaum vom App-Grid darunter ab — acht gleich gewichtete Kacheln lenkten stärker vom eigentlichen Signal ab als der Text-Block selbst. Jetzt: Akzent-Rahmen mit Glow, größere/fettere Titel- und Ticker-Schrift. Der „Stand"-Zeitstempel ist zusätzlich farbcodiert (🟢 <6 Std., 🟡 6–24 Std., 🔴 >24 Std. — an der ~6-stündigen Kadenz der Cloud-Pipeline ausgerichtet); ab 24 Std. erscheint zusätzlich ein rotes Warnbanner mit „☁️ Jetzt aktualisieren"-Button, der denselben triggerCloudWorkflow() auslöst wie in Signal-Hub/Price-Action-Hub/Rotation-Dashboard (jetzt auch in index.html dupliziert, eigener gh_actions_token-Check). Zweck: verhindert, dass während der Arbeit am System (Mac mini kurz aus, Pipeline pausiert) veraltete Setups unbemerkt für frisch gehalten werden.

🌗 Hell/Dunkel-Umschaltung (seit 2026-08-24): Die Startseite folgt demselben :root[data-theme] + prefers-color-scheme-Muster wie die anderen Apps, Toggle-Button neben der Command-Bar oben.

▸ Verlauf & Begründung (1 Eintrag)

Vierte Migration auf das im Wiki bewährte Muster. Die Startseite hatte mit Abstand die meisten verstreuten rgba(255,255,255,.0X)-„Weiß-Tönungen" (Skeleton-Loader-Shimmer, KPI-Chip-Ränder, Setup-Zeilen, Beleg-/Konfluenz-Badges) — solche Weißtöne setzen einen dunklen Untergrund voraus und werden auf hellem Grund unsichtbar. Statt für jede Stelle einen eigenen Hell/Dunkel-Variablenwert zu pflegen, nutzt der Umbau durchgehend color-mix(in srgb, var(--text) X%, transparent) — ergibt automatisch einen weißen Schleier im Dunkel- und einen dunklen im Hellmodus, weil --text selbst themeabhängig ist. Gleiches Prinzip für die zwölf App-Karten-Akzentfarben: color-mix(in srgb, var(--accent) 65%, black) dunkelt sie im Hellmodus pauschal ab. Die Marktampel-Farbe kommt aus dem geteilten assets/marktampel.js — exposure() liefert seither CSS-Variablen-Strings ('var(--success)') statt Hex-Literalen (betrifft nur die Startseite, Markets 360 nutzt einen separaten, unveränderten Export). Zwei Stellen, die der Ampelfarbe eigenhändig einen Alpha-Kanal anhängten, wurden auf color-mix(in srgb, ${reg.color} 33%, transparent) umgestellt, damit die Farbe live mit dem Theme-Wechsel mitgeht.

🗂️ Trading-Apps-Grid in drei Untergruppen (seit 2026-09-13, Nutzerwunsch): Das bis dahin einzelne 12-Karten-Grid „Trading Apps" ist in drei eigene section-title/app-grid-Blöcke aufgeteilt — Portfolio & Verwaltung (nur die Hauptapp — die einzige App, die das echte Depot verwaltet), Screening & Marktumfeld (Daily Signal Hub, Price-Action-Hub, Rotation-Dashboard, Minervini Markets 360, Markets360 Screener, Lokaler Trend-Screener, Stufe-2-Monitor) und Auswertung, Lernen & Anleitung (Minervini Coach 2, Fragenkatalog: Stop-Disziplin, Backtest-Vergleich, ETF-Analyse). Karten selbst unverändert, nur neu sortiert — dieselbe Struktur wie die bereits bestehenden Blöcke Externe Quellen/Dokumentation/Persönliches darunter.

🎯 Fragenkatalog: Stop-Disziplin (seit 2026-09-13, Karte „Auswertung, Lernen & Anleitung"): 35 Fragen in neun Gruppen (Verlustmuster, Hemmschwelle, Investment vs. Training, Stop-Regel, Alt-Positionen, Training-Umfang, App-Nutzung, Coach-Kopplung, das große Warum — Gregersen-Kernfragen auf den eigenen Trading-Verlauf bezogen), oben ein Journal-Befund-Streifen mit echten Kennzahlen aus mts_data.json (Trefferquote, Median-Gewinner/-Verlierer, Anteil ohne echten Stop). Antworten speichern je Frage automatisch (db-Fähigkeit des Artifacts, geräteübergreifend) — Fragenkatalog öffnen. Eigenständiges, fünftes Design (Fraunces/Public Sans/IBM Plex Mono, Tannen-Teal-Akzent) — knüpft inhaltlich an die Fragen-Werkstatt im Coach an, ist aber keine Coach-Seite. Design-System und URL siehe CLAUDE.md.

🗂️ MTS-Inventur (seit 2026-09-14, Karte „Auswertung, Lernen & Anleitung"): Bestandsaufnahme des Gesamtsystems auf Plausibilität und Nutzen — Zeitleiste „Bauen und Traden" (Käufe laut Journal vs. Commits je Monat), Befunde (was trägt, was doppelt vorhanden ist, Pflegelast, offene Punkte), eine Nutzungs-Inventur (jede App und jeder Coach-Bereich mit zwei Klicks: wie oft genutzt, behalten/schlanker/weg) und 43 Fragen in neun Gruppen A–I (Zweck, Bauen vs. Traden, Screening, Marktumfeld, Backtests, Hauptapp/Journal, Coach mit Lexikon und Fragen-Werkstatt, Dokumentation, Verschlanken/Erweitern), davon zehn ★-Einstiegsfragen. Antworten speichern automatisch (db-Fähigkeit, geräteübergreifend) und dienen als Grundlage für spätere Verschlankungs- und Optimierungsvorschläge — Inventur öffnen. Eigenständiges Design (Bricolage Grotesque/Source Serif 4/JetBrains Mono, Kobalt-Akzent), ergänzt den Fragenkatalog Stop-Disziplin, ersetzt ihn nicht.

✅ Minervini-Trainingsplan (seit 2026-09-22, erste Karte in „Auswertung, Lernen & Anleitung"): 12-Wochen-Ablauf zum Abhaken, Neustart am 12.10.2026 bis 03.01.2027 (der erste Start am 23.09. blieb ohne einen einzigen Haken), abgeleitet aus dem Journal-Befund (Median-Verlierer doppelt so tief wie Median-Gewinner) und den Inventur-Antworten: einmalige Startaufgaben, täglicher Check (Marktampel, Stops, max. 3 Wochen-Kandidaten), Kauf-Checkliste mit Positionsrechner, Wochen- und Monats-Rückblick, Regelwerk mit Wenn-dann-Plänen, Stufenplan nach Minervinis Progressive Exposure und eine Bau-Freigabe erst nach 5 sauberen Trades. Das Regelwerk ist mit deinen Antworten aus dem Fragenkatalog vorbelegt: Topf 20.000 €, 300 € Risiko je Trade, Stop höchstens 10 %, Trainings-Depot finanzen.net Zero. Hebelprodukte sind erlaubt, aber nur mit fester Stop-Order und demselben Euro-Risiko wie Aktien (Entscheidung vom 11.10.). Häkchen und Regelwerk speichern automatisch (db-Fähigkeit, geräteübergreifend auf iPhone, iMac und MacBook, Anmeldung bei claude.ai nötig; offline bleibt der Stand nur auf dem jeweiligen Gerät) — Trainingsplan öffnen. Das Disziplin-Protokoll (Regeltreue, Ergebnis in R, Stufe, Bau-Freigabe) führt seit 2026-10-09 die Hauptapp im Psychology-Tab, der Trainingsplan verlinkt dorthin (siehe Hauptapp-Karte). Wie jede App im Training genutzt wird, steht im Abschnitt „Trainingsphase" ihrer Anleitung — alle App-Anleitungen außer TraderFox KI-Agenten haben einen (seit 2026-09-22), die Gesamtsystem-Anleitung fasst sie in einer Tabelle zusammen. Eigenständiges Design (Schibsted Grotesk/Atkinson Hyperlegible/Red Hat Mono, Aubergine-Akzent).

🚦 Stufe-2-Monitor (seit 2026-09-13, Karte „Screening & Marktumfeld"): Marktumfeld-Ampel aus Leitindizes + Marktbreite (Ebene A: Web-Kursstruktur, immer verfügbar; Ebene B: RS-Rating/Trend-Template/Pivot/Marktbreite aus dem Markets-360-Nachtscan, wo erreichbar) plus eine eigene, editierbare Watchlist mit Stufe-2-Check (5 der 8 Trend-Template-Kriterien, ohne Ebene B) bzw. vollem Trend-Template/Pivot-Status (mit Ebene B) — Stufe-2-Monitor öffnen. Eigenständiges Claude-Artifact außerhalb dieses Repos, eigenes Design (Archivo/IBM Plex Sans/Mono, Teal-Graphit). Verbindlich bleibt weiterhin die volle Minervini-Marktampel der Startseite/Hauptapp/Markets 360 (siehe oben) — der Monitor ist eine ergänzende, watchlist-fokussierte Zweitmeinung, kein Ersatz.

🎯 Maick's Trading System (Hauptapp)

Portfolio-, Risiko- und Trade-Verwaltung. Tabs: Dashboard (Cockpit Terminal mit Radial-Tacho fürs Risiko-Budget, fünf Editorial-Kacheln mit Arc-Akzent, Klumpenrisiko, problematische Positionen), Portfolio, CRV-Coach, Trade-Planner (bis 2026-08-06 „Risk Management", inkl. Risiko-Cockpit & Trade-Planner), FIRE Kalkulator; unter „Mehr": Psychology, Committee, Steuer, Lernen & Tools. Der frühere Tab „Trading Cockpit" ist aufgelöst (Tab-Konsolidierung 2026-07): CSV-Import läuft über das 📥-Import-Modal (Dashboard/Portfolio, mit Vorschau & Duplikat-Erkennung), die offenen Positionen leben in Portfolio, die KPIs im Dashboard; die optionale Streamlit-Analyse (Port 8502) ist als Link unter „Lernen & Tools" erreichbar.

📖 Betriebsanleitung (seit 2026-08-09, Verlauf teils aufklappbar seit 2026-08-27): Header-Button „📖 Anleitung" verlinkt eine dedizierte Anleitung als Claude-Artifact — anders als bei Price-Action-Hub/Rotation-Dashboard (Symbol-/Filter-Referenz) geht es hier um den praktischen Trading-Ablauf: Ersteinrichtung, täglicher Ablauf, Wochen-/Monats-Check (dieselbe Checkliste wie im Psychology-Tab, hier mit Fundstellen im System verknüpft), Trade-Nachbereitung, Einordnung der drei Backtesting-Ebenen sowie ein Kennzahlen-Spickzettel; dazu der Abschnitt „Trainingsphase" (was in den Trainingsplan gehört und was in die Hauptapp, Kategorie „Trading" für jeden Trainings-Trade, bei abweichender Stückzahl gilt die kleinere, Stop nur nach oben). Enthält zusätzlich eigene Verbesserungsvorschläge (u.a. Tools-Tab-Reste, die im Dashboard bereits am 2026-08-03 als redundant entfernt wurden) — Hauptapp-Anleitung. Vier bekannte Fälle/Umbauten im Abschnitt „Zugriff & Notfall" stecken seit 2026-08-27 in einem eigenen aufklappbaren „Änderungen & Versionsverlauf"-Kasten statt als Dauer-Callouts im Fließtext. Design-System und URL siehe CLAUDE.md.

🧭 Gesamtsystem-Anleitung (seit 2026-08-27, seit 2026-09-03 auch als Startseiten-Karte): Zweiter, kleinerer Floating-Button „🧭 Gesamtsystem" direkt unter „📖 Anleitung" verlinkt die Cross-App-Referenzseite — wie alle zehn Apps im Alltag zusammenspielen (Trainingsplan als eigene Karte vorn, Ersteinrichtung, täglicher/wöchentlicher/monatlicher Ablauf über das Gesamtsystem hinweg, Tabelle „Alle Apps auf einen Blick" mit direktem Link zu jeder Einzel-Anleitung und einer Spalte „Im Training") — Gesamtsystem-Anleitung. Eigenes, viertes Design wie die Push-Kriterien-Übersicht (IBM Plex Sans/Mono, Türkis-Akzent, hier Karten pro Zeitphase statt pro Push-Quelle). Zusätzlich zum Hauptapp-Button seit 2026-09-03 auch als eigene Karte im „Dokumentation"-Bereich der Startseite (`index.html`) verlinkt, auf Nutzerwunsch. Design-System und URL siehe CLAUDE.md.

📕 MTS-Handbuch (PDF, seit 2026-10-09, Karte im „Dokumentation"-Bereich der Startseite): 22 Seiten A4 als Klammer um Wiki und Anleitungen — Systemlandkarte (welche App welche Frage beantwortet), Wochen-/Tages-/Kauf-/Verkaufs-/Monatsablauf mit Zeitbudget, Screening richtig lesen inklusive des aktuellen Forward-Test-Stands, Risiko und Positionsgröße, Auswertung, „was du weglassen kannst" und zwölf Vorschläge. Liegt bewusst nicht im Pages-Deploy, sondern als PDF in einem privaten Claude-Artifact, weil es Kennzahlen aus dem eigenen Trade-Journal enthält (mts-hub.pages.dev ist öffentlich). Wird wie Wiki und Anleitungen bei jeder Änderung mitgepflegt, die einen dort beschriebenen Ablauf, eine Regel oder eine App betrifft: Quelle Handbuch/handbuch.html anpassen, Handbuch/erzeugen.sh laufen lassen, auf dieselbe Artifact-URL republishen (Schritte in Handbuch/README.md). Der Doku-Check nach jedem Commit erinnert daran.

Heutiger Stand: Der frühere „Risk Management"-Tab heißt „Trade-Planner" (Struktur/Sub-Tabs unverändert). Die großen Detailbereiche im Dashboard (Kurs-Chart, Portfolio-/Sektor-Heatmap, Drawdown-Monitor, Monatliche P&L, Datenspeicherung & Sync) sind aufklappbare Akkordeons, standardmäßig aufgeklappt. Der frühere „Control Center"-Tab ist komplett aufgelöst — seine zwei Inhalte mit echtem Eigenwert (Daten-Status-Banner, Datenspeicherung & Sync) leben jetzt im Portfolio- bzw. Dashboard-Tab, das KPI-Lexikon ist als Nachschlagewerk hierher ins Wiki gewandert (siehe unten).

▸ Verlauf & Begründung (2 Einträge)

📌 Dashboard-Akkordeons + Trade-Planner-Umbenennung (2026-08-06): Die Detailbereiche im Dashboard (Kurs-Chart, Portfolio-Heatmap, Sektor-Heatmap, Drawdown-Monitor, Monatliche P&L, Datenspeicherung & Sync) sind jetzt echte <details>-Akkordeons (Klasse mts-acc) — auf Nutzerwunsch standardmäßig aufgeklappt, aber jederzeit einzeln zuklappbar; interaktive Steuerelemente (Ticker-Auswahl, Zeitraum-Buttons, Depot-Filter) sitzen bewusst im Akkordeon-Body statt im <summary>, damit ein Klick darauf nicht versehentlich die ganze Sektion zuklappt. mts-acc teilt sich die CSS-Hülle mit dem bereits bestehenden tp-acc aus dem Trade-Planner (ein Akkordeon-Baustein für die ganze App) und behält nur die eigene, kompaktere Label-Typografie. Die Mini-Equity-Curve im Dashboard hat jetzt eigene Zeitraum-Buttons (1J/5J/10J/Max, setEqRange()) und einen Hover-Tooltip (Datum + Stand beim Überfahren mit dem Cursor, samt Crosshair-Linie) — beides vorher nur an der inzwischen entfernten großen Equity-Curve im (aufgelösten) Control Center vorhanden, jetzt am einzigen verbliebenen Ort nachgerüstet. Der frühere Risk-Management-Tab heißt jetzt „Trade-Planner" (Nav-Button & Überschrift „📌 Trade-Planner mit automatischer Risikoberechnung") — Struktur und Sub-Tabs („🎯 Risiko-Cockpit & Trade-Planner" / „🔍 SEPA-Analyse") bleiben unverändert, der Trade-Planner war dem Nutzer wichtig genug, um der Tab-Identität den Namen zu geben. Zusätzlich vereinheitlicht: die vormals kräftige 2px-Akzentumrandung von .trade-setup (Kandidaten-Scanner, Kelly-Kriterium, R-Multiple-Verteilung, Mid-Trade-Checkliste) ist jetzt die gleiche dezente 1px-Umrandung wie überall sonst (.card/.tp-acc) — vorher stach dort alles gleich stark hervor, wodurch nichts mehr wirklich hervorstach; die „📚 Theorie & Referenz"-Karte (Stop-Loss/CRV/Money-Management/Limits/Drawdown, weiterhin standardmäßig eingeklappt) nutzt jetzt ebenfalls die mts-acc-Hülle statt eigener Inline-Styles. Der separate „⚠ Gesamt-Risiko"-Banner unterhalb des Drawdown-Monitors ist entfernt — er duplizierte Prozent/€/Balken 1:1 aus der Cockpit-Terminal-Kachel „Gesamtrisiko"; der einzige darin enthaltene, nicht anderswo verfügbare Wert (konfiguriertes Max-Risiko pro Trade in €) steht jetzt als kurzer Zusatz in der Kachel „Risiko-Budget frei" (z. B. „≈ 24 neue Trades · Max €500/Trade").

🎯 Control Center komplett aufgelöst (2026-08-06, zweistufig): Der Tab zeigte fast alle Kennzahlen des Dashboards ein zweites (Portfolio-Risk-Überblick) und teils drittes Mal (Equity Curve, drei „Detaillierte Dashboards"-Karten) — reine Dopplung derselben Zahlen aus refreshDashboard(). In einem ersten Schritt wurden diese Dopplungen entfernt (Portfolio-Risk-Überblick, die drei Detail-Karten, die zweite Equity Curve samt der dadurch toten refreshControlCenter()/setEqRange()/renderEquityCurve()-Funktionen inkl. Hover-Tooltip, sowie die im CRV-Rechner enthaltene „Gesamt-Risiko"-Tabelle, identisch mit der Dashboard-Depot-Übersicht db-depot-overview). Bei der Prüfung des verbleibenden Rests (Quick Actions, Apple-Ecosystem-Karten, Schnelle Links, Datenspeicherung & Sync) zeigte sich: alles war entweder redundant oder falsch platziert — die Apple-Notes-/Numbers-Deep-Links (notes:///numbers://) funktionieren im Browser nicht zuverlässig, die „Schnelle Links"-Karten ebenso, „Neuer Trade"/„Trades importieren"/„Jahresbericht" existierten in Portfolio bzw. Dashboard bereits als echte Buttons, „Portfolio ansehen"/„FIRE berechnen" waren nur Shortcuts auf ohnehin sichtbare Nav-Tabs, und die „Position Sizing"-Karten verwiesen nur auf den Trade-Planner, der direkt erreichbar ist. Der Tab ist daher komplett entfernt (Nav-Button, HTML, CSS, JS-Tab-Switch-Case); zwei Inhalte mit echtem Eigenwert sind umgezogen: der Daten-Status-Banner (cc-import-status, „Letzter Import: …") steht jetzt oben im Portfolio-Tab, die Karte „Datenspeicherung & Sync" (Offline-First-Hinweis + GitHub-Gist-Sync-Banner mts-gist-banner) steht jetzt ganz unten im Dashboard. Das KPI-Lexikon ist als Nachschlagewerk hierher ins Wiki gewandert (siehe unten).

Dashboard

📈 Gleitende Durchschnitte + Chart-Diagnose (2026-08-27). Die Kurs-Charts im Dashboard zeigen jetzt GD 20 (violett), GD 50 (blau) und GD 200 (rot) — Farben identisch zum Signal-Hub-Minichart, damit beide Apps dasselbe Chartbild sprechen. Im Raster bewusst nur GD 50 und 200: bei 150 px Kachelhöhe würde eine dritte Linie das Kerzenbild zukleistern, und an diesen beiden hängt Minervinis Trend-Template. Abschaltbar über den Haken „📈 GD 20 · 50 · 200" neben den Events; die Wahl merkt sich der Browser (localStorage: mts_chart_gd).

Der nicht offensichtliche Teil: Ein GD 200 braucht 200 Handelstage vor dem ersten sichtbaren Tag. Im 6-Monats-Fenster (~126 Tage) wäre die Linie sonst schlicht leer geblieben. Deshalb holt die App bei kurzen Zeiträumen automatisch zwei Jahre Historie (_rangeMitVorlauf()), rechnet die Durchschnitte über die volle Reihe und beschneidet erst danach auf das gewählte Fenster (_rangeStart()). Nachgemessen an TL0.DE im 6-M-Fenster: 507 Kerzen geholt, 127 angezeigt, alle drei Linien mit 127 Punkten — also durchgehend statt am linken Rand abgeschnitten. Die SMA-Formel selbst ist gegen eine Handrechnung geprüft (Reihe 1…10, Periode 3 → 8 Punkte, erster 2, letzter 9).
🔍 Warum Charts leer bleiben — jetzt sichtbar statt stumm. Ein fehlgeschlagener Kursabruf zeigte vorher nur „keine Daten", egal ob der Server fehlte, das Symbol unbekannt war, ein Timeout zuschlug oder CORS blockte — vier völlig verschiedene Ursachen, ein identisches Bild (dieselbe Fehlerklasse wie die stillen Nullwerte aus der Systemprüfung). _fetchHistory() protokolliert jetzt je Stufe den Grund: Die Kachel nennt ihn unter „keine Daten", die volle Fallback-Kette steht im Tooltip, und _mtsChartDiag() in der Browser-Konsole listet alle betroffenen Symbole auf. Wichtig: Die Funktion existiert nur in der Hauptapp (dort liegt assets/mts-extensions.js) — auf der Startseite aufgerufen kommt erwartungsgemäß ein ReferenceError.
☁️ Charts sind seit 2026-08-25 Mac-mini-unabhängig. Auf der reinen Cloud-Version funktionierten sie vorher nie: /history liefert dort nur die Pages-Fallbackseite statt JSON, die beiden http://-Varianten scheitern an Mixed-Content (die Seite läuft über HTTPS) und Yahoo direkt an CORS. Jetzt wird Yahoo zusätzlich über den bereits vorhandenen Cloudflare-Worker versucht (MTS_PROXY/mtsProxify, den Startseite und Marktampel ohnehin nutzen) — die Reihenfolge bleibt direkt zuerst, am Mac mini kostet das also keinen Umweg (isoliert nachgemessen: kein zusätzlicher Aufruf). Verifiziert mit komplett blockiertem Mac mini (localhost, .local und Tailscale abgeschaltet): 502 Kerzen über den Proxy, GD 20/50/200 vollständig.

Heutiger Stand: Die Portfolio-Hero-Zahl zeigt die Summe aus Kontostand und Depotwert, mit einer Aufschlüsselung „Kontostand X · Depotwert Y" darunter — dasselbe Prinzip gilt konsequent in allen Kacheln, Tabellen und Exports: eine Kachel/Spalte, die „Kontostand" oder „Depotwert" heißt, zeigt ausschließlich den jeweils reinen Anteil, wo bewusst mit der Summe gerechnet wird, heißt die Kachel „Gesamtkapital". Das eigenständige „Liquidität"-Feld sowie die alte Einstellungszeile am Dashboard-Ende (Kontogröße/Sparrate, Apple-Ecosystem-Karten) sind komplett entfernt.

▸ Verlauf & Begründung (4 Einträge)

Gesamt-Portfolio-Hero (Fix 2026-08-03): Die große Zahl zeigt weiterhin die Summe aus Kontostand (reines Cash) und Depotwert (Live-Marktwert der offenen Positionen) über alle Depots. Darunter steht jetzt zusätzlich eine kleine Zeile „Kontostand X · Depotwert Y", die beide Anteile einzeln ausweist — vorher war nur die vermischte Summe sichtbar, obwohl die Portfolioübersicht (Portfolio-Tab) beide Werte pro Depot schon immer getrennt zeigt. Reine Anzeige-Ergänzung, keine Änderung an der zugrundeliegenden Summe (die weiterhin in Risiko-/FIRE-Berechnungen einfließt).

Nachtrag selber Tag — echte Fehletiketten korrigiert: Eine zweite Prüfung ergab, dass mehrere weitere Kacheln nicht nur unvollständig, sondern schlicht falsch beschriftet waren — sie hießen „Kontostand" oder „Depot-Wert", zeigten aber tatsächlich _mtsDepotBalance() = Kontostand + Depotwert kombiniert. Behoben: Portfolio-Tab-KPI-Zeile („Depot-Wert" → „Gesamtkapital", neue Unterzeile mit Aufschlüsselung), 💼 Portfoliostand-Karte (die „Kontostand"-Kachel zeigte die Summe — jetzt eigene „Kontostand"- und „Depotwert"-Kachel), Portfolioübersicht „📊 Alle Depots"-Karte (Kachel „Kontostand" zeigte die Summe — jetzt drei eigene Kacheln Kontostand/Depotwert/Investiert), Dashboard-Mini-Depotkarten (zeigten „Depotwert: X" nur wenn zusätzlich ein Kontostand > 0 vorlag, sonst gar keine Aufschlüsselung — jetzt immer „Kontostand X · Depotwert Y", sobald einer der beiden Werte > 0 ist), Control-Center-Risikotabelle (Spalte „Kontostand" → „Gesamtkapital", da sie als Risiko-%-Basis bewusst die Summe nutzt), Jahresbericht-Export (KPI „Depot-Wert" → „Gesamtkapital" + zwei neue KPIs Kontostand/Depotwert, Depot-Tabelle bekommt eine eigene Depotwert-Spalte statt die Summe in der Kontostand-Spalte zu verstecken) und die FIRE-Eingabe („Aktueller Depot-Wert" → „Aktuelles Gesamtkapital", da das Feld bewusst die Summe für die Vermögens-Hochrechnung nutzt). Durchgängiges Prinzip seither: eine Kachel/Spalte, die „Kontostand" oder „Depotwert" heißt, zeigt ausschließlich den jeweils reinen Anteil; wo bewusst mit der Summe gerechnet wird (Heat, Risiko-%, FIRE, Freies Kapital), heißt die Kachel „Gesamtkapital".

„Liquidität"-Feld komplett entfernt (selber Tag): Beim Nachfragen zur Portfoliostand-Karte fiel auf, dass „Liquidität" (separates manuelles Eingabefeld pro Depot, plus ein Legacy-Duplikat auf Portfolio-Ebene) in keiner echten Berechnung mitzählte — weder Portfolio-Heat noch Freies Kapital noch die Positionsgrößen-Empfehlung lasen es, obwohl die alte Control-Center-Erklärung „Heat = 100% − Liquidität%" versprach. Die einzige Stelle, die es überhaupt nutzte (FIRE-Sync, _syncFireWithDepot), addierte es sogar anstelle von Kontostand — ein zweiter, unabhängiger Fehler. Da es nie befüllt wurde, stand es dauerhaft bei „€0" und sah wie eine tote Dopplung von Kontostand aus. Auf Nutzerentscheidung hin komplett entfernt: Eingabefelder (Depot-Formular, Dashboard-Einstellungen), Anzeige-Kacheln (Portfoliostand, „Alle Depots"-Karte, Control Center, Jahresbericht-Tabelle) und der Datenschlüssel liquiditaet aus defaults()/CSV-Import — bestehende Werte in mts_data.json bleiben unangetastet, fließen nur nirgends mehr ein. Portfolio-Heat im Control Center nutzt jetzt _mtsExposureState().exposurePct (dieselbe Formel wie der Exposure-Deckel), der FIRE-Sync nutzt jetzt durchgängig Kontostand + Depotwert. Mentales Modell seither einfach: Kontostand (manuell) + Depotwert (automatisch) = Gesamtkapital — kein drittes, unabhängiges Feld mehr.

Legacy-Zeile am Dashboard-Ende entfernt (2026-08-03): Die unterste Zeile des Dashboard-Tabs („🎯 Portfolio-Einstellungen" mit den Feldern Kontogröße/Sparrate + drei „Apple Ecosystem"-Kacheln Trading-Journal/Apple-Notizen, Trading-Suite/Numbers, Alle-Dateien/ZIP) ist komplett entfernt — auf Nutzerwunsch, unbenutzt und redundant zu den Depot-basierten Eingaben (Portfolio-Tab) bzw. dem FIRE-Tab (fire-kontostand/fire-sparrate lesen/schreiben dieselben portfolio.kontostand/portfolio.sparrate-Felder bereits vollwertig). Mitentfernt als jetzt toter Code: savePortfolioFromDashboard, syncDashboardInputs sowie die drei Text-Download-Funktionen downloadJournal/downloadNumbers/downloadBoth (inkl. Helfer downloadFile) — alle vier waren ausschließlich von dieser Zeile aus erreichbar.

Markttiming & Gate

Markt-Chip im Cockpit-Terminal-Header: zeigt seit 08/2026 dieselbe volle Minervini-Marktampel wie Startseite/Signal-Hub/Markets 360 (assets/marktampel.js, ladeMarktAmpel()) — Trend-Template + Distribution Days/VIX/Small Caps/52-W-Abstand/Follow-Through-Day/10J-Rendite, live über den Cloudflare-Worker, funktioniert ohne Mac mini. Klick öffnet Markets 360 für die Detailansicht. Vorher war der Chip bewusst eine simple, eigenständige MA/VIX-Regel (🟢 S&P über MA50 und MA200 bei VIX < 20 · 🔴 unter MA200 oder VIX > 25 · 🟡 dazwischen) und lief zudem nur bei erreichbarem 8080-Server (kein Worker-Fallback) — beides seit 08/2026 durch die geteilte Grundlage abgelöst.

Heutiger Stand: Fehlen Portfoliodaten (z. B. Cloud-Zugriff ohne hinterlegten Sync-Token), sperrt eine Overlay-Fläche über dem Cockpit-Terminal alle Kennzahlen, statt Nullen zu zeigen. Das Risiko-Budget erscheint als Radial-Tacho, CRV/Win-Rate/Budget frei/Offene Positionen als Editorial-Kacheln mit eigenem Fortschritts-Bogen. Hell/Dunkel folgt demselben data-theme-Schema wie die anderen Apps, und das Positionslimit wird konsistent aus einer einzigen Einstellung (Risk-Registry) berechnet statt an drei Stellen mit unterschiedlichen Zahlen.

▸ Verlauf & Begründung (4 Einträge)
🔒 Sperrzustand statt Null-Werte (seit 2026-08-23). Ruft man die Hauptapp auf einem Gerät ohne geladene Daten auf — typisch: Cloud-/Pages-Zugriff ohne hinterlegten gh_gist_token —, zeigte das Cockpit-Terminal vorher „Gesamt-Risiko 0,0 %", „Risiko-Budget frei", „0 offene Positionen". Das sieht aus wie ein gültiger, sogar beruhigender Zustand, bedeutet aber schlicht: nichts geladen. In einem Risiko-Werkzeug ist das der gefährlichste denkbare Leerzustand — ein „0 % Risiko" darf nie „nichts geladen" heißen. Neu: eine echte Sperrfläche über den Kennzahlen (.ct-lock, Klasse .ct-gesperrt am Terminal) statt eines Hinweisbanners darüber — KPI-Grid, FIRE-Zeile, Risiko-Gauge, Klumpenrisiko und Zombie-Liste werden weichgezeichnet und sind nicht mehr anklickbar, darüber steht „🔒 Keine Portfoliodaten geladen" mit Erklärung und einem Direkt-Button („☁ Cloud-Sync einrichten" bzw. „☁ Vom Cloud-Sync laden", je nachdem ob schon ein Token hinterlegt ist). Zusätzlich werden alle KPI-Werte auf „—" gesetzt statt auf berechnete Nullen — zentral in renderCockpitTerminal()::ctSetText/kv, damit kein späterer Aufruf die Regel umgeht und wieder eine Null in den DOM schreibt (wichtig auch für Screenreader und Kopieren). Der Header-Status zeigt dann „keine Daten" statt „System aktiv". Unterscheidungskriterium zwischen „nichts geladen" und einem echt leeren Depot: kein Trade UND kein Kontostand — ein eingerichtetes System hat immer mindestens eines von beiden, denn selbst ein leergeräumtes Depot behält seine Trade-Historie.
🎛️ Cockpit-Neugestaltung: Radial-Tacho + Editorial-Kacheln (seit 2026-08-24). Auf Nutzerwunsch eine Design-Exploration mit drei Alternativen erstellt (Radial-Tacho, Ring-Kennzahlen, Editorial+Arc-Akzent — als Claude-Artifact vorgestellt), Entscheidung: Kombination aus Radial-Tacho und Editorial+Arc-Akzent. Ersetzt das vorherige 6-Spalten-Kachelraster (.ct-kpi-grid mit Trennlinien) plus den separaten linearen Balken (.ct-gauge-wrap) — bislang das einzige Rundinstrument im gesamten System, alle anderen Kennzahlen-Anzeigen (Portfoliostand, R-Multiple-Verteilung, Klumpenrisiko) sind weiterhin lineare Balken/Kacheln.

Radial-Tacho: das Risiko-Budget (0–5 % des Kontostands) als großer Halbkreis-Bogen (reines SVG, pathLength="100"-normierter Pfad + stroke-dasharray, kein <canvas>) — Bogenfarbe und die große Zentralzahl folgen derselben Ampel-Schwelle wie vorher (grün ≤3 %, gelb 3–5 %, rot >5 %, riskColor in renderCockpitTerminal()). Die frühere Farbverlauf-Nuance im sicheren Bereich (Teal→Grün-Gradient) ist einer einzelnen Vollfarbe je Zone gewichen — ein SVG-Strich kann keinen CSS-linear-gradient direkt nutzen, das wäre ein eigener <linearGradient>-Def gewesen, für eine Nuance nicht gerechtfertigt.

Editorial-Kacheln: CRV, Win-Rate, Risiko-Budget frei und Offene Positionen bekommen je einen dezenten Viertelkreis-Akzent oben rechts, der den jeweiligen Wert gegen sein eigenes Ziel/Limit zeigt (CRV gegen 4:1 gedeckelt, Win-Rate direkt in %, Budget frei gegen das Euro-Maximum, Positionen gegen das 1,5-fache Limit aus der Risk-Registry) — Bogenfarbe identisch zur bereits bestehenden Ampel-Logik der jeweiligen Kachel, keine neue Farbregel. EV/Trade bekommt bewusst keinen Bogen: ein R-Multiple-Erwartungswert hat keine natürliche 0–100 %-Grenze, ein erzwungener Ring wäre dekorativ statt informativ gewesen.

Technischer Stolperstein: stroke-dasharray per setAttribute() zu setzen wurde von der eigenen CSS-Klassenregel (Default „0 100" + transition) überschrieben — Präsentationsattribute haben in der CSS-Kaskade die niedrigste Priorität, niedriger als jede Stylesheet-Regel. Fix: el.style.strokeDasharray (Inline-Style) statt setAttribute, das schlägt die Klassenregel und die transition greift trotzdem (reagiert auf jede Wertänderung, unabhängig von der Quelle). Verifiziert mit synthetischen Depot-/Trade-Daten über MTS.load()/MTS.refreshAllDisplays() in allen drei Ampelzonen, im Sperrzustand (Bögen blenden korrekt mit auf 0/gedämpft) und in Hell wie Dunkel.
🌗 Hell/Dunkel-Umschaltung auf data-theme migriert (seit 2026-08-24) — letzte von sechs Migrationen, Projekt abgeschlossen. Die Hauptapp lief bisher als einzige App auf einem eigenen dritten Mechanismus (body.light-mode, rein manueller Toggle, kein prefers-color-scheme) — jetzt auf demselben :root[data-theme]-Muster wie die fünf zuvor migrierten Apps (Backtest-Vergleich → Rotation-Dashboard → Price-Action-Hub → Signal-Hub → Startseite → Hauptapp). Anders als bei den fünf anderen Apps musste hier kein neues Hellmodus-Farbsystem gebaut werden — die App hatte bereits vollständige, gepflegte Hell-/Dunkel-Varianten für alle --color-*-Variablen; die Migration war eine reine Mechanismus-Umstellung: localStorage-Schlüssel mts_theme unverändert übernommen (bestehende Nutzerpräferenzen bleiben gültig), 24 body.light-mode/body:not(.light-mode)-Selektoren auf :root[data-theme="light"]/:root:not([data-theme="light"]) umgestellt, mtsToggleTheme() setzt jetzt das Attribut statt einer Body-Klasse, ein neues Inline-Head-Script verhindert das Flackern beim Laden (vorher setzte eine spät im Script-Block laufende IIFE die Klasse erst nach dem ersten Paint).

Dabei sieben echte, vorbestehende Kontrastfehler im Hellmodus gefunden und behoben (waren im allerersten Theme-Audit der Systemprüfung 2026-08-23 schon als „5 kritische Befunde" dokumentiert, aber nie behoben, weil das alte Prüfskript die Hauptapp nur grob testete): Status-Leiste (.sys-statusbar, Trennzeichen .sb-sep, Theme-Button) und die Tastaturkürzel-Anzeige hatten color:rgba(255,255,255,.XX) fest codiert — ein Weißton, der nur auf dunklem Grund funktioniert, im Hellmodus praktisch unsichtbar war. Auf var(--color-muted) bzw. color-mix(in srgb, var(--color-text) X%, transparent) umgestellt (Letzteres passt sich automatisch an, weil --color-text selbst themeabhängig ist — dasselbe Muster wie bei der Startseiten-Migration). Die drei Cockpit-Alarm-Tags (.ct-tag.red/.yellow/.green) hatten ihre Textfarbe als Hex-Literal statt als var(--color-danger/warning/success) hinterlegt — dieselbe Farbe unabhängig vom Theme, obwohl die Variable längst die richtigen, pro Theme abgestimmten Werte bereithielt. Am auffälligsten: die Sperrfläche .ct-lock (erscheint, wenn keine Portfoliodaten geladen sind) hatte einen fest schwarzen Verlaufs-Scrim (rgba(6,13,24,…)) — der helle Sperrtext/-button (--color-hero-text hell = dunkles Teal) wurde darauf fast unlesbar. Neue Variable --color-lock-scrim mit eigenem Hell-Wert löst das.
📐 Positionslimit: eine Zahl statt drei (seit 2026-08-23). Das Cockpit nannte dieselbe Regel an drei Stellen mit drei verschiedenen Zahlen: die KPI-Unterzeile schrieb fest „max. 10 (Minervini)", die Ampelfarbe schaltete bei 10/15, die Alarmzeile warnte ab 15 mit „Empfehlung ≤ 10" — während die eigentliche Risikoprüfung im selben File mit settings.maxPositions (Vorgabe 5) rechnete. Alle drei lesen jetzt dieselbe Einstellung: Unterzeile „max. N (Risk-Registry)", Ampel grün bis N / gelb bis 150 % von N / danach rot, Alarmzeile „⚠ X offene Positionen (Limit: N)". Wer das Limit ändern will, ändert es weiterhin nur an einer Stelle — in den Dashboard-Einstellungen.

🚦 Trade-Gate: Beim Anlegen eines neuen Trades zeigt das Formular die aktuelle Marktphase (grün/gelb/rot mit Begründung, seit 08/2026 aus der vollen Ampel inkl. Faktor-Details und ggf. dem „Tendenz Richtung Gelb"-Hinweis, siehe Startseite). Wird im roten Umfeld eine neue Position eröffnet, fragt die App noch einmal nach — Minervinis Kernregel: in einem schwachen Markt keine neuen Käufe. Geschlossene/historische Trades werden nie geblockt.

📊 Exposure-Deckel nach Marktampel: Die erlaubte Gesamt-Investitionsquote hängt vom Marktumfeld ab — Bearish max. 25 %, Neutral max. 50 %, Bullish bis 100 % investiert (Basis: Einstandswert aller offenen Positionen ÷ Depotwert, dieselbe Kennzahl wie die „invested/heat"-Anzeige in Portfolio und Cockpit). Marktumfeld = window._marktStatus.status, seit 08/2026 aus der vollen Ampel (vorher aus dem eigenen MA/VIX-Check) — dieselbe Grundlage wie überall sonst, daher tendenziell öfter „grün"/höhere erlaubte Exposure als vorher. Der Positionsgrößen-Rechner kappt die empfohlene Stückzahl automatisch auf die verbleibende erlaubte Exposure; würde ein Trade das Limit reißen, fragt die App beim Speichern noch einmal nach. Die aktuelle Exposure samt Limit steht direkt im Marktphasen-Hinweis des Trade-Formulars.

Trade-Erfassung (MTS führt, Numbers ist Archiv)

📒 Trades werden seit 2026-09-17 direkt im MTS erfasst — die Numbers-Tabelle ist nur noch Archiv. Grund (Prüfung Portfolio/Trade-Planner): Der CSV-Import legte bekannte Trade-Nummern nie neu an, aktualisierte sie aber auch nicht — in Numbers nachgetragene Verkäufe kamen deshalb nie im MTS an (Stand der 33 „offenen" Positionen = letzter Import 19.06.). Außerdem las der Import nur ~15 der ≥37 Spalten und erfand Stop/Ziel (−8 %/+20 %), und keiner der 252 Trades hatte Journal, Setup oder Verfassung.

Kauf (Trade-Planner): ganz oben neben dem Depot zuerst „Trading oder Investment?" wählen (Pflicht bei neuen offenen Positionen). Außerdem Pflicht: Verfassung vor dem Kauf, Strategie/These und Notiz, bei Trading zusätzlich ein echter Stop („kein Stop, kein Trade"). Bei Investment (langfristig halten, nicht nach Minervini getradet) sind Stop und SEPA-Verbotsliste optional und die Stop-Abstand-/CRV-Warnungen entfallen; ohne Stop zählt das Risiko mit 0. Marktampel-Rückfrage, Exposure-, Positions- und Sektorlimits gelten bewusst für beide Kategorien weiter — die Kategorie ist eine Kennzeichnung, keine getrennte Buchführung. Freiwillig: Muster nach Minervini (VCP, Cup with Handle, Flat Base, High Tight Flag …), Setup-Typ, SEPA-Score. Marktampel, Pivot-Abstand und Signal-Fingerabdruck werden automatisch mitgespeichert, ebenso das Ausgangsrisiko für das spätere R-Ergebnis. Bei Trading zwei Häkchen fürs Disziplin-Protokoll: Kauf-Checkliste vollständig, Stop sofort als Order (nachträglich änderbar in der Positionsbearbeitung).

Eine Verfassungsskala: vor dem Kauf, während der Haltezeit (Journal) und beim Verkauf gilt dieselbe Liste mit zehn Einträgen (Ruhig & klar, Zuversichtlich, Neutral, Erleichtert, Unsicher / ängstlich, Ungeduldig / FOMO, Gierig / euphorisch, Unter Druck (Verlust aufholen), Frustriert / verärgert, Müde / abgelenkt). Ältere Einträge aus den früheren Listen und die Emoji des alten Journals bleiben gespeichert und werden beim Anzeigen und im CSV-Export auf diese Skala übersetzt.

📔 Journal: Prozessnote A–D bewertet die Entscheidung, nicht das Ergebnis (A nach Plan, B kleine Abweichung, C eine Regel verletzt, D Regelbruch). Sie ersetzt die Sterne, alte Sterne werden als Note angezeigt (5 → A, 4 → B, 3 → C, 1–2 → D). Dazu Verfassung während der Haltezeit, „Was lief gut“, „Was anders“, Notizen. Bei geschlossenen Trades der Abschnitt 🔍 Nachbetrachtung: Was zeigt der Chart heute über den Ausstieg (zu früh, richtig, zu spät, Stop hat Schlimmeres verhindert, unklar) und was nehme ich mit. Fällig wird sie 28 Tage nach dem Verkauf (Trades ab Trainingsstart 12.10.2026): Hinweis oben im Disziplin-Protokoll und in der Trade-Akte, Zahl der fälligen im wöchentlichen Push des Wochen-Rückblicks.

🎯 Disziplin-Protokoll (Mehr → Psychology, Deeplink …/Maick's Trading System.html#disziplin): jede Position mit Kategorie „Trading“ ab dem Trainingsstart am 12.10.2026 ist ein Trainings-Trade (MTS_TRAINING_START, gleicher Wert in agenten/wochen_rueckblick.py). Sauber heißt: beide Kauf-Häkchen, Stop nie nach unten verschoben (aus dem Stop-Verlauf), jeder Verkauf „nach Plan“ und kein Verlust jenseits von −1,5 R (Ergebnis aller Teil- und Komplettverkäufe ÷ Risiko beim Kauf). Angezeigt werden Stop-Order-Quote, Stop-Treue, Ø R, größter Verlust, die Stufe (Stufe 2 nach 8 sauberen geschlossenen Trades, Stufe 3 nach 12 weiteren, jeder Regelbruch setzt auf Stufe 1 zurück) und der Zähler für die Bau-Freigabe (5 saubere geschlossene Trades, Knopf „Bau-Freigabe einlösen“ setzt ihn zurück). Das Ergebnis eines Trades zählt dabei nicht.

🛑 Stop-Momente (Psychology-Tab über dem Disziplin-Protokoll, Deeplink …/Maick's Trading System.html#stopmomente): Das übrige Journal greift erst beim Verkauf — wer einen Stop nicht ausführt, hinterlässt dort keine Spur. Liegt der Kurs einer offenen Position auf oder unter ihrem Stop (USD-Kurse in Euro umgerechnet, gilt für Trading und Investment, sobald ein Stop gesetzt ist), erscheint oben im Dashboard ein roter Hinweis. „Festhalten“ fragt: was hält dich gerade vom Verkauf ab (feste Liste, mehrere möglich: Hoffnung, Verlust realisieren tut weh, zu spät, nicht am Tief verkaufen, Erholung abwarten, Steuer, zum Investment umgedeutet, Fehler nicht eingestehen, Stop lag nicht als Order, nicht dran gedacht, Sonstiges), Verfassung, was tust du jetzt (Pflicht: verkaufen, bewusst halten, umdeuten, unentschieden) und der Satz im Kopf. Gespeichert wird ein Verlaufseintrag der Position (verlauf[] mit typ: 'stopmoment', sichtbar auch in Trade-Akte und Export). Bleibt die Position unter Stop, fragt die App nach 7 Tagen erneut. Die Karte zeigt alle betroffenen Positionen und zählt Gründe, Verfassungen und Entscheidungen über alle Einträge; jeder Grund verlinkt auf das passende Thema im Minervini Coach. Die App bewertet nicht, ob verkauft werden soll — sie hält nur fest, was im Moment passiert.

🖨️ Druck-Zettel für unterwegs (seit 2026-10-10): zwei PDFs, je A4 quer mit zwei A5-Hälften zum Durchschneiden — Kauf- und Verkaufs-Zettel (links Kauf, rechts Verkauf) und Stop-Moment-Zettel (zweimal derselbe). Verlinkt als zwei Karten im „Dokumentation“-Bereich der Startseite. Zum Ankreuzen, wenn die App gerade nicht zur Hand ist, und danach zum Nachtragen. Die Felder stehen in derselben Reihenfolge wie im Trade-Planner, im Verkaufsdialog mit Journal und im Stop-Moment-Dialog, die Auswahllisten (Verfassung, Setup-Typ, Muster, Verkaufsgrund, Fehler, Prozessnote, Gründe und Entscheidung im Stop-Moment) sind dieselben wie in der App; neben jedem Stop-Moment-Grund steht das passende Coach-Thema. Unten auf jedem Zettel steht ein Block „Nachpflegen“. Erst der Eintrag in der Hauptapp zählt fürs Disziplin-Protokoll. Der Zettel rechnet nicht, Stückzahl und Risiko-Budget prüft nur der Trade-Planner. Beim Drucken „Tatsächliche Größe“ wählen. Quelle: Checklisten/*.html mit zettel.css, PDFs neu erzeugen mit node Checklisten/erzeugen.js (bricht ab, wenn eine Hälfte nicht mehr auf A5 passt). tests/test_checklisten.py schlägt fehl, sobald sich eine dieser Listen in der Hauptapp ändert und der Zettel nicht nachgezogen wurde.

📔 Mein Trade Journal (Psychology-Tab, darunter): ein Eintrag je Position — Teilverkäufe hängen an ihr, der Numbers-Altbestand ohne Journal, Reflexion oder Charts bleibt ausgeblendet. Filter nach Kategorie, Muster, Prozessnote (auch „noch ohne“), Fehler aus der Verkaufs-Reflexion (auch „keiner notiert“), Ergebnis und „nur mit Charts“; darüber eine Übersicht mit Notenverteilung, Ø R je Note und den häufigsten Fehlern der gefilterten Einträge. Zwei Ansichten: ☰ Liste (Journaltexte aller Datensätze der Position, Fehler, Muster, Verfassung beim Kauf) und ▦ Galerie (je Position das Vorher-Bild aus Phase Kauf bzw. Plan neben dem Nachher- oder Exit-Bild; Bilder laden erst, wenn sie in Sichtweite kommen). Filter und Ansicht merkt sich das Gerät.

🔀 Vorher/Nachher (aus Galerie, Liste oder Trade-Akte): alle Bilder einer Position in drei Spalten — Vorher (Plan, Kauf), Verlauf & Exit, Nachher — darunter Kauf, These, Verfassung, Verkaufsgründe, Fehler, Lektion, R, Prozessnote und Nachbetrachtung. Ein Tipp auf ein Bild zeigt es groß.

Monats-Rückblick: /rueckblick wertet Prozessnote, „Verkauf nach Plan“, Fehler, Verfassung (Kauf und Haltezeit) und Nachbetrachtung über agenten/journal_auswertung.py aus — dieselbe Zählung wie in der Hauptapp (je Position, R nur aus echtem Risiko, Altwerte übersetzt); Gruppen unter 8 Fällen werden als „zu wenig Fälle“ markiert. Aufruf auch direkt: python3 agenten/journal_auswertung.py --seit 2026-10-12 (--quelle gist für den geräteübergreifenden Stand).

Stop-Momente im Rückblick: agenten/journal_auswertung.py zählt seit 2026-10-10 auch die Stop-Momente des Zeitraums (auch bei noch offenen Positionen): Gründe, Verfassung, Entscheidung und wie oft „Verkaufen — Order heute“ angekündigt und binnen 7 Tagen tatsächlich verkauft wurde. Fehler aus der Verkaufs-Reflexion verlinken in der Journal-Liste ebenfalls auf das passende Coach-Thema.

➕ Nachkauf (Pyramidisieren): in der Positionsbearbeitung mit echtem Kaufkurs, Stückzahl und Datum. Der Durchschnittskurs wird wie beim Broker neu berechnet, der Erstkauf-Kurs bleibt in entryErstkauf erhalten, jeder Kauf steht in pyramidLevels. Nachkauf unter dem Durchschnittskurs fragt nach (Averaging Down). Die starre 25/25/50-Staffel („Progressive Exposure", unten) gibt es weiterhin daneben.

✅ Teil- und Komplettverkauf: Teilverkäufe mit freier Stückzahl über „🎯 Gestaffelter Ausstieg" (jeder Teilverkauf wird ein eigener abgeschlossener Datensatz mit partialExitOf, der Rest bleibt offen; der letzte schließt die Position). Bei jedem Verkauf fragt der Block „🧠 Zum Verkauf": Verkaufsgrund und „Nach Plan gehandelt?" (Pflicht), Verfassung, Fehler nach Minervini (Stop nach unten verschoben, Verlust laufen lassen, zu weit über dem Pivot gekauft …) und Lektion (freiwillig) — gespeichert in verkauf. These, Kategorie und Verfassung gehen auf den Teilverkaufs-Datensatz mit über.

Stop-Historie: jede Stop-Änderung in der Positionsbearbeitung wird mit Datum in stopHistorie festgehalten. Bei Alt-Trades mit geschätztem Stop weist der Dialog darauf hin, den echten Stop einzutragen — dieser erste echte Stop wird als „echter Stop gesetzt" vermerkt (von: null), nicht als Verschiebung. Die Kategorie lässt sich dort ebenfalls ändern.

📂 Trade-Akte: das 📂 neben dem Ticker (offene und abgeschlossene Positionen) öffnet alle Angaben zu einem Trade auf einer Seite, gegliedert in Kauf (Datum, Ø-/Erstkauf-Kurs, Stück, Quelle, Setup, Muster, SEPA-Score, Pivot-Abstand, Verfassung, Signalzustand, These, Notiz), Risiko (Stop, Ziel, Stop-Verlauf mit „⬇ nach unten"-Markierung), Käufe, Verkäufe (jeder Teilverkauf mit Reflexion), Ergebnis (realisiert, offen, R-Vielfaches, Haltedauer) und Journal. Leere Felder werden weggelassen. Ein Teilverkauf öffnet die Akte seiner Position. Darunter „✏️ Bearbeiten" (nur offen) und „📔 Journal" (offen und geschlossen).

📷 Charts & Belege: Am Ende der Akte hängst du Chart-Screenshots und Belege an, jeweils mit Phase: 📌 Plan, 🛒 Kauf, 📈 Verlauf, ✅ Verkauf oder 🔍 Nachher. Das Format ist egal (PNG, JPG, HEIC, WebP, PDF). Bilder werden im Browser auf höchstens 2400 px verkleinert und als WebP bzw. JPEG gespeichert, von PDFs entsteht ein Vorschaubild der ersten Seite. Am Mac geht auch ⌘V: Der Screenshot landet in der gerade gewählten Phase. Antippen zeigt das Bild groß, ein PDF öffnet sich in einem neuen Tab. Auch der Trade-Planner („📷 Charts zum Trade", Phase Plan oder Kauf) und der Verkaufsbereich im Positionsdialog („📷 Exit-Chart") nehmen Bilder an. Was im Planer hängt, geht mit „Als Plan speichern" an den Plan und mit „Trade jetzt anlegen" an den Trade.

🗒️ Verlauf: Kurze datierte Notizen während der Haltezeit, zum Beispiel „Stop auf Einstand" oder „Earnings gehalten, weil …". Sie stehen in der Akte und im CSV-Export (Spalten „Verlaufsnotizen" und „Anhänge").

Wo die Bilder liegen: im privaten R2-Bucket mts-journal hinter dem Worker (/journal-anhang), nicht im Gist und nie auf Pages. Im Trade steht nur ein Verweis. Zugriff nur mit dem Gist-Token gh_gist_token: Der Worker fragt bei GitHub nach, wem der Token gehört, und lässt nur das eigene Konto durch. Ohne eingerichteten Token auf dem Gerät gibt es deshalb keine Bilder. Wer einen Anhang entfernt, löscht die Datei mit, sofern kein anderer Trade oder Plan sie noch nutzt.

🏷️ Kategorien (Sammelliste): Knopf über den offenen Positionen — listet alle offenen Positionen, die ohne Kategorie zuerst, je Zeile „📈 Trading" / „💼 Investment"; ein Tipp speichert sofort. Die Kategorie erscheint als Badge neben dem Ticker in beiden Positionstabellen.

Filtern & Sortieren der offenen Positionen: Über der Tabelle stehen Chips Alle · 📈 Trading · 💼 Investment · ❔ ohne (mit Anzahl je Kategorie); der Filter gilt für die Einzel- und die aggregierte Ansicht, die Überschrift zeigt dann „(x von y)", und die Wahl wird pro Gerät gemerkt. Liegt eine ausgeblendete Position unter ihrem Stop, erscheint daneben ein roter Hinweis — ein Stop-Alarm verschwindet nie durch den Filter. Daneben die Auswahl Sortieren (Datum, Ticker, Kategorie, P&L %/€, R akt., SL-Abstand, Pos.-Wert, Risiko, Konto) mit Pfeil zum Umkehren — bequemer am iPhone als die Spaltenköpfe, die weiterhin ebenfalls sortieren (neu auch „R akt." und „SL-Abst."; SL-Abstand zeigt zuerst die Positionen am nächsten am Stop).

↕️ Sortieren in beiden Ansichten der offenen Positionen: Einzelansicht und „📊 Aggregiert" (eine Zeile je Ticker, Ø-Kaufkurs über alle Positionen) lassen sich per Tipp auf den Spaltenkopf sortieren, ein zweiter Tipp dreht die Richtung um. Aggregiert: Ticker, Ø-Kurs, Stück, Kurs, Positionswert, P&L in €, P&L in % und Status (sortiert nach Depotanteil). Beide Ansichten merken sich ihre Sortierung getrennt, auch beim Hin- und Herwechseln (seit 2026-09-17; vorher war „Aggregiert" fest nach Ticker sortiert, und die Einzelansicht setzte die Markierung beim Neuzeichnen auf „Datum" zurück).

📊 Abgeschlossene Trades aggregiert: derselbe Umschalter „📊 Aggregiert / 📋 Einzeln" über den abgeschlossenen Trades fasst alle Verkäufe eines Tickers zu einer Zeile zusammen — Anzahl Verkäufe (Teilverkäufe zählen einzeln), Ø Kauf- und Ø Verkaufskurs (mengengewichtet), Stück, P&L in €, P&L in % (auf den gesamten Einstand), Treffer (Gewinner/Verkäufe) und letzter Verkauf. Alle Spalten sind sortierbar, Standard ist „letzter Verkauf, neueste zuerst". Die Ansicht ist unabhängig von der Ansicht der offenen Positionen (seit 2026-09-17).

P&L % in der Einzelansicht der abgeschlossenen Trades (sortierbare Spalte neben P&L): realisierter G/V ÷ Einstand (Kaufkurs × Stück) — dieselbe Basis wie in der aggregierten Ansicht. So erscheinen auch Knockouts ohne Exit-Kurs korrekt (rund −100 %), und im G/V aus Numbers enthaltene Gebühren fließen mit ein, weshalb der Wert leicht von (Exit − Entry) ÷ Entry abweichen kann.

📱 Portfolio-Tab am iPhone war ~1500 px breit (behoben 2026-09-17). Die Benchmark-Tabelle zog als Grid-Kind ohne min-width:0 alle Karten des Tabs auf ihre volle Breite, zusätzlich brach die Knopfleiste über den offenen Positionen nicht um. Folge: vom Portfolio aus geöffnete Dialoge lagen außerhalb des sichtbaren Bereichs. Fix: .grid > * { min-width:0 } und umbrechende Knopfleiste. tests/smoke-audit.js prüft nur den Start-Tab und hatte das deshalb nicht gemeldet.

⬇️ CSV-Export für Numbers (Portfolio → Abgeschlossene Trades → „CSV Export"): alle Datensätze (offen, geschlossen, Teilverkäufe) im deutschen Format — Semikolon, Dezimalkomma, TT.MM.JJJJ, UTF-8 mit BOM — mit 47 Spalten inkl. Kategorie, Ø-/Erstkauf-Kurs, Nachkäufen, R-Vielfachem, Haltedauer, Verfassung, Verkaufsgrund, Regel eingehalten, Fehler, Lektion und Journal. Zum Auswerten in Numbers gedacht, nicht zum Wieder-Import (Sicherung bleibt der JSON-Export).

📥 Numbers-Import nur noch als Übernahme/Nachtrag: bekannte Trade-Nummern werden jetzt aktualisiert (Verkauf, G/V, Stückzahl, leere Stammdaten), neue angehängt. Was im MTS erfasst wurde (echter Stop, Nach-/Teilverkäufe, Journal, Kategorie, Verkaufs-Reflexion), bleibt unangetastet. Wurde eine Position in Numbers verkauft, im MTS aber schon teil- oder nachgekauft, löst der Import das nicht selbst auf, sondern nennt sie in der Ergebnismeldung zum Schließen von Hand.

🔄 Geräte-Abgleich pro Trade statt pro Gesamtstand (seit 2026-09-17). Bisher gewann beim Abgleich über Gist und Mac mini der komplette Datenstand mit dem jüngeren Speicherzeitpunkt — ein am iPhone erfasster Teilverkauf konnte von einer kurz darauf am Mac gespeicherten Stop-Änderung lautlos überschrieben werden. Jetzt bekommt jeder geänderte Trade beim Speichern ein updatedAt, gelöschte einen Löschvermerk (_meta.geloeschteTrades, 180 Tage). Beim Laden und vor jedem Gist-Schreiben werden beide Stände Trade für Trade zusammengeführt (_mtsMergeDaten). Kursabrufe zählen nicht als Änderung. Seit 2026-09-23 wird auch die Watchlist pro Eintrag abgeglichen (Schlüssel Ticker, jüngeres ts gewinnt, Löschvermerk in _meta.geloeschteWatchlist), weil das Kauf-Review per „👀 Beobachten“ von jedem Gerät aus hineinschreibt. Depots, Einstellungen und Pläne folgen weiter dem jüngeren Gesamtstand. Test: node tests/test-trade-verwaltung.js.

Risiko-Management & Regeln

🎯 Minervini Risiko-Cockpit (Trade-Planner): eine zentrale Sizing-Logik (_mtsSuggestedSize) statt der früher drei parallelen Rechner (Risk-Tab, Trade-Formular, Control-Center). Formel: Max-Risiko€ = Kontostand des gewählten Depots × Risiko-% (Risiko-Registry: risikoProTrade, maxPositions, maxSectorExposure, Tages-/Wochen-/Monats-Risikolimit) ÷ |Entry − Stop| — Sizing rechnet pro einzelnem Depot (nicht mehr gegen die Summe aller Depots), Kill-Switch und Exposure-Deckel bleiben bewusst portfolioweit. Das Cockpit zeigt zusätzlich die Auswirkung aufs Gesamtdepot (Depot-Anteil, Portfolio-Anteil, verbleibende Exposure-Headroom), eine Kill-Switch-/Exposure-Ampel und eine Liste aller offenen Pyramiding-Positionen mit Stufen-Status. Der Trade-Planner hat dafür ein eigenes Depot-Feld (tf-depot) — vorher bekamen manuell angelegte Trades gar kein Depot zugewiesen, wodurch der Formular-Rechner zwangsläufig gegen den gepoolten Gesamtwert rechnete. Das Control-Center verlinkt pro Depot direkt in den Trade-Planner (vorausgewählt), statt eine eigene Formel zu pflegen.

🔍 SEPA-Analyse (Trade-Planner, zweiter Sub-Tab neben Risiko-Cockpit & Trade-Planner — seit 2026-07-23 aus minervini-coach-2.html hierher zusammengeführt, bewusste Teil-Rücknahme der früheren Entflechtung nur für dieses Sub-Feature; Pyramidisieren, Academy, Mental und Quiz bleiben im Coach): TradingView-Chart, 23-Punkte-Checkliste mit Ampel (Trend-Template 8 Kriterien aus SMA50/150/200 + 52-Wochen-Hoch/Tief, Fundamentals & Volumen — EPS-Wachstum > 25 %, Umsatzwachstum > 20 % (jüngstes Quartal ggü. Vorjahresquartal) und 🥇 Code 33 automatisch über den Worker-Endpunkt /fundamentals, dieselben Zahlen wie in der Setup-Analyse; Volumen und RSI aus dem Kursverlauf —, Chart Pattern & Setup — Stage-2-Base/VCP-Verengung/Konsolidierungsdauer/Breakout-Volumen automatisch erkannt, Logik aus Signal-Hub/src/pivot.py portiert —, Markt-Stage via S&P 500/SPY vs. SMA200), Batch-Screener (44-Ticker-Universum oder eigene Watchlist) und Watchlist (data.watchlist, Teil von mts_v2 — automatisch per Server-/Gist-Sync gesichert, Karte „📋 SEPA-Watchlist · Fokusliste“; seit 2026-09-23 landen dort auch die per „👀 Beobachten“ im Kauf-Review gesetzten Werte, erkennbar am 👀 statt der Ampel und mit Pivot/Stop statt Score; eine spätere SEPA-Analyse desselben Tickers ergänzt den Eintrag, statt Pivot/Stop zu verwerfen). Preis-Historie: lokaler MTS-Server zuerst (/history, wie ATR-Stop-Rechner/Verkaufs-Check), sonst Yahoo über die bestehende CORS-Proxy-Kette (_yahooJson/MTS_PROXY) — funktioniert dadurch auch ohne laufenden Mac mini. „📌 In Trade-Planner übernehmen" füllt Ticker/Entry/Stop (Entry −8 %) direkt ins Risiko-Cockpit und wechselt dorthin — die Positionsgrößen-Berechnung läuft über die ohnehin vorhandene _mtsSuggestedSize (Kill-Switch, Exposure-Deckel, Max-Position-%), nicht über einen separaten Rechner. Alte Coach-Links (minervini-coach-2.html?ticker=…/?page=sepa) leiten dauerhaft hierher um. Ein Aufruf mit ?ticker=… (z. B. aus der Setup-Analyse) startet die Analyse und springt direkt zur Karte „Aktie analysieren“ — am iPhone muss man nicht erst am Trade-Planner vorbeiscrollen.

🩹 Zwei SEPA-Checklisten-Fehler korrigiert (2026-08-29, SEPA/VCP-Methodikabgleich). ① „Operating Margin > 15%" (Fundamentals & Volumen, Punkt 3) nannte fälschlich eine absolute Schwelle — Minervini verlangt keinen bestimmten Margen-Level, sondern eine expandierende Marge Quartal über Quartal. Text korrigiert auf „Operating Margin expandiert (QoQ) — wächst gegenüber dem Vorquartal". Diese Checkbox hatte ohnehin nie eine Autofill-Logik (anders als z. B. Trend-Template/Chart-Pattern) — sie ist wie die übrigen vier Punkte des Fundamentals-Blocks (EPS/Revenue Growth, Volume Spike, RSI ausgenommen) eine rein manuelle Einschätzung, es gab also keinen Berechnungscode zu korrigieren. Seit 2026-09-30 ist dieser Margen-Punkt durch 🥇 Code 33 ersetzt (enthält die Margenausweitung, Nettomarge +2 Pp. ggü. 2–3 Quartalen zuvor), und EPS-/Umsatzwachstum werden automatisch befüllt — siehe oben. ② „Position Size berechnet — max. 2–3% Portfolio-Risiko" (Risk Management, Punkt 2) widersprach Minervinis tatsächlicher Regel und dem eigenen App-Default (risikoProTrade: 1, siehe Risiko-Cockpit oben) — korrigiert auf „max. 1–2% Portfolio-Risiko", konsistent mit den übrigen sechs Stellen in der App, die bereits korrekt 1–2 % zeigten.

📉 Drawdown-Kill-Switch (Dashboard, „Drawdown-Monitor"): wertet realisierte Trades nach Tages-/Wochen-/Monats-Fenstern aus. Tages-Drawdown ≤ −3 % → 🔴 Handelsstopp (Positionsgrößen-Rechner blockiert, Trade-Formular fragt beim Speichern noch einmal nach), Wochen-Drawdown ≤ −6 % → 🟡 Positionsgröße automatisch auf 50 % reduziert, Monats-Drawdown ≤ −10 % → 🔴 kompletter Reset (ebenfalls Rückfrage vor neuen Trades). Zusätzlich zeigt das Widget die klassische Drawdown-Kurve (aktuelle/maximale Distanz zum Equity-Hoch) als Underwater-Chart. Bugfix 2026-08-03: Das Dashboard-Widget (assets/mts-extensions.js::renderDrawdownMonitor — überschreibt per <script defer> eine ältere, inzwischen entfernte Kopie gleichen Namens in der Hauptdatei, die dadurch nie ausgeführt wurde) rechnete die Prozent-Basis fälschlich nur aus dem reinen Kontostand statt aus Gesamtkapital (Kontostand + Depotwert). Bei jedem Konto mit nennenswertem kumuliertem Gewinn wurde dadurch die Basis rechnerisch auf 0 geklemmt — die Kurve lief flach, die Ampel sprang fälschlich auf 🔴 „Stopp" und die Positionsgrößen-Empfehlung auf 0–25 %, obwohl real ein harmloser einstelliger Drawdown vorlag. Betraf nur die Dashboard-Anzeige — der echte Kill-Switch fürs Trade-Formular (_mtsDrawdownState, siehe oben) nutzte bereits korrekt das Gesamtkapital und war nie betroffen. Pro Depot oder gesamt (seit 2026-08-03): Ein Dropdown neben der Überschrift („Alle Depots (konsolidiert)" + jedes einzelne Depot, identisches Muster wie der Depot-Filter im Portfolio-Tab) schaltet Kurve, Ampel, Kennzahlen und Gesamtkapital-Basis auf ein einzelnes Depot um — dessen eigene Trades und dessen eigenes Gesamtkapital (_mtsDepotBalance), nicht die Summe aller. Ohne Angabe liest renderDrawdownMonitor() die aktuelle Dropdown-Auswahl selbst aus, damit automatische Re-Renders (Kurs-Refresh, Tab-Wechsel) die Auswahl nicht zurücksetzen.

🔺 Progressive Exposure (Pyramiding): Beim Anlegen eines Trades kann „Progressive Exposure nutzen" aktiviert werden — Zielgröße eingeben, die App setzt automatisch die initiale Position auf 25 %. In der Positionsbearbeitung erscheint dann, sobald die Position im Plus ist, ein „Nachkaufen"-Baustein: +25 % ab +2 % Gewinn (Stufe 2/3), +50 % final ab +5 % Gewinn (Stufe 3/3) — jeweils nur aktiv, wenn die Schwelle erreicht ist. Entry, Positionsgröße und Risiko werden nach jedem Nachkauf automatisch als gewichteter Durchschnitt neu berechnet.

🎯 Gestaffelter Ausstieg (Teilverkäufe erfassen) (seit 2026-07-30, Gegenstück zum Pyramid-Nachkauf): in der Positionsbearbeitung erscheint bei offenen Positionen der Block „🎯 Gestaffelter Ausstieg" mit Vorschlag für die nächste Verkaufsstufe (Minervini 50/25/25: Stufe 1 bei T1/+8 %, Stufe 2 bei +20 %, Stufe 3 = Restposition bei +40 %) inkl. Kurs- und Stückzahl-Vorschlag sowie Historie bereits erfasster Teilverkäufe. „✅ Teilverkauf erfassen" (addPartialExit) reduziert die offene Position um die verkaufte Stückzahl — technisch spaltet jeder Teilverkauf, der die Position nicht vollständig schließt, einen eigenen, ganz normalen geschlossenen Trade-Datensatz ab (Feld partialExitOf verweist auf die Ursprungsposition, exitStage zählt mit). Grund: Kelly-Kriterium, R-Multiple-Verteilung, Steuerjahr-Zuordnung und alle „Performance nach …"-Auswertungen gehen von „ein Trade = ein Exit" aus — ein Teilverkauf als eigener geschlossener Trade zählt dort automatisch korrekt mit, ohne dass diese Auswertungen etwas von Teilverkäufen wissen müssen. Der letzte Teilverkauf (Restposition = 0) schließt stattdessen die Ursprungsposition direkt, exakt wie ein normaler Vollverkauf.

🚫 SEPA-Verbotsliste: Vor jeder neuen offenen Position muss eine Pflicht-Checkliste bestätigt werden (Kurs über 50-SMA, positive relative Stärke, Trade mit dem Trend, Kauf am Pivot statt auf Verdacht) — ohne alle vier Häkchen speichert die App nicht. Zusätzlich erkennt sie automatisch Averaging-Down (Nachkauf in eine bereits offene Position zu einem Kurs unterhalb des bisherigen Einstiegs) und fragt vor dem Speichern ausdrücklich nach — Minervinis absolute Regel: niemals Verlustpositionen aufstocken. Pyramiding (siehe oben) ist davon nicht betroffen, da es zwingend Gewinn voraussetzt.

⛔ Minervini 50/80-Regel (seit 2026-07-22, _mtsLoadGap80Cache/_mtsGap80Info): die Hauptapp lädt beim Start zusätzlich Signal-Hub/data/signals.json nach (rein lesend, relativer Pfad — funktioniert nur, wenn beide Apps vom selben Server ausgeliefert werden, z.B. 8090; scheitert sonst bewusst still, das Feature ist rein zusätzlich). Drei Stellen nutzen das: ① „+ Neuer Trade"/Risiko-Cockpit/Trade-Planner zeigen bei einem 50/80-belasteten Ticker automatisch eine Warnung in der Live-Vorschau und als Bestätigungs-Dialog beim Speichern — dieselbe Quelle wie Stop-Abstand/CRV (_mtsPreTradeWarnings), kein separater Dialog. ② Die Positionsgrößen-Empfehlung (_mtsSuggestedSize) halbiert sich automatisch für 50/80-belastete Ticker — analog zur bestehenden Drawdown-Reduktion, keine neue Sizing-Formel. ③ In der Positionsliste (Portfolio, einzeln & aggregiert) markiert ein Badge „⛔50/80" bestehende offene Positionen, die selbst so einen Gap-down hatten — Warnung vor dem Nachkaufen zum Senken des Durchschnittskurses, bevor überhaupt ein neuer Trade angelegt wird. Bewusst kein Kelly-Eingriff: Kelly „Was-wäre-wenn" (siehe unten) rechnet mit den realen R-Multiples der eigenen Trade-Historie, nicht pro Ticker — dafür gibt es keinen sinnvollen Anknüpfungspunkt für eine Ticker-spezifische Regel, die Positionsgrößen-Halbierung deckt denselben Zweck (konservativer bei erhöhtem Risiko) ab.

🛡️ Minervini-Plausibilitätsprüfung (Stop-Abstand · CRV · Positions-Deckel · Limits): Vier weitere Prüfungen, gespeist aus einer gemeinsamen Quelle (_mtsPreTradeWarnings bzw. Risiko-Registry), erscheinen live als Warnbox im „+ Neuer Trade"-Formular und beim Speichern als Bestätigungs-Dialog (analog Markt-Gate): ① Stop-Abstand > 10 % unter Entry (Minervini: max. 10 %, Ideal 7–8 %), ② CRV < 2:1 (sofern Target gesetzt; die CRV-Vorschau färbt sich grün/orange), ③ Positions-Deckel: der Positionsgrößen-Rechner kappt die Empfehlung zusätzlich auf max. maxPositionPct (Standard 25 %) des Depotwerts — verhindert, dass sehr enge Stops rechnerisch das ganze Depot in eine Position schicken, ④ Positionslimit & Sektor-Klumpenrisiko: die bislang ungenutzten Registry-Werte maxPositions (Standard 5 verschiedene Ticker; Aufstocken bestehender Ticker bleibt frei) und maxSectorExposure (Standard 30 % des Gesamtdepots je Sektor, Basis Einstandswert) werden jetzt beim Speichern geprüft. Das Risiko-Cockpit zeigt dazu eine dritte Ampel-Kachel „Positionslimit" und dieselben Stop-Warnungen.

✋ Pre-Trade-Prüfer (Claude Code, /pre-trade TICKER [Einstieg] [Stop] [Trading|Investment]) (seit 2026-09-17, agenten/pre_trade_check.py, liest mts_data.json nur): Prüfung vor der Order gegen die eigenen Regeln, als Ergänzung zu den Warnungen im Trade-Formular (die erst beim Anlegen greifen). Drei Stufen je Punkt — ⛔ Regelverstoß, ⚠️ bewusst entscheiden, ✅: Marktampel (Rot = ⛔, eigene Regel „bei Rot keine Käufe"; Gelb oder Distribution-Day-Warnung = ⚠️), Stop (fehlt oder > 10 % = ⛔, > 8 % = ⚠️), Kategorie Trading/Investment ist Pflicht (⛔ ohne), Positionsgröße nach risikoProTrade mit Wechselkurs in €, gedeckelt auf maxPositionPct, Risiko-Budget Woche/Monat (nur echtes, nicht synthetisches Risiko), Nachkauf unter Einstand (⛔, Abgleich per Fingerabdruck über Ticker/Yahoo-Symbol/ISIN/Name), offene Trading-Positionen gegen maxPositions und Altlasten unter Stop, Earnings innerhalb warn_tage, Einstieg > 5 % über dem Pivot. Einstieg/Stop kommen ohne Angabe aus dem geplanten Trade der Hauptapp, sonst aus der Pivot-Engine. Ergebnis ist „Regelkonform" oder „Regelverstoß" — keine Kaufempfehlung (dafür /setup). Bei „Regelkonform" folgt die Stop-Zusage: Stop sofort als Order beim Broker hinterlegen, Trade mit echtem Stop anlegen.

🪞 Wochen-Rückblick (sonntags 16:00 UTC, Cloud-Routine „MTS Wochen-Rückblick (Cloud, sonntags)“ + agenten/wochen_rueckblick.py) (seit 2026-09-17, rein lesend): stellt jede Kalenderwoche Bauen (Commits in Root, Signal-Hub, Price-Action-Hub, Rotation-Dashboard) gegen Traden (Käufe mit echtem Stop und Kategorie, Verkäufe, ausgefüllte Reflexion), prüft die Stop-Disziplin bei Verlust-Verkäufen (gehalten bis 1,2 R; bei Trades ohne echtes Risiko bis 10 % Verlust), zählt nachgezogene Stops und die offenen Trading-Positionen unter ihrem Stop und zeigt den Verlauf der letzten vier Wochen. Push aufs iPhone mit reinen Zahlen (keine Titel, keine Beträge), danach ordnet Claude die Woche in 3–5 Sätzen ein und stellt eine Frage für die kommende Woche; am ersten Sonntag im Monat hängt es zusätzlich /rueckblick für den Vormonat an. Läuft seit 2026-10-02 als Cloud-Routine, unabhängig vom Mac mini (vorher Claude-Desktop-Aufgabe dort — fiel aus, wenn der Mac aus war). Die Trades kommen aus dem geheimen Gist der Hauptapp (--quelle gist), nicht aus der lokalen mts_data.json: die aktualisiert sich nur beim Öffnen der Hauptapp über den Mac-mini-Server und war sonst wochenlang veraltet. Der Bericht nennt deshalb den „Stand“ der Trade-Daten. Die Gist-ID ist das Geheimnis (ein geheimer Gist ist mit der ID allein lesbar) und steht nur als Umgebungsvariable MTS_GIST_ID in der eigenen Cloud-Umgebung „MTS-Rückblick“, zusammen mit NTFY_THEMA; Netzwerkzugriff dort: Standardliste plus ntfy.sh, api.github.com, gist.githubusercontent.com. Die Claude-GitHub-App ist dafür auf maick-trading-system installiert, die Skills unter .claude/skills/ sind versioniert. Selbst aufrufen: python3 agenten/wochen_rueckblick.py [--bis JJJJ-MM-TT] [--quelle gist] (für --quelle gist MTS_GIST_ID setzen).

🛠️ Kontostand-0-Fehlalarme nach CSV-Import behoben (seit 2026-08-24, Nutzerprüfung der CSV-Import-Logik). Zwei Stellen im Portfolio-Tab rechneten mit data.portfolio.kontostand, ohne den Fall „noch kein Kontostand eingetragen" abzufangen — genau der Zustand direkt nach einem CSV-Import (Numbers-Journal), solange der Kontostand noch nicht manuell nachgetragen wurde: ① die Alertbox „⛔ Gesamtrisiko > 5 % des Depots" (renderAlerts) verglich stats.totalRisk gegen eine Schwelle von Kontostand × 5 % — bei Kontostand 0 ist die Schwelle selbst 0, wodurch die Warnung bei jeder offenen Position fälschlich feuerte; ② der Benchmark-Vergleich (renderBenchmark) teilte den YTD-Gewinn durch einen ||1-Fallback statt durch den echten Kontostand und zeigte dadurch rohe Euro-Beträge als absurde Prozentwerte (z. B. 300 € Gewinn als „30000 %"), ebenso die Jahres-Performance-Tabelle darunter. Beide Stellen zeigen jetzt „—"/bleiben stumm, bis ein echter Kontostand vorliegt — dieselbe Absicherung, die das Cockpit-Terminal-Radial-Tacho (siehe „Risiko-Budget" oben) bereits hatte.

Verkaufsdisziplin

🔍 Verkaufs-Check (Sell-Coach): Knopf über den offenen Positionen. Prüft je Position die Minervini-Ausstiegsregeln aus Tageskursen (vom /history-Endpunkt) und zeigt eine Ampel in der Spalte „🔍 Verkauf": 🔴 Verkauf prüfen bei hartem Signal (Bruch der 50-Tage-Linie auf erhöhtem Volumen oder Kurs unter der 200-Tage-Linie = Stage-2-Ende), 🟡 Beobachten bei weichem Signal (Schluss unter 50-Tage-Linie, größter Tagesverlust seit Einstieg, Bruch des jüngsten Swing-Tiefs oder > 15 % unter dem jüngsten Hoch), sonst 🟢 Halten. Die Begründungen stehen im Tooltip der Ampel. Das ist das Gegenstück zur SEPA-Kaufprüfung im Coach — die Verkaufsdisziplin, wo bei Minervini die Rendite entschieden wird. Der Check schlägt zusätzlich einen nachgezogenen Stop-Loss vor (⤴ Stop → …): ab +8 % Break-Even, ab +20 % unter die 50-Tage-Linie bzw. das jüngste Swing-Tief — nur anhebend, nie senkend. Ein Klick übernimmt den neuen Stop (mit Rückfrage).

🔵 Stärke-Signal „Teilgewinn in Stärke" (seit 2026-07-24): Der Verkaufs-Check war bisher rein defensiv (reagiert auf Schwäche). Minervini verkauft aber genauso in die Stärke. Der Check zeigt daher zusätzlich ein blaues Signal, wenn eine Position im Gewinn überdehnt läuft — Auslöser (einzeln oder kombiniert): > 12 % über der 20-Tage-Linie, +25 % Lauf in 10 Handelstagen, oder Klimax-Volumen (> 2× Ø-Volumen an einem starken Aufwärtstag). Das Signal ist additiv zur defensiven Ampel und erscheint nie bei hartem Verkaufssignal (🔴, dann will man ganz raus). Die Abschlussmeldung des Checks zählt die Stärke-Kandidaten mit.

R akt. (Live-R-Vielfaches je offener Position) (seit 2026-07-24): Neue Spalte in der Positionsliste (zwischen P&L% und SL-Abst.) — offener Gewinn/Verlust ÷ anfängliches Risiko (€), also die risiko-normalisierte Sicht auf jede laufende Position. Nutzt den bereits gespeicherten currentPrice (kein zusätzlicher Kursabruf nötig, anders als der Verkaufs-Check). Farbcodiert: ab +2R 💪 grün (Gewinnrealisieren-Zone, deckt sich mit dem 🔵 Stärke-Signal), 0…+2R blau, im Minus orange, ab −1R rot. Gegenstück zur R-Multiple-Verteilung, die dasselbe für geschlossene Trades zeigt.

📏 ATR-Stop-Rechner: Button „ATR-Stop berechnen" neben dem Stop-Loss-Feld holt Tageskerzen vom 8090-Server (/history, dieselbe Quelle wie der Verkaufs-Check) und errechnet die Average True Range über 14 Tage. Vorschlag: Stop = Entry − 2 × ATR — ein objektiver, volatilitätsbasierter Stop-Abstand als Alternative zur festen Prozent-Regel, mit „Übernehmen"-Knopf.

🎯 SEPA-Score-Snapshot: Das Trade-Formular hat ein Eingabefeld „SEPA-Score bei Kauf" (0–23) plus einen Knopf „🔍 SEPA-Analyse", der seit der Zusammenführung (siehe oben) direkt in den SEPA-Analyse-Subtab des Trade-Planners wechselt und die Analyse für den eingetragenen Ticker startet — kein externer Tab mehr. Von dort füllt „📌 In Trade-Planner übernehmen" den Score automatisch in tf-sepa-score zurück, sobald derselbe Ticker im Formular steht. Der Wert wird als Snapshot am Trade gespeichert und erscheint schreibgeschützt in der Positionsbearbeitung.

📐 Setup-Typ-Tag (seit 2026-07-22, nach einem Minervini-Private-Access-Q&A: „konsequent verschlagworten und auswerten"): Zweites Freitext-Analog-Feld neben „Quelle" — festes Dropdown „Setup-Typ" (Swing / Cheat-Pivot / Pyramid-Add / Test / Sonstige) im Trade-Formular. Wie der SEPA-Score ein reiner Entry-Snapshot (trade.setupTyp), schreibgeschützt in der Positionsbearbeitung sichtbar, nie nachträglich änderbar. Psychology-Tab wertet mit „📐 Performance nach Setup-Typ" aus (Trades/Win-Rate/Ø-CRV/P&L je Setup-Typ, gleiches Schema wie „Performance nach Quelle") — zeigt, welcher Entry-Stil beim eigenen Trading tatsächlich funktioniert.

📍 Entry-Timing-Snapshot (Pivot-Abstand): Videokern „right price, not lowest price" — beim Ausfüllen des Trade-Formulars zeigt eine Live-Hinweisbox, wie weit der eingetragene Kurs vom zuletzt bekannten Signal-Hub-Pivot (Signal-Hub/data/pivot.json, bisher ungenutzt) entfernt liegt, inkl. Pivot-Status (ARMED/BREAKOUT). Wird beim Speichern als Snapshot am Trade abgelegt (pivotEntry, pivotDistPctEntry) — nur wenn zum Zeitpunkt Pivot-Daten für den Ticker vorlagen, sonst bleibt das Feld leer (kein Pflichtfeld, scheitert still wie die 50/80-Cross-App-Anbindung). Schreibgeschützt in der Positionsbearbeitung sichtbar. Psychology-Tab wertet mit „📍 Win-Rate nach Pivot-Nähe" aus: Buckets ≤2 % / 2–7 % / >7 % über Pivot, je mit Trades/Win-Rate/P&L — beantwortet, ob nah am Pivot gekaufte Trades tatsächlich besser laufen als extended gekaufte.

🏷️ Positionsgrößen-Kategorie-Badge (Portfolio-Tab, offene Positionen): rein visuelle Einordnung Voll/Halb/Viertel/Test unter dem bestehenden „Offen"-Badge, abgeleitet aus dem bereits berechneten Depotanteil (depPct) relativ zu maxPositionPct (Standard 25 %, Minervini: 20–25 % = „Full Position"). Keine neue Berechnung, nur Sichtbarmachung der bestehenden Risiko-Registry-Kennzahl direkt in der Tabelle.

Planung & Auswertung

📌 Trade-Planner — ein Bildschirm für Durchrechnen UND Kaufen (zusammengelegt 2026-07-30 aus dem vorherigen Risiko-Cockpit + separatem „+ Neuer Trade"-Modal). Ein Ticker rein — aus dem Kandidaten-Scanner übernommen, per Screener-Deeplink oder manuell — und alles wird automatisch befüllt und berechnet: Firmenname, Entry/Stop, Targets T1/T2/T3 (Minervini 50/25/25: +8 %/+20 %/+40 %), SEPA-Trend-Ampel und SEPA-Verbotsliste (siehe unten), Pyramidisierung, gestaffelter Ausstiegsplan, Depot- & Portfolio-Auswirkung und das Sicherheitsnetz. Gliederung: Gesamtverdikt-Ampel → Risiko-Budget → Stammdaten → Preisplan & Targets → Größe & Risiko → Pyramidisierung → Gestaffelter Ausstieg → Depot-/Portfolio-Auswirkung → Sicherheitsnetz → SEPA-Verbotsliste. Unten zwei gleichwertige Buttons: „📌 Als Plan speichern" (unverbindlich, eigener Datenbereich planned) und „💾 Trade jetzt anlegen" (saveTrade(), alle Gates greifen normal). Gespeicherte Pläne erscheinen darunter mit einem „▶ Übernehmen"-Knopf; nach erfolgreichem „Trade jetzt anlegen" wird der zugehörige Plan automatisch entfernt. „Größe"-Feld ist eine echte Eingabe (Fix 2026-08-01): Positionswert, Risiko-€/%, die Target-Tabelle, Depot-/Portfolio-Auswirkung und der Ausstiegsplan rechneten bislang immer mit der automatischen Risiko-Empfehlung (r.suggested), egal was im „Größe"-Feld selbst stand — eine manuell eingetragene Stückzahl hatte keinerlei Effekt. Alle diese Blöcke nutzen jetzt r.actualSize (manueller Wert, falls >0 gesetzt, sonst weiterhin die Empfehlung); die „empfohlen: N"-Anzeige daneben bleibt unverändert die echte Empfehlung, damit sichtbar bleibt, wovon eine manuelle Größe abweicht. Die Pyramidisierungs-Vorschau folgt zusätzlich einer eigenen, manuell in „Zielgröße" eingetragenen Stückzahl, falls vorhanden.

📝 Pflichtfelder „Strategie/These" & „Notiz" (seit 2026-08-23, Systemprüfung). Bis dahin gab es im Trade-Planner gar kein Formularfeld für eine Begründung — strategie/notes waren nur beim CSV-Import befüllbar und lagen im Bestand entsprechend zu 87–93 % leer (siehe „🧪 Verschärfte Beleg-Regel" bzw. die Systemprüfung: „die Backtests messen das System, aber nichts misst den Trader"). Zwei neue Felder in „Stammdaten" — Strategie/These (kurzer Freitext, Datalist mit gängigen Setups) und Notiz — warum dieser Trade? (Textarea) — sind jetzt saveTrade()-Pflicht für jeden gespeicherten Trade, offen oder retroaktiv geschlossen: fehlt eines, bricht das Speichern mit Toast ab, das „Stammdaten"-Akkordeon klappt automatisch auf (war sonst wirkungslos, weil .focus() auf einem Element in einem geschlossenen <details> nichts bewirkt) und der Fokus springt ins fehlende Feld. Bewusst hart, kein Override wie beim Markt-/Drawdown-Gate — ein optionales Feld blieb hier nachweislich leer.

🔒 Signal-Fingerabdruck beim Kauf (seit 2026-08-28, _mtsSignalSnapshot, Feld trade.signalSnapshot): friert beim Anlegen eines Trades den Systemzustand dieses Tages ein — Score/Tier, Pivot-Status und Pivot-Qualität, Price-Action-Score, Konfluenz (quellen_unabhaengig), Kern-Setup-Markierung, Marktampel, Earnings-Abstand und die Trend-Template-Ampeln. Schließt die letzte Messlücke des Systems: die fünf Forward-Test-Kohorten messen, ob ein Signal recht hatte, aber nichts misst bisher, ob die Signale, denen tatsächlich gefolgt wird, in der eigenen Hand Geld verdienen. Datenquelle ist Signal-Hub/data/top_setups.json (winzig, enthält die bereits gekreuzte Schnittmenge) mit Rückfall auf signals.json + pivot.json für Werte außerhalb der Top-Setups; _mtsLoadGap80Cache heißt deshalb jetzt _mtsLoadSignalsCache und wertet dieselbe, ohnehin einmal geholte signals.json für Minervini-50/80 und den Fingerabdruck aus, statt sie zweimal zu ziehen. Im Trade-Planner zeigt eine zweite Zeile unter dem Pivot-Hinweis live, was gespeichert wird; im Trade-Detail steht es später als „🔒 Signalzustand beim Kauf".

Warum das Feld quelle Teil der Messung ist, nicht bloß eine Herkunftsnotiz. Der Fingerabdruck wird immer geschrieben, auch wenn kein Signal vorlag — genau diese Trades sind die Kontrollgruppe, ohne die sich nie belegen ließe, dass die Systemsignale überhaupt einen Unterschied machen. Unterschieden werden fünf Zustände: top_setups (Ticker stand in der Schnittmenge), signals (gescort, aber nicht in der Schnittmenge), nicht_gelistet (Signaldaten lagen vollständig vor, Ticker kam nicht darin vor — die echte Kontrollgruppe), nicht_verfuegbar (Signaldaten nicht ladbar, Zustand unbekannt — dieser Trade muss aus der Auswertung ausgeschlossen werden, statt fälschlich in der Kontrollgruppe zu landen) und nachgetragen (Trade-Datum liegt mehr als MTS_SNAPSHOT_MAX_DELTA_TAGE = 3 Tage vor dem Stand der Signaldaten — dann werden bewusst keine Werte eingefroren, weil heutige Werte nicht die vom Kauftag sind). Ein positiver Treffer bleibt auch dann belastbar, wenn nur top_setups.json geladen wurde; ein negativer Befund braucht zwingend die vollständige signals.json, sonst würde jeder Wert außerhalb der Schnittmenge fälschlich als „nicht gescort" gezählt. stand (Zeitstempel der Signaldaten) und standDeltaTage werden in jedem Fall mitgeschrieben, damit die spätere Auswertung strenger filtern kann als die App selbst. Rückwirkend nicht rekonstruierbar — die Signaldateien werden bei jedem Pipeline-Lauf überschrieben; die 252 Alt-Trades bleiben deshalb ohne Fingerabdruck, die Kohorte wächst erst ab jetzt. Test: node tests/test-signal-snapshot.js (42 Fälle, ohne Browser und ohne MTS.save()).
Zuordnung des eingetippten Titels (seit 2026-09-13, Roadmap-Punkt 7). Die Signaldaten sind nach System-Kürzeln geschlüsselt (DXCM, SAP.DE), Trades werden aber oft unter Namen („Dexcom“), WKN oder deutschem Listing (TL0.DE für Tesla) angelegt. Probe am 13.09. mit den 33 offenen Positionen: der alte, reine Ticker-Vergleich ordnete keine einzige zu — alle 33 wären Kontrollgruppe gewesen, obwohl Dexcom, Arista, Robinhood oder Hims & Hers im Signal-Hub gescort waren. Damit wäre genau der Vergleich verfälscht worden, für den der Fingerabdruck existiert. Jetzt prüft _mtsSignalSnapshot(ticker, datum, name) exakte Kandidaten in fester Reihenfolge — im Formular bestätigte Zuordnung, eingetippter Ticker, früher aufgelöstes yahooSymbol desselben Tickers, guessTicker() für Name und Ticker — jeweils gegen Ticker und yahoo_symbol der Signaldaten. Der Weg steht in signalSnapshot.zuordnung.weg (ticker, bekanntes_symbol, ticker_map, bestaetigt). Ergebnis der Probe: 8 Treffer, 12 nicht zuordenbar, 13 Kontrollgruppe.
Neuer sechster Zustand nicht_zuordenbar: kein exakter Treffer, aber entweder passt der Name zu einem System-Titel (Vergleich der ersten zwei Kernwörter, Umlaute und Zusätze wie „Inc.“ ignoriert) oder die Eingabe ist gar kein Börsenkürzel (WKN, ISIN, Derivat). Solche Trades fallen aus der Kontrollgruppe heraus statt sie zu verfälschen. Passt der Name, zeigt der Trade-Planner Knöpfe wie „✓ TSLA · Tesla, Inc. · Top-Setup“ und „Kein Signal-Titel“ — nie automatisch, weil ein Namensabgleich auch Fremdes trifft (in der Probe landete der iShares Aerospace & Defence ETF bei GE Aerospace). Die Bestätigung ändert nicht das Trade-Kürzel (die Kursabfrage bleibt z. B. in EUR auf TL0.DE), nur den Vergleichsschlüssel, und gilt ausschließlich für den Ticker, für den sie gesetzt wurde. nicht_gelistet (Kontrollgruppe) gibt es nur noch, wenn ein eindeutiges Börsenkürzel ohne Treffer und ohne Namensvorschlag vorliegt (symbol_ohne_treffer) oder „Kein Signal-Titel“ bestätigt wurde. Die Auswertung im rueckblick-Skill schließt nicht_zuordenbar aus allen Gruppen aus und führt ältere Snapshots ohne zuordnung getrennt.

🔎 Ticker-Autofill (_tfAutofillFromTicker): Ticker-Feld verlassen (oder Enter) → fehlende Felder werden nachgeladen, ohne je einen bereits gesetzten Wert zu überschreiben (Deeplink- oder Handeingaben haben immer Vorrang). Preis bevorzugt den Signal-Hub-Pivot/Basis-Tief (_mtsPivotInfo), sonst Fallback letzter Kurs (Kurs / −8 % / +8 %). Der Firmenname kommt aus derselben Yahoo-Chart-Antwort, die ohnehin für den Kurs abgerufen wird (meta.longName/shortName). Technisch teilen sich alle Bausteine dieselben Render-Funktionen (_renderImpactBlock, _renderAmpelBlock, _renderRRScenarios, _renderPyramidPreview, _renderExitPlanPreview) statt eigener Kopien. „🔄 Aktualisieren"-Knopf neben dem Ticker-Feld (seit 2026-08-01): das bewusste „nie überschreiben" ist beim erstmaligen Ticker-Eintippen richtig, wird aber zum Problem, wenn im selben, schon befüllten Trade-Planner ein anderer Ticker eingetragen wird — Entry/Stop/Target/Name blieben dann vom vorherigen Ticker stehen. Der Knopf ruft _tfAutofillFromTicker(true) auf und überschreibt bewusst alles (inkl. SEPA-Trend/Verbotsliste, dafür wird deren Cache für diesen Ticker geleert). Dabei aufgefallen und mitbehoben: _sepaFetchHistory() liefert bei einem Treffer über den lokalen Mac-mini-Proxy (source:'MTS-Server') keinen Firmennamen mit (/history kennt kein Namensfeld) — nur der Yahoo-Fallback (source:'Yahoo') bringt meta.longName mit. Der Autofill holt den Namen in diesem Fall jetzt gezielt per zusätzlichem, leichtem _yahooFetchHistory()-Aufruf nach, statt ihn leer zu lassen.

🎯 Gesamt-R:R statt reiner T1-CRV (_computeBlendedRR, seit 2026-07-30): die CRV-Kennzahl bezog sich zuvor nur auf Target 1 — bei Minervini bewusst nah am Entry („Risiko finanzieren"), wodurch die reine T1-CRV fast immer nahe 1:1 liegt und als alleinige Kennzahl systematisch falsch gewarnt hätte. Jetzt zählt überall (Kopf-Kachel, Warnschwelle <2:1, Gesamtverdikt) das gewichtete Gesamt-R:R über T1–T3 (50/25/25-Gewichtung, dieselbe Formel wie im Ziel-Szenario-Block). Direkt darüber steht die Gesamtverdikt-Ampel (_computeOverallVerdict): 🟢/🟡/🔴 aus dem schlechtesten Wert von Gesamt-R:R, Kill-Switch, Exposure-Deckel und Positionslimit — eine Zahl/Farbe statt vier Einzel-Ampeln getrennt lesen zu müssen.

⚖️ Duales Risiko-Limit (_mtsSuggestedSize, neue Settings risikoProTradePortfolio): zwei unabhängig einstellbare Prozent-Felder im Trade-Planner-Kopf — „Risiko-Limit dieses Depot" und „Risiko-Limit Gesamtportfolio" (beide leer = Standard aus den Risiko-Einstellungen, Session-Override, nichts wird dauerhaft gespeichert). Die empfohlene Positionsgröße nutzt automatisch die strengere der beiden Grenzen; ein Hinweistext zeigt, welche gerade bindet ("bindend: Depot/Gesamtportfolio, max. X €"). Beide Settings sind standardmäßig identisch, wodurch sich am bisherigen Verhalten nichts ändert, solange niemand den neuen Portfolio-Regler enger stellt.

🚦 Automatische SEPA-Trend-Ampel & Verbotsliste (_computeSepaTrendScore, _computeRelativeStrengthVsSpy, _autofillVerbotsliste): die 8 objektiven Trend-Template-Kriterien (Kurs/SMA50/150/200, 52-Wochen-Range — dieselbe Formel wie die manuelle SEPA-Analyse) werden beim Ticker-Autofill automatisch aus den ohnehin geladenen Kursdaten berechnet und als Kachel „SEPA-Trend N/8" angezeigt (grün ≥7, gelb 5–6, rot <5) mit Link zur vollen 23-Punkte-Analyse. Dieselben Daten füllen die SEPA-Verbotsliste vor: Punkt 1 (>50-SMA) und Punkt 3 (Trendausrichtung) aus den Trend-Kriterien, Punkt 4 (Pivot-Breakout) aus dem Signal-Hub-Pivot-Status, Punkt 2 (relative Stärke) aus einem echten 3-Monats-Kursvergleich gegen SPY (nicht approximiert). Die Checkboxen bleiben real anklickbar — die bewusste Bestätigung vor dem Kauf bleibt Pflicht, nur die Recherche entfällt.

🔍 Kandidaten-Scanner (oberhalb des Trade-Planners, window._scannerTickers): mehrere Ticker parallel grob durchrechnen, bevor man sich für einen entscheidet — Ticker eintippen, Zeile erscheint mit Entry/Stop, Gesamt-R:R, SEPA-Trend-Score, empfohlener Größe, Hinweisen (Pivot-Distanz, Minervini-50/80) und einer Ampel-Punkt-Gesamtbewertung. Bewusst nicht in mts_data.json persistiert (eigenständig von data.watchlist, den dauerhaft gemerkten SEPA-Analyse-Ergebnissen) — für spontane Testläufe/Session-Watchlists gedacht, nicht als zweite dauerhafte Watchlist. Zeile anklicken übernimmt den Ticker in den vollen Trade-Plan darunter (derselbe Autofill-Pfad — seit 2026-08-02 erzwungen wie beim „🔄 Aktualisieren"-Knopf, wenn es ein anderer Ticker als der zuletzt geladene ist: sonst blieben Entry/Stop/Name/Target vom vorherigen Kandidaten stehen, und eine dort manuell gesetzte Größe hätte sich fälschlich auf den neuen Kurs bezogen — deshalb wird beim Tickerwechsel zusätzlich „Größe"/„Zielgröße" geleert. Klickt man denselben, schon geladenen Kandidaten erneut an, bleibt eine laufende manuelle Größe unangetastet). Teure Abrufe (Kurs/Name/Trend) werden pro Ticker gecacht, Größe/CRV/Ampel werden bei jedem Rendern live neu berechnet (abhängig vom gewählten Depot und den Risiko-Limits). Seit 2026-08-01: auch der ?neuerTrade-Deeplink aus dem Signal-Hub trägt den Ticker automatisch in diese Liste ein (_addTickerToScanner, gemeinsamer Kern mit der manuellen Eingabe) — beim Durchklicken mehrerer Signal-Hub-Treffer baut sich so von selbst eine Watchlist zum Vergleichen auf. Da jeder Signal-Hub-Klick eine echte Seiten-Navigation ist (eigenes HTML-Dokument), reicht ein reines In-Memory-Array dafür nicht — die Liste liegt deshalb zusätzlich in sessionStorage (mts_scanner_tickers): überlebt beliebig viele Signal-Hub-Klicks/Reloads im selben Tab, verschwindet aber beim Schließen des Tabs — bewusst kein Dauerzustand wie data.watchlist.

☁️ Kursdaten auch rein über die Cloud-Seite (via Tailscale) (_checkProxy/_apiBase, Fix 2026-08-01): Kandidaten-Scanner, Ticker-Autofill und SEPA-Trend-Ampel brauchten bislang eine echte Verbindung zum Mac mini (LAN/Tailscale) oder scheiterten auf der reinen *.pages.dev-Version mit „keine Kursdaten verfügbar" — obwohl _sepaFetchHistory eigentlich einen Yahoo-Fallback hat. Ursache: Cloudflare Pages beantwortet jeden unbekannten Pfad (auch /ticker_map, /history) mit Status 200 und liefert dabei als SPA-Fallback die index.html aus; die alte Prüfung _checkProxy() verließ sich nur auf r.ok und hielt das fälschlich für einen erreichbaren 8090-Proxy — das blockierte wiederum den echten Yahoo-CORS-Fallback in _yahooJson() (der bei „Proxy vermeintlich verfügbar" bewusst übersprungen wird). Zwei Korrekturen: (1) _checkProxy() validiert jetzt zusätzlich den Content-Type-Header (muss …/json enthalten — server_mts.py setzt das immer, Cloudflare Pages' HTML-Fallback nie); (2) neue dritte Stufe Tailscale-Hostname (TAILSCALE_HOST, dieselbe Konstante wie in index.htmls mtsUrl()) — schlagen gleiche Origin und lokale Ports fehl, wird https://mac-mini-von-maick.tail02bccd.ts.net/ticker_map versucht. Ist das Gerät per Tailscale verbunden, liefert die Cloud-Seite dadurch jetzt echte Mac-mini-Kursdaten (schnell, ISIN-fähig) statt auf die langsameren öffentlichen CORS-Proxys angewiesen zu sein; ist auch das nicht erreichbar, greift jetzt korrekt der Yahoo-Fallback statt stumm leer zu bleiben. Betrifft alle _apiBase()-Verbraucher (Trade-Planner-Preisabruf, ATR-Stop, Verkaufs-Check, Sektor-Rotation, KI-Committee).

🧙 Risiko-Erfassungs-Wizard (Knopf „Risiko-Erfassung" über den offenen Positionen): geht alle offenen Positionen ohne definierten Stop-Loss einzeln durch — Minervini: kein Stop-Loss = kein Trade. Pro Position werden Stop (Pflicht) und optional der SEPA-Score nachgetragen; Risiko wird automatisch neu berechnet. Gedacht für den einmaligen Nachtrag bei Bestandspositionen, die vor Einführung der Risiko-Registry angelegt wurden.

🎲 FIRE Monte-Carlo (FIRE-Tab): zusätzlich zur glatten Compound-Hochrechnung simuliert die App 2000 Verläufe mit schwankenden Jahresrenditen (einstellbare Volatilität) und zeigt eine ehrliche Bandbreite — optimistisch (P10), Median (P50), pessimistisch (P90) — plus die Wahrscheinlichkeit, das Ziel binnen 40 Jahren zu erreichen.

🔬 Kelly „Was-wäre-wenn" (Trade-Planner): wendet ein konsequentes fixes Risiko (1,0 / 1,25 / 2,0 %) auf die realen R-Multiples deiner geschlossenen Trades an und zeigt den kumulierten Depot-Multiplikator. Macht greifbar, wie stark stures Risikomanagement die Equity-Kurve verändert.

📊 R-Multiple-Verteilung (Trade-Planner, unter Kelly): R-Multiple = realisierter Gewinn/Verlust ÷ geplantes Risiko (trade.risiko). Statt nur eines Durchschnitts zeigt ein Histogramm die volle Verteilung (< −2R bis ≥ +5R) — im Minervini-System der entscheidende Indikator, da das System auf wenigen großen Gewinnern bei vielen kleinen kontrollierten Verlusten basiert.

📏 Risiko offen und Risiko beim Kauf (seit 2026-10-09): Jede Position führt zwei Werte. Risiko offen (risiko) ist der mögliche Verlust zum aktuellen Stop: Einstand − Stop, nie negativ. Liegt der Stop über dem Einstand, ist es 0. Damit rechnen Risiko-Cockpit, Heat und Depotrisiko. Risiko beim Kauf (risikoInitial) bleibt fest: Ein Teilverkauf nimmt seinen Anteil mit, ein Nachkauf erhöht es um das Risiko der neuen Stücke. Alle R-Werte rechnen damit: R-Multiple-Verteilung, Kelly, CRV-Spalten, Regel-Check, Risiko-Historie, CSV-Export (neue Spalte „Risiko beim Kauf €“), „R akt.“ bei offenen Positionen sowie Trade-Akte, Journal und Disziplin-Protokoll. Vorher wurde risiko beim Nachziehen mit Betrag neu berechnet, ein Stop im Gewinn zählte als Risiko. Im Testlauf mit HOOD stand dadurch ein Teilverkauf mit +3,7 R statt +2,5 R da. Altbestand ohne festes Kaufrisiko friert sein bisheriges Risiko beim ersten Stop-Nachzug ein, außer bei geschätzten Stops aus dem Numbers-Import.
🧪 R-Statistiken zählen jetzt nur noch echtes Risiko (seit 2026-08-23, Systemprüfung). Alle Trades aus dem alten Journal-CSV-Import (parseMaickJournalCSV, tradeId-Präfix „ms-") hatten stop/target nie als eigene Entscheidung — eine feste Formel aus dem Entry-Kurs (stop = entry×0,92, target = entry×1,2), alle 252 Alt-Trades dadurch mit exakt demselben 8 %-Stop-Abstand. R-Multiple/CRV/Disziplin-Check auf solchen Werten würde eine reine Rückrechnung als diszipliniertes Risikomanagement missverstehen. Neues Feld risikoSynthetisch markiert diese Trades — einmalig rückwirkend beim Laden migriert (MTS.load(), greift nur bei tradeId-Präfix „ms-"), neue CSV-Importe setzen es direkt. Der neue Helfer MTS.hatEchtesRisiko(t) filtert damit konsequent: R-Multiple-Verteilung und Kelly „Was-wäre-wenn" hier, Ø-CRV je Quelle/Setup-Typ (Psychology-Tab), der Disziplin-Tracker („Positionsgröße ≤ 2 %"/„Stop-Loss eingehalten" — bei ausschließlich synthetischem Risiko verschwinden beide Regeln jetzt korrekt, statt eine falsche 100 %-Compliance zu zeigen) sowie die Risiko-Historie. In der Portfolio-Tabelle bleiben die Rohwerte sichtbar (kein Wegfiltern in einer reinen Datenansicht), aber ein 🧪-Symbol markiert „R akt."/„CRV" bei betroffenen Zeilen mit Tooltip-Erklärung. Manuell über den Trade-Planner angelegte Trades sind nie betroffen — deren stop kommt aus einer echten Formulareingabe.

📊 Sektor-Rotation (Portfolio-Tab): zeigt die 11 US-Sektoren nach 1-Monats-Performance gerankt (stark → schwach). „🔄 Laden & einordnen" ordnet zusätzlich die offenen Positionen ihrem Sektor zu (Sektor kommt vom 8090-Server: FMP, sonst yfinance-Fallback) und markiert Positionen in den drei schwächsten Sektoren. Minervini-Logik: Leader in starken Sektoren halten, relative Schwäche im Auge behalten.

🧭 Verlinkung zum Breadth-Grid (seit 2026-08-23, unter „📈 Benchmark-Vergleich" im Portfolio-Tab): Link „Depot-Trendstärke vs. Marktbreite" öffnet Minervini Markets 360 direkt im Tab „Marktbreite" (per #tab-breadth-Deeplink, den Minervini Dashboard.html seither auswertet). Das eigentliche Breadth-Grid lebt bewusst dort statt hier in der Hauptapp — Depot-Positionen liegen zwar in mts_data.json, die S&P-500-/Euro-Stoxx-50-Marktbreite braucht aber den externen MinerviniMarkets360-Nachtscan, den nur der 8080-Server kennt (Details siehe dortiger Absatz). Da die Hauptapp normalerweise über den 8090-Origin läuft, musste dafür der bestehende Markets-360-Reverse-Proxy in server_mts.py (_proxy_8080, bisher nur /markt//fred//sector-perf) um /breadth erweitert werden — sonst hätte der Link auf einer 404-Fehlermeldung geendet. sw.js führt /breadth und /depot-tickers zusätzlich als „immer frisch"-API-Pfade, damit der Service Worker keine fehlgeschlagene Antwort zwischenspeichert.

🏛️ Committee-Verdikt inline: Hat eine offene Position eine Investment-Committee-Analyse (Minervini/Weinstein/O'Neil), erscheint in der Ticker-Spalte ein farbiges Score-Badge (🏛️ Score). Tooltip zeigt Entscheidung + Entry/Stop/Ziel; Klick öffnet den Committee-Tab. So sieht man das KI-Urteil direkt am Bestand statt nur im separaten Tab.

Überwachung & Speicherung

📲 Proaktive Alarme: Der 8090-Server überwacht die offenen Positionen automatisch (alle 20 Min, außerhalb der Ruhezeit 22–08 Uhr und am Wochenende pausiert) und schickt einen Push an deinen ntfy-Kanal (derselbe wie der Signal-Hub, nichts Neues abonnieren) bei: Stop-Loss-Bruch (Priorität „urgent"), SL-Nähe (Kurs < 3 % über dem Stop), Teilverkaufszonen +8 % / +20 % / +40 % (Minervini 50/25/25) und erreichtem Kursziel. Anti-Spam: Beim ersten Sichten einer Position wird nur der Ist-Zustand gemerkt — gepusht wird erst bei einem echten Übergang. Logik: mts_alarms.py. Test: python3 mts_alarms.py --test.

🩺 Status-Leiste: Oben zeigt die App den Live-Zustand: „Mac mini: online/offline" (alle 30 s neu geprüft — ein Ausfall des Mac mini wird sofort sichtbar, nicht erst an leeren Charts) und das Kurs-Datenalter („HH:MM · vor X Min", grün < 15 Min, orange < 60 Min, rot darüber). So ist immer klar, ob die angezeigten Zahlen frisch sind.

Daten werden bei jeder Änderung an den Server gespeichert (mts_data.json, siehe Daten & Sicherung).

KPI-Leitfaden & Lesehilfe

Der frühere Erklärkasten aus dem Control Center (seit 2026-08-06 hierher verschoben, siehe oben) — Formeln und Faustregeln zu den wichtigsten Kennzahlen im Dashboard und Trade-Planner:

📈 Win Rate: Prozentsatz profitabler Trades von allen abgeschlossenen Trades — Gewinner ÷ (Gewinner + Verlierer) × 100. Minervini-Standard: ≥ 50 % ist solide, aber wichtiger als die Win Rate allein ist das CRV. Eine 40-%-Win-Rate mit 3:1-CRV ist besser als 70 % mit 1:1-CRV.

⚖️ CRV — Chance-Risiko-Verhältnis: Verhältnis von durchschnittlichem Gewinn zu durchschnittlichem Verlust — Ø Gewinner ÷ Ø Verlierer. Minervini-Mindestwert 2:1, Ziel 3:1+. CRV > 2 bei Win Rate > 40 % ergibt ein profitables System.

🎯 Erwartungswert (EV): der wichtigste KPI, der durchschnittliche Gewinn/Verlust pro Trade — (Win Rate × Ø Gewinner) − (Verlust-Rate × Ø Verlierer). EV > 0 bedeutet, das System macht langfristig Gewinn; EV = 150 € heißt im Schnitt +150 € pro Trade.

🎲 Kelly-Kriterium: mathematisch optimale Positionsgröße zur Kapitalmaximierung — Kelly % = W − (1 − W) ÷ R (W = Win Rate, R = CRV). Full Kelly ist rechnerisch optimal, aber zu aggressiv für die Praxis; empfohlen ist Half Kelly (halbiertes Risiko bei 75 % der Rendite). Minervini selbst rechnet unabhängig davon mit fixen 1–2 % pro Trade — Kelly nie über Half-Kelly hinaus anwenden, sonst wird das Drawdown-Risiko zu hoch.

🔥 FIRE-Zahl & 4-%-Regel: das Depot-Ziel für finanzielle Unabhängigkeit — Jahresausgaben × 25 (basiert auf der Trinity Study). Beispiel: 3.000 €/Monat Ausgaben = 36.000 €/Jahr → Ziel 900.000 € Depot. Die FIRE-Jahre ergeben sich aus aktueller Sparrate und Rendite bis zum Ziel — je höher die Trading-Performance, desto kürzer der Weg.

📊 Portfolio Heat: Anteil des Gesamtkapitals, der im Einstandswert offener Positionen steckt (80 % Heat = 80 % investiert, 20 % frei). Minervini-Empfehlung: maximal 70–80 % investiert, immer einen Cash-Puffer behalten. Das Gesamtrisiko (Summe aller Stop-Loss-Abstände) sollte 5 % des Depots nicht übersteigen.

📈 Benchmark Alpha: die Mehrrendite gegenüber einem Index wie dem S&P 500 — eigene YTD-Rendite − Benchmark-Rendite. Beispiel: Depot +25 %, S&P 500 +18 % → Alpha = +7 %. Positives Alpha bedeutet echten Mehrwert gegenüber reinem Passiv-Investieren.

⚖️ Abgeltungssteuer (Deutschland): 25 % Abgeltungssteuer + 5,5 % Soli = 26,375 % effektiv, gilt für alle Gewinne (die Spekulationsfrist wurde 2009 abgeschafft). Der Freistellungsauftrag (1.000 €/Jahr steuerfrei, seit 2023) sollte zuerst genutzt werden; Verluste aus Aktienverkäufen sind nur mit Gewinnen aus Aktienverkäufen verrechenbar — steuerliche Planung heißt: Verluste vor Jahresende realisieren, wenn Gewinne überwiegen.

📐 Das Minervini-Gleichgewicht: Profitables System = Win Rate × Ø Gewinner > (1 − Win Rate) × Ø Verlierer — Risiko 1–2 % pro Trade, Stop-Loss 7–10 % unter Entry, Mindest-CRV 2:1, maximale Portfolio Heat 80 %.

🧠 Minervini Coach 2 (minervini-coach-2.html)

Lern- und Analyse-App mit den Seiten Coach, Fragen-Werkstatt, Quiz, Academy (Stage-2-Basen, VCP, Entry, Pyramidisieren), Mental und Guide. Lernfortschritt synchronisiert über GitHub-Gist. Die frühere Seite SEPA-Analyse (Trend-Template-Check, Chart-Pattern-Erkennung, Batch-Screener, Watchlist, Position Size Rechner) lebt seit 2026-07-23 nicht mehr hier, sondern in „Maick's Trading System" (Trade-Planner) — bewusste Zusammenführung mit dem dortigen Trade-Planner, siehe Hauptapp-Abschnitt. Alte Links (?ticker=…, ?page=sepa) leiten automatisch dorthin um.

🛑 Brücke zum Trade-Journal (seit 2026-10-10): Coach und Hauptapp-Journal nutzen dieselbe Verfassungsskala (zehn Stufen, COACH_VERFASSUNG = MTS_VERFASSUNG, tests/test_coach_journal.py vergleicht beide). In der täglichen Reflexion ist „Nach einem Verlust“ ein eigenes Häkchen statt einer Emotion; alte Einträge bleiben lesbar. Darüber zeigt die Karte „Aus deinem Trade-Journal“, welche Positionen auf oder unter ihrem Stop liegen und welcher Grund in den festgehaltenen Stop-Momenten am häufigsten vorkommt — mit Knopf ins Journal (#stopmomente) und zum passenden Coaching-Thema. Liegt etwas unter Stop, nennt auch die Coaching-Antwort diese Positionen. Gelesen wird nur der Hauptapp-Stand auf diesem Gerät (localStorage, gleiche Herkunft); ist die Hauptapp hier noch nie geöffnet worden, bleibt die Karte aus. Neuer Deeplink minervini-coach-2.html?page=coach&thema=cutting_losses öffnet ein Thema direkt (genutzt von Fehlern und Gründen im Journal).

❓ Fragen-Werkstatt (seit 2026-09-13): Reflexionswerkzeug nach Hal Gregersens Buch „Questions Are the Answer", zugeschnitten auf Finanzen, Investment, Märkte, Trading und Mindset. Vier Tabs: Heute zeigt eine feste tägliche Leitfrage („Wo könnte ich gerade falsch liegen?") plus zwei rotierende Fragen aus einem Pool von rund 40 Fragen in fünf Kategorien (Kernfragen, Annahmen & Markt, Risiko & Disziplin, Mindset & Wahrnehmung, Lernen & Umsetzung) — die Rotation läuft den Pool einmal komplett durch, bevor sie neu mischt, und ist pro Tag fest (kein Neu-Würfeln bei jedem Reload). Question Burst ist Gregersens 4-Minuten-Technik: nur Fragen sammeln, nicht beantworten, danach die stärksten markieren — markierte Fragen wandern automatisch nach Meine Fragen, wo zusätzlich eigene Fragen je Kategorie angelegt werden können. Alle Fragen zeigt den kompletten Pool zum freien Durchblättern. Notizen zu jeder Frage sind eine fortlaufende, sich entwickelnde Antwort statt eines Tagesjournals — Änderungen überschreiben die vorherige Notiz zur selben Frage bewusst, damit die Reflexion über Zeit verfeinert statt dupliziert wird. Speicherung wie die übrigen Coach-Module lokal plus automatisch im selben Gist wie Coach/Mental/Academy/Lexikon (minervini-fortschritt.json), dadurch geräteübergreifend ohne zusätzliche Einrichtung.

📚 Minervini-Lexikon (seit 2026-08-31, Phase 0): Archivierte X-Posts von Mark Minervini — Originaltext (Englisch, wo erfasst per Umschalter „🇬🇧 Original zeigen" einsehbar) und deutsche Übersetzung, je nach Post mit Marktkontext des Tages (Ampel/Leitindex, nur bei exaktem Datumstreffer im Regime-Logbuch), erwähnten Kursen/Trades, Verweisen auf verwandte Posts und angehängten Bildern. Suche und Filter nach Typ (Marktkommentar, Lebensweisheit, Artikelverweis, Videoverweis). Erfassung ist bewusst manuell (kein automatisiertes Abrufen — verstößt gegen X's Nutzungsbedingungen): direkt auf der Seite (siehe nächster Absatz) oder über eine Claude-Code-Sitzung; Bilder liegen am Mac in iCloud und zusätzlich (seit 2026-10-10) im privaten Speicher des Workers: Auf localhost lädt der Coach sie direkt, auf mts-hub.pages.dev über /lexikon-bild mit dem Gist-Token. Ohne eingerichteten Cloud-Sync steht dort ein Hinweis statt des Bildes. Bei jeder Änderung der Lexikon-Datei legt minervini_lexikon.py außerdem eine Kopie in Google Drive ab (Ordner „Mixed Kurier“). Der Cloud-Spiegel gleicht, solange der Mac mini läuft, alle 30 Minuten mit dem Gist ab, damit auch im Coach erfasste Posts in Datei und Drive-Kopie ankommen; ist er aus, passiert das beim nächsten Abgleich (minervini_lexikon.py --gist) an einem Mac, der den Coach-Stand holt. Synchronisiert geräteübergreifend über denselben privaten Gist wie der übrige Lernfortschritt. Dieselben Marktkommentare erscheinen zusätzlich als kurze Zitat-Karte unter der Minervini-Analyse in Signal-Hubs Setup-Detail-Seite (setup-detail.html) — auch dort ausschließlich über den privaten Gist geladen, nie als öffentlich deploybare Datei.

✏️ Pflege direkt auf der Seite (seit 2026-09-23): Im Lexikon erfasst „+ Post hinzufügen" einen neuen Post: Datum, Typ und deutscher Text sind Pflicht, Uhrzeit, englisches Original, Kontext und Link sind freiwillig. Ein Link auf einen X-Post dient als Kennung, damit derselbe Post nicht doppelt landet. Den Marktkontext des Tages trägt der nächste Abgleich am Mac nach. Jeder Post und jedes Video hat einen 🗑-Knopf (mit Rückfrage). Gelöscht wird auf allen Geräten, und ein späterer Import nimmt den Eintrag nicht wieder auf: Gelöschte Kennungen stehen dauerhaft in der Liste geloescht. Im Reiter Lektionen legt „+ Video vormerken" einen YouTube-Link, optional mit eingefügtem Transkript samt Zeitmarken, in eine Warteschlange. Die Kernaussagen schreibt weiterhin Claude, sobald man in einer Sitzung darum bittet. Die Warteschlange ist eine eigene Datei minervini-lexikon-eingang.json im selben privaten Gist. Ein eingefügtes Transkript liegt dort nur, bis es am Mac übernommen ist (src/minervini_transkripte.py --eingang), danach wird der Text entfernt.

Technisch: Der Gist ist seither die maßgebliche Quelle. Datei am Mac, Gist und Browser werden zusammengeführt statt überschrieben: Einträge je Kennung vereinigt, bei zwei Fassungen gewinnt die zuletzt geänderte (geaendert), Gelöschtes bleibt gelöscht. Der Coach liest vor jedem Speichern den aktuellen Gist, damit ein zweites offenes Gerät mit altem Stand nichts zurückdreht. Am Mac gleicht src/minervini_lexikon.py --gist in beide Richtungen ab (braucht gh auth login mit Gist-Recht), mit --eintraege … --gist vor und nach dem Hinzufügen. Die Regeln stehen zweimal, in minervini-coach-2.html::_lexikonMerge und minervini_lexikon.py::merge_lexikon, und tests/test_minervini_lexikon.py prüft, dass beide gleich rechnen. Der Knopf „Lokalen Stand hochladen" und der Auto-Abgleich beim Öffnen auf localhost führen die Mac-Datei ebenfalls nur noch zusammen.

🎓 Lektionen (Videos) (seit 2026-09-21): Eigener Reiter neben dem Lexikon mit Kernaussagen aus Minervini-Videos (Interviews, Webinare, Podcasts, Q&As), nach Thema sortiert. Jede Aussage trägt eine Zeitmarke, die per Klick an die Stelle im YouTube-Video springt. Zwei Ansichten: „Nach Thema" (alle Aussagen eines Themas, z. B. Verluste begrenzen, Progressive Exposition, Verkaufsregeln, quer über alle Videos) und „Nach Video" (Zusammenfassung plus alle Aussagen). Die Aussagen sind eigene, deutsche Zusammenfassungen, keine Wortlaute; unklare Stellen im automatischen Untertitel sind markiert. Die Einträge liegen im selben Datenbestand und im selben privaten Gist wie das Lexikon (Typ video_lektion, das Lexikon blendet sie aus) — Sync und Schutz gegen versehentliches Überschreiben sind dieselben. Volltext-Transkripte bleiben nur lokal in iCloud (Signal-Hub/data/minervini-lexikon/transkripte/), nie im Gist und nie im Deploy (Urheberrecht, gleiche Regel wie beim Lexikon). Aufnahme: im Coach „+ Video vormerken" (siehe oben), Transkript als .txt nach minervini-lexikon-eingang/transkripte/ legen oder die JSON des Apify-Actors „YouTube Scraper" (Untertitel-Option an) importieren, danach per Claude-Code-Sitzung verarbeiten lassen; src/minervini_transkripte.py prüft dabei auf Dubletten (Video-ID, Text-Hash, Textanfang) und legt Doppelte in _duplikate/ ab, statt sie zu erfassen. Videos von Dritten, die nur über Minervini reden, werden bewusst nicht aufgenommen.

📖 Betriebsanleitung (seit 2026-08-27): Link „📖 Anleitung" unten in der Sidebar verlinkt eine dedizierte Anleitung als Claude-Artifact — kurzer Ist-Zustand pro Abschnitt, Versionsverlauf wo vorhanden aufklappbar statt im Fließtext; der Abschnitt „Trainingsphase" ordnet die Wochenthemen des 12-Wochen-Lernpfads den Coach-Bereichen zu (Mental Stärke, Guide, Academy, Lektionen, Quiz) und trennt Verstehen/Reflektieren (Coach) von Handeln/Messen (Trainingsplan) — Coach-Anleitung. Design-System und URL siehe CLAUDE.md.

📊 Minervini Markets 360 (Minervini/Minervini Dashboard.html)

Die 360°-Marktsicht. Tabs: 🚦 Marktampel (Berechnung siehe eigener Absatz unten), Technik, Marktbreite, Sentiment, Makro, Rohstoffe, Sektoren, COT, Grundlagen, Links.

📖 Betriebsanleitung (seit 2026-08-27): Header-Button „📖 Anleitung" verlinkt eine dedizierte Anleitung als Claude-Artifact — Marktampel-Nutzung zuerst, die übrigen acht Tabs als kompakte Tabelle statt Einzelabschnitte, Versionsverlauf je Abschnitt aufklappbar; der Abschnitt „Trainingsphase" übersetzt die Ampelfarbe in die Trainingsregel (Grün volle, Gelb halbe Größe, Rot kein Kauf) und beschreibt die Übung für Lernpfad-Woche 5 — Markets-360-Anleitung. Design-System und URL siehe CLAUDE.md.

🧭 Breadth-Grid „Depot-Trendstärke vs. Marktbreite" (seit 2026-08-23, Tab Marktbreite): Tabelle mit dem Anteil der Aktien/Positionen über GD20/50/200 — angelehnt an den TraderFox-Megatrend-Monitor, aber fürs eigene Depot statt für KI-Aktien. Referenzzeilen S&P 500 und Euro Stoxx 50 kommen aus der lokalen Nachtscan-Ausgabe des externen MinerviniMarkets360-Projekts (exports/markets360_latest.csv, gelesen von Minervini/server.py unter /breadth — echte Breadth, keine Schätzung, da der Scan das jeweils komplette Universum abdeckt).

Kein Russell 2000: Wikipedia führt keine vollständige Mitgliederliste, die öffentliche iShares-Holdings-CSV sitzt hinter Akamai-Bot-Schutz (bewusst nicht umgangen) — stattdessen zeigt die Zeile „US-Small-Caps (Marktkap.-Proxy)" alle US-Werte im Marktkapitalisierungsband 200 Mio.–3,5 Mrd. USD aus einem einzelnen TradingView-Bulk-Scan (dieselbe Quelle/Methode wie der Marktvorfilter im Screener-Projekt, kostet keinen zusätzlichen Yahoo-Abruf).

Gesamtdepot/Einzeldepots kommen per Cross-Origin-Abruf von /depot-tickers auf dem 8090-Server (server_mts.py, Hauptapp) — bewusst ein schmaler, separater Endpunkt, der nur Ticker-Symbole und Depotnamen liefert, keine Beträge/P&L (/data selbst bleibt ohne CORS; dafür wurde die CORS-Allowlist um localhost:8080/127.0.0.1:8080 erweitert). Je Position wird die Kurshistorie über den vorhandenen /markt/<symbol>-Proxy geholt und GD20/50/200 clientseitig berechnet; ISIN-artige Depot-„Ticker" (Zertifikate/Turbos ohne Yahoo-Symbol, z. B. bei Finanzen.net Zero) werden erkannt und übersprungen statt die Ladezeit über Fallback-Ketten hochzutreiben.

Datenherkunft/Frische: Der produktive Nachtscan läuft inzwischen primär als Cloud-Workflow (GitHub Actions → R2) und schreibt nicht direkt auf den lokalen Mac zurück — fetch_breadth() zieht deshalb bei jeder Cache-Erneuerung (30-Min-Takt) per rclone copyto zuerst die frischeste Version herunter, bevor sie gelesen wird. Läuft der Pull mangels Internet/rclone einmal ins Leere, bleibt einfach die letzte lokale Kopie stehen (kein Absturz) — der tatsächliche Zeitstand steht dafür transparent rechts über der Tabelle. Der Nachtscan selbst lässt sich weiterhin zusätzlich manuell über „☁️ Cloud-Scan starten" anstoßen. Die drei GD20/50/200-Spalten je Einzelaktie sind eine Ergänzung des Nachtscan-CSV-Exports und unabhängig von Minervinis 8-Kriterien-Trend-Template-Summenscore (der nutzt klassisch nur 50/150/200, kein 20er). Von der Hauptapp aus verlinkt seit 2026-08-23 ein Link im Portfolio-Tab (Karte „Benchmark-Vergleich") direkt hierher.

☁️ Cloud-Fallback ohne laufenden Mac (seit 2026-08-23): Ursprünglich brauchte das Breadth-Grid zwingend /breadth (8080) und /depot-tickers (8090) — ohne laufenden Mac „keine Daten". minervini-markets-360/scripts/build_breadth_summary.py fasst nach jedem Nachtscan dieselben Kennzahlen zusätzlich in einer kompakten breadth.json zusammen (S&P 500/Euro Stoxx 50 aus der CSV, US-Small-Cap-Proxy per TradingView) und lädt sie nach R2 hoch; ein neuer letzter Nachtscan-Schritt stößt danach den Deploy im Hauptrepo an (DEPLOY_TRIGGER_TOKEN, gleiches Muster wie bei Price-Action-Hub/Rotation-Dashboard/Trend-Screener), deploy.yml/deploy.command ziehen die Datei mit nach mts-hub.pages.dev/Minervini/breadth.json. loadBreadthGrid() versucht zuerst /breadth, bei Fehlschlag die statische breadth.json daneben — Anzeige wechselt dann von „Nachtscan-Stand" auf „☁️ Cloud-Stand". Depot-Zeilen: Neues Secret PORTFOLIO_TICKERS_JSON (nur Ticker-Symbole je Depotname, keine Beträge/Stückzahlen — exakt dasselbe Schutzniveau wie SIGNALHUB_DEPOT_NAMEN im Signal-Hub, nach jeder Trade-Änderung neu zu setzen, kein Auto-Sync aus mts_data.json — seit 2026-09-30 erledigt das python3 agenten/depot_namen_secret.py zusammen mit SIGNALHUB_DEPOT_NAMEN; vorher blieb es fünf Wochen alt und die Cloud-Depotzeilen wichen deutlich vom lokalen Breadth-Grid ab) lässt denselben Nachtscan zusätzlich Gesamtdepot/Einzeldepots berechnen (dieselbe SMA20/50/200-Logik wie lokal, ISIN-artige Zertifikate/Turbos werden genauso übersprungen); fehlt das Secret, bleibt einfach nur der Marktreferenz-Teil der breadth.json gefüllt. Live-Verifikation lief gegen einen isolierten Test-Server ohne /breadth-Route.

Woraus die Marktampel berechnet wird (seit 2026-07 identisch in Markets 360 und im Markets360 Screener — Streamlit-App, Port 8501, siehe Server-Tabelle unten: beide rufen dieselbe Python-Engine minervini360.scoring.market direkt auf, damit nie wieder unterschiedliche Prozentzahlen erscheinen wie vor der Vereinheitlichung):

  • Trend-Template je Leitindex (S&P 500, Nasdaq): Kurs über SMA50/SMA200, SMA50 über SMA200, SMA200 seit ~1 Monat steigend — 4 Kriterien je Index. Die %-Zahl ist der Anteil erfüllter Kriterien über alle Indizes; grün gibt es nur, wenn ALLE Indizes grün sind (sonst gelb/rot).
  • 10-Monats-EMA je Leitindex (seit 2026-08-29): stuft ein sonst grünes Index-Ergebnis auf gelb herab, wenn der Index unter seinem 10-Perioden-EMA auf Monatsbasis notiert. Zählt bewusst nicht in die %-Zahl (die bleibt auf 4 Kriterien geeicht) und kann nie Rot auslösen. Details und die beiden dabei behobenen Datenfallen siehe „Einheitliche Marktampel".
  • Sentiment-/Bestätigungsfaktoren (dämpfen eine grüne Ampel auf Gelb, sobald seit 08/2026 mind. 3 davon auf Rot stehen — vorher 2, angehoben beim Hinzufügen der letzten beiden Faktoren, weil Follow-Through-Day und Abstand-zum-Hoch am selben Rücksetzer hängen und sich sonst gegenseitig verstärken würden; nie umgekehrt gelb→rot): Distribution Days, Marktbreite (Russell 2000), Abstand zum 52-Wochen-Hoch, VIX, Follow-Through-Day (O'Neil/IBD-Kriterium, seit 08/2026: läuft ein Rücksetzer von ≥3 % ohne dass ab Rally-Tag 4 ein Tagesgewinn ≥1,25 % bei höherem Volumen als am Vortag folgt, gilt er als unbestätigt — Bestätigungsfenster 25 Handelstage, danach gilt ein nie bestätigter Rücksetzer als veraltet statt als Dauer-Warnsignal) und 10-Jahres-Rendite (^TNX, seit 08/2026: ab 4,5 % erhöhte Wachsamkeit, ab 5,0 % Gegenwind — Minervinis eigene genannte Reizschwelle).
  • Live-Quellen: ausschließlich Yahoo Finance (Indizes/VIX/Russell/^TNX) — bewusst keine Scrapes fremder Seiten ohne API-Vertrag (CBOE Put/Call, AAII-Umfrage, CNN Fear&Greed), die ohne Vorwarnung wegbrechen können.
  • Die früheren 8 manuellen Bullish/Neutral/Bearish-Dropdowns (inkl. Handeingabe für Fear&Greed/Put-Call/AAII) sind komplett entfallen. Leitaktien-Performance und USD-Stärke (DXY) bleiben als reine Zusatzinfo in den Karten sichtbar, zählen aber nicht mehr zur Ampel (keine Minervini-Kriterien).

Markt-Heatmaps (im Marktampel-Tab): „US Sektoren · live" ist selbst gebaut aus eigenen Daten (Yahoo, ±15 Min — Standardansicht). „US Aktien · Vortag" ist das TradingView-Widget, das für US-Aktien prinzipbedingt nur den Vortagesschluss liefert (E-Badge) — daher die Beschriftung.

🏦 Makro-Tab: FRED-Karten (Fed Funds, CPI, Kern-PCE, Payrolls, Zinsen, Spreads …), darunter der Block 📰 Truflation · Truman Macro Daily und Link-Karten zu Fed, Zinsen & Wirtschaftsdaten (u. a. Truflation Dashboard — TruCPI, tägliche US-Inflation aus beobachteten Marktpreisen, läuft der monatlichen offiziellen CPI meist voraus).

Truflation-Newsletter (seit 2026-09-30): Die Truflation-API ist kostenpflichtig (ab Professional-Plan), deshalb liest die Signal-Hub-Pipeline bei jedem ihrer vier Läufe am Tag die Newsletter-Mails von truman@truflation.com aus demselben freenet-Postfach wie die übrigen Mail-Signale (Signal-Hub/src/truflation_mail.py) und schreibt Signal-Hub/data/truflation.json (letzte 10 Ausgaben). Übernommen werden nur Fakten: Land, Thema, Titel, Status (Verified/Cited/Lookahead), Key Metrics und Quellen als Text — bewusst nicht die Einordnungstexte (fremder Newsletter-Inhalt, die Datei liegt öffentlich auf Pages) und nie Links aus der Mail (alles personalisierte Tracking-Links inkl. Abmeldelink; eine Notbremse im Modul bricht ab, sobald eine URL oder Mailadresse in der Ausgabe landen würde). Nennt eine Ausgabe den TruCPI-Wert, erscheint er oben als eigene Kachel. Jede Ausgabe wird zusätzlich unveränderlich unter R2 truflation/history/JJJJ-MM-TT.json abgelegt (Datum aus dem Mailkopf); kommt keine neue Mail, bleibt der letzte Stand stehen, nach 7 Tagen ohne Ausgabe zeigt der Block einen Hinweis. Rein informativ — fließt in keine Ampel, keinen Score und keine Top-Setups ein.

📊 Wochenanalyse-Link (seit 2026-08-08): Karte unter den Ampel-Faktoren im Marktampel-Tab mit Link auf eine extern erstellte Wochenanalyse (z. B. ein Grok-Chat-Share-Link) plus Datum. Der Link wird manuell gepflegt — keine Engine ruft Grok automatisch auf, dafür gibt es keine öffentliche API/Zeitplan-Anbindung — aber geräteunabhängig über einen kleinen GitHub Gist gehalten: Lesen läuft für alle Geräte ohne Token (fester, „secret" Gist, per ID ohne Auth abrufbar, genau wie ein geteilter Link), Schreiben (Button „✎ Aktualisieren") nutzt den geteilten Token gh_gist_token (localStorage, gleiche Origin wie Coach/Hauptapp — beim ersten Klick auf einem Gerät einmalig per Prompt abgefragt, danach automatisch mitverwendet). Ablauf: neuen Grok-Analyse-Link erzeugen (Prompt in Grok ausführen → „Share"-Link kopieren), dann in Markets 360 auf „✎ Aktualisieren" klicken und Link + Titel einfügen — von jedem Gerät aus, auch ohne Mac mini.

☁️ Cloud-Nachtscan (GitHub Actions, seit 2026-07-17): Der nächtliche Vollscan (minervini360.cli screen --top 100) läuft zusätzlich ohne Mac mini als Workflow im eigenen Repo Miksch52/minervini-markets-360 (.github/workflows/nightly.yml, täglich 05:38 Europe/Berlin — krumme Minute seit 2026-09-10, siehe Cron-Absatz beim Signal Hub) und lädt markets360_latest.csv per rclone nach r2:signalhub-magazine/markets360/ — von dort liest der Daily Signal Hub sie als zusätzliche Quelle (siehe dort). Der Mac-mini-LaunchAgent com.maick.markets360.nightly läuft vorerst parallel weiter.

Datenquelle: Läuft der 8080-Server, kommen Marktampel/FRED/Sektoren über ihn (lokaler Cache; die Marktampel unter /marktampel, siehe oben). Ist der Mac mini aus, holt die App die Kurse selbst — direkt von Yahoo bzw. über CORS-Proxies (VIX, FRED und US-Sektoren-Heatmap). Auch die Marktampel läuft seit 2026-07-25 ohne Mac mini (loadMarktampelViaWorker, JS-Fallback im Dashboard): Ist der 8080-Server nicht erreichbar (Mac mini aus, Pages-Version), holt das Dashboard ^GSPC/^IXIC/^VIX/^RUT/^TNX selbst über den Cloudflare-Worker und rechnet dieselbe Ampel client-seitig — eine 1:1-Portierung von minervini360.scoring.market (Index-Trend, Distribution Days, VIX, Small-Caps, 52-W-Abstand, Follow-Through-Day, 10-Jahres-Rendite, Sentiment-Dämpfung). Bei Änderungen an der Python-Engine muss die JS-Portierung im Dashboard nachgezogen werden, sonst laufen Server- und Fallback-Ampel auseinander. Als robusten, eigenen Proxy gibt es einen Cloudflare Worker (kostenlos, immer erreichbar) — Code + Anleitung in cloudflare-worker/. Nach dem Deploy die Worker-URL in die Konstante MTS_PROXY der drei Apps eintragen; dann wird er bevorzugt genutzt und die unzuverlässigen öffentlichen Gratis-Proxys (corsproxy.io ist kostenpflichtig geworden) nur noch als Notnagel. Über die HTTPS-PWA (https://…ts.net/) reicht zudem der 8090-Server die Datenendpunkte (/markt, /sector-perf, /fred) an den 8080-Server durch — so kommen echte Server-Daten ohne Mixed-Content, ohne Umweg über öffentliche Proxies. Minervinis Kernregel: Nur kaufen, wenn der Markt grün ist.

🛰️ Daily Signal Hub (Signal-Hub/signal-hub.html)

Tägliche, gerankte Ideen-Liste aus fünf Quellen: PDF (Traderfox-Magazine aus dem iCloud-Ordner), Mail (Traderfox-Signale via IMAP/freenet, Passwort im macOS-Schlüsselbund signal-hub-imap), Finviz (8 Screener: Momentum, VCP, SEPA, Zanger, Darvas, Livermore, High-Tight-Flag, Pullback), Markets 360 und der Trend-Screener (beide seit 2026-07-17 als eigene Rohsignal-Quellen eingebunden, siehe Datenquellen-Absatz unten).

📖 Betriebsanleitung (seit 2026-07, Änderungshinweis aufklappbar seit 2026-08-27): Im „⚙️ Aktionen"-Akkordeon verlinkt „📖 Anleitung" eine dedizierte Anleitung als Claude-Artifact — Tagesrhythmus, Sortierungen, Badge-Lexikon, Hebel-Ampel, Setup-Analyse, der Weg einer Idee, Wochen-Dossier und als Abschnitt 12 die Trainingsphase (höchstens drei Kandidaten pro Woche aus dem Dossier, unter der Woche abends nur diese im Kauf-Review prüfen, keine Hebelprodukte) — Signal-Hub-Anleitung. Der einzige echte Änderungshinweis der Seite (ARMED-vor-BREAKOUT-Tausch, Abschnitt 03) steckt seither in einem aufklappbaren Kasten statt offen im Fließtext; die übrigen „Neu seit …"-Absätze und Technik-Hinweise bleiben unverändert sichtbar, da sie aktuelle Funktion statt reiner Historie beschreiben. Design-System und URL siehe CLAUDE.md.

📱 Header-Kollision behoben + Aktionen-Menü (seit 2026-08-23, Systemprüfung). Der fixierte „🏠 Start"-Chip (#mts-home-btn, position:fixed, außerhalb des Header-Boxmodells) und der Titel „📈 Daily Signal Hub" standen beide bei x≈8–20 px und überlappten sich messbar — der Effekt hing an festen Pixelpositionen, nicht an der Fensterbreite, betraf also nicht nur mobile Geräte (bei 823 px war „📈 Dail" verdeckt). Fix: header bekommt padding-left: calc(env(safe-area-inset-left,0px) + 100px) statt mehr padding-top — zusätzliche Höhe hätte der zweiten Baustelle widersprochen. Zusätzlich: die sieben Aktionsbuttons (Aktualisieren/Watchlist erstellen/Kauf-Review/Price-Action-Hub/Backtest-Report/Anleitung/Einstellungen) standen vorher offen in der Kopfzeile und stapelten sich auf dem iPhone über eine volle Bildschirmhöhe, bevor die erste Signal-Karte kam — auf einem Werkzeug, das morgens ~30 Sekunden lang genutzt wird, die falsche Reihenfolge. Sie stecken jetzt in einem eigenen „⚙️ Aktionen"-Akkordeon direkt über „Filter & Sortierung" — dasselbe <details>/<summary>-Muster, kein neues Widget, standardmäßig eingeklappt (auch am Desktop, ein Klick reicht).

Score & Faktoren

Unified Score (0–100) aus Stage-2-Trend, relativer Stärke, 52W-Hoch-Nähe, Basis, Volumen, Quellen-Konsens, der Minervini 50/80-Regel (Gap-down-Malus, siehe unten) und der Branchenstärke (seit 2026-07-24 aktiv mit Gewicht 8, ETF-basiert — siehe unten); Smart-Money (Up/Down-Volumen-Verhältnis) und CMF behalten Startgewicht 0 (sichtbar/filterbar, zählen erst nach bewusster Aktivierung im ⚙︎-Menü). Relative Stärke seit 2026-07-24 verfeinert (scorer.py::rs_gewichtet + f_rs_pool_rang): statt eines einzelnen 6-Monats-Fensters eine gewichtete Multi-Fenster-RS über 3/6/9/12 Monate (40/20/20/20 — das jüngste Quartal zählt doppelt, wie beim IBD-RS-Rating; junge Aktien mit kurzer Historie werden auf den verfügbaren Fenstern renormalisiert), und der Faktorwert ist eine 50/50-Mischung aus dem absoluten Wert gegen den Markt-Index und dem Perzentil-Rang der gewichteten RS im aktuellen Kandidatenpool (Minervini/IBD: RS ist ein Rang — das erkennt Werte, die JETZT Führung übernehmen, statt solcher, die vor Monaten liefen; der absolute Anteil bleibt drin, damit ein schwacher Pool keine Stärke vortäuscht). Das Feld rs_gewichtet steht je Treffer in signals.json. Schwellen: ≥ 70 Kauf-Kandidat, ≥ 50 beobachten. Earnings-Warnung und Depot-Abgleich („hältst du bereits") sind integriert (Depot-Abgleich nur beim lokalen Mini-Lauf, siehe Cloud-Pipeline unten).

✅ Trend Template als K.-o.-Prüfung (seit 2026-08-29, scorer.py::f_trend_template): Bis dahin kannte der Signal-Hub Minervinis Trend Template nur als stetigen Score-Faktor (stage2_trend, Gewicht 25) — eine Aktie mit 6 von 8 Kriterien konnte damit Kauf-Kandidat werden, obwohl Minervini alle 8 als Pflicht beschreibt. Außerdem fehlte das RS-Rating als Kriterium 8 vollständig: relative Stärke war nur ein gewichteter Faktor, kein Bestandteil des Templates. Beides ist jetzt nachgezogen — Zählweise und Schwelle (rs_min 70) sind bewusst identisch zu Markets 360 (scoring/trend_template.py), das dieselbe Frage vorher anders beantwortete als der Signal-Hub. Ergebnis steht je Treffer unter signals.json → trend_template (Einzelkriterien, erfüllte Zahl, RS-Rating, pass).

Standardmäßig nur messend. config.json → trend_template.strikt steht auf false: die Prüfung wird berechnet und mitgeschrieben, ändert aber weder Score noch Einstufung. Grund ist die Backtest-Pflicht — die bestehende Gewichtung ist durch score_faktoren_backtest.py gemessen, die K.-o.-Variante nicht. Auf true gestellt kann ein Treffer ohne bestandenes Template kein Kauf-Kandidat mehr sein (Herabstufung auf „Beobachten", der Score selbst bleibt unverändert). Zur Einordnung: an einer Stichprobe von 59 hochbewerteten Kandidaten bestanden 15 das volle Template — scharf geschaltet wäre das ein erheblicher Eingriff, deshalb erst nach Beleg durch die Kohorten *_tt_pass/*_tt_fail. Terminlage (Stand 2026-09-12): Die Freigabebedingung ist erstmals ab 20.09.2026 prüfbar — tt_pass wird seit dem 30.08. ins Pivot-Logbuch geschrieben, und eine Kohorte gilt ab 21 Tagen als gereift. Bis dahin zeigen die Kohorten *_tt_pass/*_tt_fail im Backtest-Report zwangsläufig n = 0; das ist kein Fehler, sondern der normale Reifeverlauf.
🌐 RS-Rating ist seit 2026-08-30 marktweit (scorer.py::f_rs_marktweit) — vorher war es das Perzentil innerhalb des heutigen Kandidatenpools, also unter ohnehin auf Momentum vorgefilterten Treffern. „RS ≥ 70" hieß damit „besser als 70 % der heutigen Kandidaten" statt „besser als 70 % des Marktes" — an der 70er-Schwelle nachweislich in 37 % der Fälle die falsche Ja/Nein-Entscheidung. Die Zahl kommt jetzt aus dem breiten Universum von Markets 360 (~630 bewertete Werte), ohne einen einzigen zusätzlichen Kursabruf: dessen Export wird ohnehin schon eingelesen.
  • Direkt (rs_rating_quelle: "markets360"): Ticker steht im Export — Rating exakt übernommen. Betraf zuletzt 217 von 546 Treffern.
  • Kalibriert ("kalibriert"): Ticker fehlt dort (v. a. Small-/Mid-Caps aus Finviz/TraderFox). Aus allen Tickern, die in beiden vorkommen, entsteht eine monotone Umrechnungskurve rs_gewichtet → Rating. Das funktioniert so gut, weil Markets 360 nach derselben Formel rankt (40/20/20/20 über 3/6/9/12 Monate): 5-fache Kreuzvalidierung ergab einen Medianfehler von 0 Punkten und 99 % richtige Ja/Nein-Einordnung an der 70er-Schwelle — gegenüber 63 % beim alten Pool-Rang.
  • Nur Pool ("pool"): weder das eine noch das andere möglich. Solche Treffer gelten für Kriterium 8 als nicht bewertbar, ausdrücklich nicht als durchgefallen.
Bewusst unverändert: der Score-Faktor relative_staerke rechnet weiter mit dem Pool-Rang — der ist dort per score_faktoren_backtest.py kalibriert, und ein Umbau würde jeden Score im System verschieben. Betroffen ist nur die Zahl, die das Trend Template prüft.

Grenze: Markets 360s Universum sind Index-Werte, also große Standardwerte. Ein Rating heißt damit „wo dieser Wert unter großen Standardwerten ränge" — deutlich näher an Minervini als der Kandidatenpool, aber kein IBD-Rating über 8000 Titel.

🏭 Branchenstärke (scorer.py::f_sektor_staerke, seit 2026-07-24 ETF-basiert und mit Gewicht 8 aktiv — ersetzt die pool-interne Version vom 18.07.): Minervini-Heuristik „erst führende Branchengruppe, dann Marktführer darin". Maßstab ist jetzt der Rang des zugehörigen SPDR-Sektor-ETFs unter allen 11 S&P-500-Sektoren (XLK, XLF, XLV, XLY, XLP, XLE, XLI, XLB, XLU, XLRE, XLC — Symbole am 24.07.2026 gegen sectorspdrs.com/Yahoo verifiziert), gemessen mit derselben gewichteten 3–12-Monats-RS wie bei den Aktien (rs_gewichtet). Nur 11 zusätzliche, tages-gecachte Yahoo-Abrufe je Lauf. Warum der Umbau: Der Kandidatenpool ist bereits momentum-selektiert — ein pool-interner Branchen-Rang (alte Version) sagte deshalb wenig aus; der ETF-Rang misst, ob die Gruppe den Gesamtmarkt führt. Den „Marktführer darin"-Teil übernimmt seit demselben Umbau der Pool-Perzentil-Anteil der relativen Stärke (f_rs_pool_rang). Sektor-Namen aus Nasdaq-Profil („Finance", „Telecommunications") und Yahoo („Financial Services", „Communication Services") werden per Substring-Zuordnung auf die ETFs gemappt; EU-Werte nutzen dieselben US-Sektor-ETFs als Näherung (Sektortrends korrelieren global). Sektor unbekannt/nicht zuordenbar → neutral (0,5). Gewicht im ⚙︎-Menü weiter frei justierbar; ob die 8 gerechtfertigt sind, prüft künftig die Trefferquoten-Auswertung (siehe unten).

🛑 Stop-Distanz (pivot.py::_stop_info, seit 2026-07-18): Jeder ARMED/BREAKOUT-Treffer zeigt jetzt einen Stop-Preis + Stop-% (Badge „🛑 Stop X (-Y %)"). ARMED: Tief der letzten engen Verengung (ENG_FENSTER); BREAKOUT: Tief der Basis vor dem frischen Ausbruch. Bewusst kein Kursziel/R:R dazu — Minervini/Livermore traden ohne festes Ziel (trailen bzw. in Stärke verkaufen), ein erfundenes Ziel wäre reine Spekulation.

🧱 Basis-Zählung (scorer.py::basis_anzahl, seit 2026-07-18): grobe Näherung der Minervini-Basiszahl (1./2./3./4. Basis) über die volle Kurshistorie (nicht das auf 126 Tage gekürzte Dashboard-Chart-Fenster) — zählt abgeschlossene Zyklen „Hoch → Rücksetzer ≥10 % → neues Hoch" seit dem letzten 52-Wochen-Tief. Rein informativ, kein Score-Faktor; Dashboard zeigt ab der 3. Basis ein Vorsicht-Badge, ab der 5. ein Klimax-Warn-Badge („🧱 N. Basis ⚠️"). Test gegen den kompletten lokalen Cache (~14.800 Ticker, alle Marktphasen): Verteilung nimmt erwartungsgemäß mit steigender Basiszahl ab (0er/1er/2er am häufigsten, >8 selten) — plausibel, aber wie bei Branchenstärke eine Heuristik ohne eigenen Backtest.

📈 Extended-Filter (scorer.py::extended_pct, seit 2026-07-18): der Minervini-Standardtest für „zu weit gelaufen" — Prozent-Abstand des Kurses zur SMA50. Ab 25 % Abstand ein Vorsicht-Badge, ab 50 % ein Klimax-Warn-Badge („📈 Extended +N% ⚠️"), rein informativ, kein Score-Faktor. Anders als Branchenstärke/Basis-Zählung gibt es dafür einen echten Filter: Checkbox „📈 Extended ausblenden" (Standard: an) blendet alle Treffer ≥25 % über SMA50 komplett aus der Liste aus — beantwortet die Frage „welcher Filter zeigt mir nicht-überdehnte Setups". Test gegen den kompletten lokalen Cache (~13.100 Ticker mit genug Historie): 85 % normal, ~12 % im 25–50 %-Vorsichtsbereich, ~3 % ≥50 % stark extended — plausible Verteilung, aber ebenfalls eine Heuristik ohne eigenen Backtest. Was weiterhin fehlt: eine explizite Stage-1/2/3/4-Klassifizierung — der Faktor „① Stage-2-Trend" zeigt nur, wie viele von 8 Kriterien erfüllt sind, unterscheidet aber nicht, ob ein schwacher Wert an einer Stage-1-Basis oder einem Stage-3/4-Abwärtstrend liegt.

⛔ Minervini 50/80-Regel (scorer.py::f_gap80, seit 2026-07-22): nach einem scharfen Gap-down auf hohem Volumen (typischerweise eine Erdenttäuschung) hat eine Aktie laut Minervini eine hohe Wahrscheinlichkeit, innerhalb der folgenden ~8 Monate noch einmal unter das Gap-Tief zu fallen — kein „billig" Nachkaufen. Sucht je Ticker den jüngsten Tag im Fenster (Standard 168 Handelstage ≈ 8 Monate) mit Gap (Vortages-Close → Open) ≤ −10 % und Volumen ≥ 1,5× Ø50-Tage-Volumen; ein einzelnes Symbol kann so einbrechen, ein ganzer Index praktisch nie (nur Ereignisse wie 1987/2008/2020) — die Schwelle wirkt dadurch selbst schon als Einzeltitel- vs. Markt-Crash-Filter, ganz ohne Abgleich mit historischen Earnings-Terminen (die liegen nicht vor, earnings_info kennt nur den nächsten Termin).

Anders als Basis-Zählung/Extended-Filter ist das ein echter Score-Faktor (Startgewicht 10 von 100 in config.json → score_gewichte.minervini_5080): der Malus ist direkt nach dem Gap am stärksten und klingt bis zum Fensterende linear ab. Dashboard-Badge „⛔ 50/80: −N% vor MW" (rot/fett solange ≤8 Wochen alt, sonst zunehmend transparent) zeigt Gap-Größe, Volumen-Vielfaches und verbleibende Vorsichtsdauer als Tooltip. Einstellbar im ⚙︎-Menü unter „Filter & Quellen" (an/aus, Schwelle, Volumen-Faktor, Fenster). Eigener Filter „⛔ 50/80 ausblenden" (Standard: an) blendet betroffene Treffer wie beim Extended-Filter komplett aus der Liste.

Zwei Zusatz-Badges kombinieren das mit bestehenden Signalen: „🔥 Klimax-Risiko" (scorer.py::klimax_warnung) erscheint, wenn zusätzlich schon die 3./4. Basis erreicht ist (spätes Topmuster); „🩳 Short-Setup" (scorer.py::ist_short_setup, Kurs unter SMA50/150/200) ist ein rein optionaler Zusatz-Layer, standardmäßig ausgeblendet, über die Checkbox „🩳 Short-Kandidaten anzeigen" einschaltbar — der Signal-Hub bleibt sonst ein reines Long-Tool.

❄️ Frische-Gewichtung des Quellen-Konsens (config.json → frische_gewichtung, scorer.py::_decay, aktiviert seit 2026-07-19): beantwortet die Frage, wie das System zwischen frischen und veralteten PDF-/Mail-Erwähnungen unterscheidet. Zwei Schutzebenen: harte Altersgrenze bereits vor der Aufnahme als Rohsignal (PDF max_alter_tage 21 Tage, Mail 14 Tage, geprüft über Datei-mtime und ein im Dateinamen kodiertes Datum), plus jetzt zusätzlich ein exponentieller Verfall innerhalb dieses Fensters — jede Erwähnung zählt nach Halbwertszeit (Standard 10 Tage) weniger in den Konsens-Faktor. Empirisch geprüft (Vorher/Nachher-Vergleich, 1371 Ticker, 19.7.2026): kein einziger der 180 bestehenden Kauf-Kandidaten fiel unter die 70-Punkte-Schwelle (größter Rückgang ENVA 72,4→70,1), 76 % aller Ticker blieben komplett unverändert, größter Ausschlag −3,4 Punkte. Grund: Quellen-Konsens macht nur 10 von 100 Punkten aus — die dominanten Faktoren (Stage-2, RS, Volumen, Basis, Nähe-Hoch) werden ohnehin bei jedem Lauf komplett neu aus aktuellen Kursdaten berechnet, unabhängig vom Erwähnungsdatum. Eine echte, dauerhaft laufende Trendaktie kann durch den Verfall also nicht aus dem Kauf-Kandidat-Bereich fallen.

Bewusst zurückgestellt: Smart-Money/CMF dauerhaft ins Score-Gewicht zu übernehmen (bisher nur Ad-hoc-Checkbox im Dashboard) — dafür fehlt bislang ein Beleg aus echter Nutzung/Performance, genau wie bei Branchenstärke. Erst hochgewichten, wenn sich das im Alltag bewährt hat, nicht auf Verdacht.

🔎 Sofort-Überblick auf jeder Kachel (seit 2026-08-03): dieselbe kompakte Punkte-Optik wie in setup-detail.html (grün/gelb/rot-Punkt je Faktor + Klartext-Tooltip) ist jetzt direkt sichtbar auf jeder Karte, nicht mehr hinter „📈 Chart & Faktoren" versteckt — die vorherige Balken-Darstellung dort war dadurch redundant und wurde entfernt (Chart bleibt aufklappbar). Kein neuer Faktor, nur eine sofort sichtbare Zusammenfassung der bestehenden 11 Score-Faktoren.

Pivot-Detektor & Validierung

🎯 Pivot-Armed-Detektor (Signal-Hub/src/pivot.py + pivot_screener.py): sucht die Entstehung eines Ausbruchs statt wie der Unified Score einen schon bestätigten Trend. Vier Zustände: BREAKOUT (frische Pivot-Kreuzung auf ≥ 1,4× Volumen, max. 4 % über dem Pivot), ARMED (enge VCP-Endkontraktion + Volumen-Dry-up, mit Supply-Gate gegen Overhead-Angebot), CHEAT (siehe eigener Absatz unten) und WATCH (Basis bildet sich noch). Ausgabe Signal-Hub/data/pivot.json, Push bei neuen ARMED/BREAKOUT-Fällen, im Dashboard eigene Badges/Filter/Sortierung.

DatumÄnderungGrund
07-22CHEAT-Status eingeführtzusätzlicher früher Pivot-Zustand, siehe eigener Absatz unten
07-24BREAKOUT-Fenster von 8 % auf 4 % über Pivot verengt, ARMED bekommt Supply-GateBacktest zeigte: BREAKOUT weit deutlich schlechter als nah; niedriger Supply-Score trennt die ARMED-Spreu stark
08-09Bugfix: --notify kam nie bei pivot_screener.py an; Anti-Spam-Zustand zusätzlich in R2 gesichertPush war seit Einführung faktisch tot; ohne R2-Sicherung meldete jeder frische Cloud-Runner bereits scharfe Ticker erneut als „neu"

📊 Pivot-Backtest / Evaluations-Loop (Signal-Hub/src/pivot_backtest.py): misst per Retro- und Forward-Test, ob die Pivot-Zustände tatsächlich Forward-Return bringen. Der Retro-Test (sofort verfügbar, Walk-Forward über die 126-Tage-Charts) ist durch die heutige Momentum-Vorauswahl systematisch verzerrt und dient nur zum Tuning von Schwellen; verlässlich ist ausschließlich das Forward-Logbuch, das echte ARMED/BREAKOUT-Fälle mit Datum/Kurs mitschreibt und wöchentlich (So 08:15) gegen aktuelle Kurse auswertet. CHEAT-Picks fließen bewusst nicht ins Forward-Logbuch ein. Aktueller Stand (n≈83–90 bei 4 Wochen): ARMED schlägt BREAKOUT deutlich (71 % vs. 34 % Win-Rate) — Dashboard-Sortierung und Top-Setups-Aggregator reihen ARMED deshalb vor BREAKOUT.

DatumÄnderungGrund
07-24Tuning-Splits eingeführt (BREAKOUT nah/weit, ARMED nach Supply-Score/Megaphone)erste Retro-Messung zeigte: BREAKOUT_DEHN zu weit gefasst, Supply-Score trennt stark, Megaphone trennt nicht
08-02Forward-Test dreht das Bild (ARMED 71 % vs. BREAKOUT 34 % Win-Rate) → Ranking auf ARMED vor BREAKOUT umgestelltdie Retro-Zahlen hatten BREAKOUT durch den Universums-Bias zu gut aussehen lassen
08-16Forward-Logbuch geräteunabhängig über R2 gesichertlag vorher nur lokal am Mac mini — jeder Cloud-Runner startete mit leerem Logbuch
08-21Earnings-Fenster-Validierung ergänzt (Split „earnings-nah"/„earnings-fern")prüft Minervinis Regel „nicht kurz vor Earnings kaufen" messbar; kein rückwirkendes Backfill möglich
10-09Stand nach dem Dubletten-Fix: ARMED 28,6 % (n = 4.287) vs. BREAKOUT 32,0 % (n = 147) Win-Rate nach 4 W, beide im Schnitt hinter dem Index — Unterschied laut Statistik-Skeptiker (04.10.) Rauschen. Ranking ARMED vor BREAKOUT bleibt (ohne Beleg nicht ändern), ist aber kein Qualitätsurteil mehr; Signal-Hub-Anleitung entsprechend korrigiertdie 71/34-Zahlen vom 08-02 stammten aus einer Auswertung mit Wochenend-/Feiertags-Dubletten
⚠️ Behoben 2026-08-23: der Forward-Test lief nie in der Cloud. Beim Verifizieren der neuen Beleg-Regel (siehe „Verschärfte Beleg-Regel" beim Startseiten-Panel) fiel auf, dass die Live-Seite bei jedem Setup „kein Beleg" anzeigte. Ursache: src/run.py rief in der Pipeline nur pivot_backtest.py --log auf, nie --evaluate — Letzteres lief ausschließlich wöchentlich auf dem Mac mini (com.maick.pivot-backtest.plist). Auf einem frischen Cloud-Runner existierte data/pivot_backtest.json deshalb gar nicht, top_setups.py fand nichts und lieferte für jeden Eintrag backtest: null. Die ⭐-Kern-Setup-Auszeichnung konnte im Cloud-Betrieb also nie greifen — sie funktionierte nur bei lokalen Mac-mini-Läufen. Zweite Folge derselben Ursache: die auf Pages ausgelieferte pivot_backtest.json stammte am 2026-08-23 vom 09.08., weil der wöchentliche R2-Upload nur bei eingeschaltetem Mac mini läuft. Beides verstieß gegen den obersten Grundsatz „Geräteunabhängigkeit hat Vorrang".

Fix — drei kleine Änderungen, kein neuer Mechanismus:
  • src/run.py ruft jetzt zusätzlich pivot_backtest.py --evaluate auf. Kostet praktisch nichts: evaluate() zieht keine zusätzlichen Yahoo-Daten, sondern liest den Tages-Cache, den scorer.py im selben Lauf ohnehin schon gefüllt hat.
  • pipeline.yml holt vor dem Lauf den zuletzt deployten Stand aus R2 (_deploy/signal-hub/pivot_backtest.json) nach data/. Nötig, weil --evaluate seine Forward-Zahlen in die bestehende Datei merged — ohne diesen Restore entstünde auf dem frischen Runner eine Datei ohne den RETRO-Block, was den Backtest-Report auf der Live-Seite kaputtgemacht hätte.
  • run.py lädt bei lokalen Läufen pivot_backtest.json/.js mit nach R2 hoch (Liste in upload_to_r2.sh-Aufruf erweitert), damit ein Mac-mini-Lauf nicht frischere Zahlen hat als die Live-Seite.
Der Rest war bereits verdrahtet: das signal-hub-data-Artefakt enthält den ganzen data/-Ordner (also auch die neue Datei), der price-action-hub-Job lädt es herunter, und der deploy-Job schiebt alle *.json/*.js nach _deploy/signal-hub/. Der RETRO-Lauf bleibt bewusst wöchentlich und lokal — er ist der teure (~560 Charts) und ohnehin durch die Universums-Vorauswahl verzerrte Teil; der Forward-Test ist der, der täglich reifen muss.

🎯 Trefferquoten-Auswertung für den Momentum-Score (Signal-Hub/src/score_backtest.py): das Gegenstück zum Pivot-Forward-Test, aber für den Score selbst — beantwortet per Episoden-Dedupe und Vergleich gegen den Markt-Index (edge_idx), ob Tier A (Kauf-Kandidat) nach 4/8/12 Wochen tatsächlich besser läuft als Tier B (Beobachten). Läuft täglich direkt nach dem Scorer, Ausgabe Signal-Hub/data/score_backtest.json, im Dashboard als eingeklapptes Panel mit Win-Rate-Balken je Tier/Horizont.

DatumÄnderungGrund
07-24Score-Backtest eingeführtGegenstück zum Pivot-Forward-Test, diesmal für den Score selbst statt für die Pivot-Zustände
08-16Logbuch geräteunabhängig über R2 gesichertlag vorher nur lokal — das Panel blieb auf der Cloud-Version dauerhaft leer
08-17Win-Rate-Balkendiagramm je Tier/Horizont ergänztgleiches Chart-Prinzip wie in den anderen vier Backtest-Panels

🚦 Markt-Regime-Backtest (seit 2026-08-21, Signal-Hub/src/regime_backtest.py): Bislang wurde jeder einzelne Signal-Typ (Pivot, Score, Hebel-Ampel, Price-Action-Muster, Rotation-Setups) unverzerrt forward-getestet — die Grün/Gelb/Rot-Marktampel selbst, die über 25/50/100 % Exposure entscheidet und damit der größte Einzelhebel im gesamten System ist, aber nie. Gleiches Grundprinzip wie hebel_backtest.py (--log/--evaluate, bewusst ohne Retro-Modus — eine rückwirkende Rekonstruktion müsste distribution_days()/follow_through_day() über jeden historischen Tag neu laufen lassen, für einen ersten Baustein unverhältnismäßig aufwendig): hängt täglich den Leitindex-Kurs (^GSPC für USA, ^STOXX für Europa — derselbe „leit"-Index wie in scorer.py::markt_regime()) am Tag der jeweiligen Regime-Einstufung ans Forward-Logbuch (regime_logbuch.json, R2-gesichert von Anfang an, nicht erst nachträglich retrofittet), wertet gereifte Einträge (≥21/50/78 Kalendertage) gegen den aktuellen Kurs aus. run.py ruft log_und_evaluate() bei jedem Lauf auf — anders als bei den ticker-basierten Backtests kommen aber nur zwei neue Einträge pro Tag dazu (ein Regime-Wert je Markt), die Stichprobe wächst also spürbar langsamer und braucht entsprechend länger, bis die Aussage belastbar wird. Im Backtest-Report als vierter Tab „Markt-Regime" sichtbar, gepoolt über USA+Europa (eine Trennung nach Markt würde die ohnehin kleine Stichprobe nochmal halbieren).

🎯 Exit-Regel-Backtest (seit 2026-08-21, pivot_backtest.py::_simulate_exit()): Bislang testeten alle Backtests im System nur den Einstieg (welches Setup kaufen) — simuliert jetzt zusätzlich die Minervini-Ausstiegsstaffel aus dem Trade-Planner (Maick's Trading System.html: T1 +8 %/50 %, T2 +20 %/25 %, T3 +40 %/25 %) Tag für Tag gegen den echten historischen Kursverlauf und vergleicht das Ergebnis mit einfachem Halten bis zum selben 12-Wochen-Stichtag (derselbe Schlusskurs für beide Zahlen, damit der Vergleich nicht durch unterschiedliche Zeitpunkte verzerrt wird). Nutzt Tages-Hoch/-Tief statt nur Schlusskurs — ein Stop/Ziel kann intraday ausgelöst werden, ohne dass der Schlusskurs das zeigt. Keine zusätzliche Yahoo-Anfrage nötig: scorer.py::yahoo_chart() holt bei jedem Aufruf ohnehin schon 2 Jahre komplette Tageshistorie (seit diesem Umbau zusätzlich mit einem Datums-Array je Handelstag, vorher nie extrahiert) — bislang wurde davon nur der letzte Schlusskurs genutzt, jetzt zusätzlich Hoch/Tief/Datum für dieselben ohnehin schon abgerufenen Ticker. Ergebnis wird einmalig pro Logbuch-Eintrag berechnet und dauerhaft gecacht (wie realisiert) — kein erneutes Nachrechnen bei späteren --evaluate-Läufen, die Kosten wachsen nur mit der Zahl neu gereifter Picks, nicht kumulativ mit der Zeit. Nur für ab 2026-08-21 neu geloggte Einträge möglich (ältere haben kein stop-Feld) und erst auswertbar, sobald der Chart tatsächlich 78 Kalendertage nach dem Signal erreicht. Im Backtest-Report als „Exit-Staffel"/„Exit-Hold"-Zeilen in den ARMED- und BREAKOUT-Panels sichtbar.

📐 Index-Vergleich in allen Engines (seit 2026-09-12, Systemprüfung Punkt 5, index_vergleich.py): Bis dahin rechnete nur score_backtest.py den Vorsprung gegen den Leitindex. Pivot-, Muster-, Hebel- und Rotations-Backtest meldeten reine Absolutrenditen — in einem fallenden Markt sieht damit jedes Signal schlecht aus, in einem steigenden jedes gut. „Leader-Aktie 91,7 % Trefferquote" und „Trendbar stark bullisch 21,1 %" waren deshalb gleich wenig belegt, nur in entgegengesetzte Richtungen. Jetzt liefert jede Kohorte zusätzlich edge_idx_avg (Ø-Vorsprung in Prozentpunkten) und edge_idx_win (Anteil der Picks, die den Index schlagen) — dieselben Feldnamen, die der Score-Backtest seit jeher schreibt, damit backtest-vergleich.html alle Engines gleich behandeln kann. Dort gibt es eine neue Spalte „vs. Index" und eine Sortierung danach. Jeder Pick wird gegen den Leitindex seines Marktes gemessen (USA → ^GSPC, Europa → ^STOXX); das Rotations-Logbuch führt keinen Markt und fällt auf den US-Index zurück, weil die Engine ausschließlich mit US-Themen-ETFs und deren Leadern arbeitet. Kosten: zwei zusätzliche Yahoo-Abrufe je Lauf und Engine.

Eine Datei, drei Repos — und ein Test, der sie zusammenhält. index_vergleich.py liegt identisch in Signal-Hub/src/, Price-Action-Hub/src/ und Rotation-Dashboard/src/. Bewusste Duplizierung statt Import über Repo-Grenzen: die drei Engines laufen als eigenständige Jobs mit unterschiedlichen relativen Pfaden (lokal Geschwisterordner, im Cloud-Lauf Secondary-Checkout) — ein Import wäre genau die Art stiller Bruch, die das System schon von den zwei Datenpfaden der Datenfusion kennt. Damit die Kopien nicht auseinanderlaufen, vergleicht tests/test_index_vergleich.py ihre Prüfsummen: wer eine ändert, muss alle drei anfassen, sonst schlägt der Test fehl. Genauigkeit: Liegt eine Datumsreihe vor, wird der Startpunkt exakt über das Signaldatum gesucht — dafür liefern seit demselben Umbau auch die Charts von Price-Action-Hub und Rotation-Dashboard ein dates-Feld (Yahoo schickt die Zeitstempel ohnehin mit, sie wurden bisher nur verworfen). Fehlt die Reihe (alte Cache-Einträge am Umstellungstag), greift die frühere Näherung „Handelstage ≈ Kalendertage × 5/7" statt die Kohorte ganz auszulassen.

Auch die Strategie-Spur ist jetzt index-bereinigt: Die Simulation aus Punkt 4 misst gegen einen festen Stichtag, deshalb reicht dort der normale Index-Vergleich nicht — index_return_fenster() rechnet den Index über genau dasselbe 78-Tage-Fenster. In score_backtest.json steht das Ergebnis unter strategie_sim.vs_index (Index-Rendite im Fenster, Vorsprung der Staffel, Vorsprung beim reinen Halten, Anteil der Episoden, die den Index schlagen). Erst damit ist die Zahl aus Punkt 4 als Leistung des Signals lesbar statt als Abbild der Marktphase.

🗑️ Die Forward-Logbücher haben ihre eigene Datenbasis weggeworfen (behoben 2026-09-13). Sechs Engines kürzten ihr Logbuch nach Eintragszahl (lb = lb[-5000:] bzw. 2.000/8.000) statt nach Alter. Diese Kappe skaliert mit der Treffermenge — und die ist hoch: Bei rund 417 Hebel-, 348 Muster- und 172 Pivot-Einträgen je Tag behielten die Cloud-Logbücher in R2 (_state/) nur 12, 23 bzw. 29 Tage. Jede Episode wurde gelöscht, bevor sie den 4-, 8- oder 12-Wochen-Horizont erreichen konnte. Das war die eigentliche Ursache zweier Rätsel: Der Hebel-Backtest zeigte in der Cloud dauerhaft n = 0 (lokal hatte der Mac nur an zwei Tagen geloggt, blieb unter der Kappe und hatte deshalb reife Fälle — eine R2-Sync-Störung, wie zunächst vermutet, lag nicht vor), und die Pivot-Kohorten für 8W/12W blieben seit Wochen leer.

Verlorener Bestand: Pivot-Episoden vom 02.07. bis 15.08.2026 existieren weder in R2 noch lokal noch in einer Sicherung und sind nicht wiederherstellbar. Genau dieser Verlust verschärfte den Befund aus der Gewichts-Simulation, dass die Datenmenge der eigentliche Engpass ist.

Neue Regel: Alle sechs Logbücher (pivot_backtest.py, regime_backtest.py, hebel_backtest.py, muster_backtest.py, katalysator_backtest.py, rotation_backtest.py) behalten Einträge 120 Tage nach Datum — längster Horizont (78 Kalendertage) plus Puffer, unabhängig davon, wie viele Treffer ein Tag bringt. Zusammen wachsen Pivot-, Muster- und Hebel-Logbuch dadurch auf etwa 15 MB statt bisher 3,7 MB. tests/test_logbuch_aufbewahrung.py prüft statisch, dass keine Kappe nach Eintragszahl zurückkommt, und funktional, dass 10.000 frische Einträge vollständig erhalten bleiben. Das Score-Logbuch war nie betroffen: dort zählt lb[-400:] Tages-, nicht Pick-Einträge.
🗄️ Forward-Archiv für gereifte Pivot-Fälle (seit 2026-09-22). Auch die 120-Tage-Regel kostete noch Evidenz: Ein Fall, der alle drei Horizonte erreicht hatte, fiel nach 120 Tagen aus dem Logbuch und damit aus den Aggregaten — n wäre dauerhaft gegen eine Decke gelaufen. pivot_backtest.py schreibt gereifte Fälle deshalb einmalig in pivot_forward_archiv.json (Pfad pfade.PIVOT_FORWARD_ARCHIV, gespiegelt über R2 _state/ wie die Logbücher — in pipeline.yml sowie scripts/sync_logbuch_pull.sh/_push.sh) und zählt sie von dort weiter mit, samt der Kohorten, denen sie angehörten. Das Logbuch selbst bleibt bei 120 Tagen, die Einzelfall-Liste im öffentlichen JSON bleibt rollierend. Dazu die SEPA-Kohorten auch retrospektiv: Kontraktionen, Enge und Basisdauer (plus Follow-Through-Volumen bei BREAKOUT) werden jetzt auch im Retro-Backtest in dieselben Kohorten sortiert wie im Forward-Test — mit identischen Labels und Schwellen, damit der Vergleich Retro/Forward aussagekräftig ist. Bewusst nicht retrospektiv: Trend-Template (braucht das marktweite RS-Rating des jeweiligen Tages) und institutioneller Trend (13F-Lage des damaligen Quartals) — beides ließe sich nur mit heutigem Wissen rekonstruieren. Filter und Gates bleiben unverändert aus: misst nur, filtert nicht.
📅 Wochenend- und Feiertags-Dubletten in allen Forward-Logbüchern (behoben 2026-10-04). Alle sechs Logbücher (pivot_backtest.py, regime_backtest.py, hebel_backtest.py, muster_backtest.py, katalysator_backtest.py, rotation_backtest.py) trugen als datum das Schreibdatum. Der Abgleich gegen echte Yahoo-Schlusskurse zeigte zwei Folgen. Erstens war das Datum fast immer um einen Tag verschoben: Der Morgenlauf vor Börsenstart sieht den Schluss des Vortags. Zweitens begann die Auswertung jede Episode beim ersten Kurs ab dem Logdatum. Sa, So und Mo landeten so alle auf dem Montagsschluss, ebenso ein Feiertag und der Tag danach (Labor Day 07.09. → 08.09.). Das ergab Dubletten mit identischem Ergebnis: rund 24 % der Rotations-, 21 % der Pivot-, 15 % der Muster-, 26 % der Hebel- und 30 % der Regime-Episoden. Die Kohorten-n waren damit zu hoch, die Trefferquoten aber nicht systematisch in eine Richtung verzerrt.

Korrektur: Neue Einträge führen zusätzlich handelstag, den jüngsten abgeschlossenen Handelstag laut Leitindex des Marktes (index_vergleich.handelstag_fuer()). Der Bar des laufenden Tages zählt nie. Die Doppel-Prüfung beim Loggen läuft über diesen Handelstag, Sa-, So- und Mo-Lauf legen also nur noch einen Eintrag an. datum bleibt das Logdatum, weil die 120-Tage-Aufbewahrung daran hängt. Gestartet wird beim ersten Kurs nach dem Handelstag, das ist derselbe Bar wie nach der bisherigen Regel.

Bestehende Einträge bleiben unangetastet: Die Auswertung zählt je Ticker, Kohorte und Startkurs-Tag nur die erste Nennung (index_vergleich.ist_dublette(), geprüft am echten Ticker-Chart, ein europäischer Wert am US-Feiertag bleibt also eigenständig). Wie viele Dubletten herausfielen, steht im jeweiligen Backtest-JSON (dubletten_gefiltert, beim Pivot forward_dubletten_gefiltert). Im Pivot-Archiv bekommt jeder Fall seinen Starttag (start), Dubletten werden mit dublette_von markiert statt gelöscht. Neue _state/-Dateien gibt es keine, pipeline.yml und scripts/sync_logbuch_*.sh bleiben unverändert. Erstlauf auf den R2-Kopien: Rotation 232, Pivot 982, Regime 13 Dubletten. tests/test_logbuch_handelstag.py und neue Fälle in tests/test_index_vergleich.py sichern das ab. Folge: Kohortenvergleiche aus der Zeit davor, etwa Urteile des Statistik-Skeptikers, beruhten auf zu hohen n und sollten neu geprüft werden. Gewichte und Schwellen wurden nicht verändert.

🌱 „Reift noch" statt verschwundenem Panel (seit 2026-09-13, Systemprüfung Punkt 8, zweiter Teil): Direkte Folge der Kappungs-Korrektur oben — nach dem Reset am 13.09. hatte am 13.09. noch keine Episode in Hebel-Ampel, Price-Action-Muster oder Rotation-Setups den kürzesten Horizont (4W = 21 Kalendertage) erreicht. Die Trefferquoten-Panels in price-action-hub.html, signal-hub.html und rotation-dashboard.html blendeten sich bis dahin bei n = 0 in jeder Kohorte komplett aus (if(!zeilen){ box.style.display='none'; }) — dieselbe Reaktion wie bei einer fehlenden oder kaputten Datei. Ein leeres Logbuch und ein echter Ladefehler waren dadurch nicht zu unterscheiden. Jetzt bleibt das Panel sichtbar und zeigt „🌱 Reift noch — das Logbuch wurde ausgewertet, aber noch keine Episode ist alt genug …" statt zu verschwinden; komplett ausgeblendet wird ein Panel weiterhin nur, wenn die Datei selbst nicht ladbar war. backtest-report.html (Pivot-Kohorten) und der Score-Backtest in score_backtest.json selbst waren davon nicht betroffen — sie formulieren „noch keine gereiften Fälle" bereits seit Längerem pro Kohorte in Fließtext. Der Katalysator-Layer bleibt bewusst ohne eigenes Frontend-Panel (siehe oben) — katalysator_backtest.json wird bei jedem Lauf mit gültiger, wenn auch leerer JSON-Struktur geschrieben; nur ein per touch leer angelegter Platzhalter (Workflow-Artefakt, wenn der ganze Price-Action-Hub-Schritt übersprungen wird) bleibt 0 Bytes groß — unproblematisch, solange nichts diese Datei anzeigt.

📏 Feste Fenster statt „bis heute" (seit 2026-09-13, Systemprüfung Punkt 2): Bis dahin maßen alle Forward-Tests eine Episode vom Signalkurs bis zum Kurs des jeweiligen Auswertungstags und sortierten sie nur in ihren reifsten Horizont (≥ 21/50/78 Kalendertage). Das hatte zwei Folgen: Der 4W-Wert einer Episode änderte sich jeden Tag, und eine Episode zählte immer nur in einem Eimer — solange die Episoden jung waren, blieben 8W und 12W leer, und sobald eine reifte, verschwand sie aus der 4W-Kohorte. Jetzt rechnet jede Engine vom Schlusskurs am Signaltag bis zum Schlusskurs genau 21, 50 bzw. 78 Kalendertage später (jeweils erster Handelstag ab diesem Datum). Eine Episode zählt damit in jedem Horizont, den sie erreicht hat, und ihr Wert dort steht für immer fest. Umgesetzt in allen acht Auswertungen: score_backtest.py, score_faktoren_backtest.py, gewichte_sim.py, pivot_backtest.py, regime_backtest.py, muster_backtest.py, hebel_backtest.py, katalysator_backtest.py, rotation_backtest.py. Die Rechnung liegt zentral in index_vergleich.fenster_returns() — dem Modul, das ohnehin identisch in allen drei Repos liegt und per Prüfsummen-Test zusammengehalten wird; der Index-Vorsprung je Horizont kommt über exakt dasselbe Fenster (fenster_edges()).

Was sich beim Lesen der Zahlen ändert. Alle Forward-Kohorten ab dem 13.09. sind nach der neuen Methode berechnet und mit älteren Zahlen nicht direkt vergleichbar; ältere Werte in diesem Wiki bleiben als datierte Nachweise stehen und werden nicht umgeschrieben. Drei Details sind bewusst so gebaut:
  • Der laufende Handelstag zählt nie als Stichtag. Yahoo liefert für heute bis Börsenschluss einen Intraday-Stand; würde er verwendet, änderte sich ein „fester" Wert am Folgetag doch noch einmal. Fällt der Stichtag auf heute, zählt die Episode erst beim nächsten Lauf.
  • Ohne Datumsreihe im Chart wird nichts genähert. Fehlt sie (etwa ein Cache-Eintrag von vor dem Umbau), wird die Episode für diesen Lauf ausgelassen und die Zahl ausgegeben — ein fester Wert soll exakt sein, nicht geschätzt.
  • Einzelfall-Listen zeigen weiterhin eine Zeile je Episode (horizont/return_pct = längster erreichter Horizont), zusätzlich mit dem Feld fenster für alle erreichten Horizonte — sonst erschiene dieselbe Episode im Backtest-Report dreimal als „bester Einzelfall".
Kein Push-Schwall durch den Methodenwechsel: Da sich 8W/12W beim ersten Lauf schlagartig füllen, hätte push_reife() sofort eine Serie „📊 Pivot-Forward-Test reif"-Nachrichten verschickt, obwohl nur die Messung gewechselt hat. Der Anti-Spam-Zustand nutzt deshalb einen neuen Schlüssel (gepusht_fenster in pivot_eval_state.json), der beim ersten Lauf ohne Push mit den aktuellen Stichprobengrößen vorbelegt wird. Gepusht wird erst wieder, wenn eine Kohorte erstmals reif wird oder um die Hälfte wächst (tests/test_pivot_push_fenster.py).
🧭 Pick und Index messen jetzt denselben Zeitraum (Code-Review 2026-09-13). Beim Review der festen Fenster fiel ein Versatz auf, der schon seit dem Index-Vergleich (Punkt 5) bestand: Der Pick startete beim geloggten Preis, der Index beim Schlusskurs am Signaltag. Stichproben aus Score- und Pivot-Logbuch zeigen, dass der geloggte Preis je nach Logzeit etwas anderes meint — in den USA zu rund 88 % den Vortagesschluss (Morgenlauf vor Handelsbeginn), in Europa nur zu 50–60 %, zu 30–40 % einen Intraday-Kurs und zu etwa 10 % bereits den Signaltagsschluss. Die Tagesbewegung am Signaltag steckte damit meist im Pick-Return, fehlte aber im Index-Return; jeder Index-Vorsprung trug diese Bewegung als Fehler mit.

Behoben: Pick und Index starten jetzt am Schlusskurs des Signaltags, direkt aus dem Chart (index_vergleich._start_index()). Das ist der einzige Anker, der für jede Logzeit passt und nie vor dem Signal liegt — wer das Signal morgens, mittags oder abends sieht, kann zu diesem Kurs noch einsteigen. Der geloggte Preis dient nur noch als Vollständigkeitsprüfung. Im selben Zug nimmt auch das Index-Fenster den laufenden Tag nie mehr als Stichtag (vorher nur der Pick), sodass der Vorsprung genauso fest bleibt wie der Return.

Strategie-Spur konsequent mit umgestellt (exit_simulation.simuliere()): Einstieg zum Signaltagsschluss statt zum geloggten Preis, der 8-%-Stop bezieht sich auf diesen Einstieg, und Stop und Ziele werden erst ab dem Folgetag geprüft — Hoch und Tief des Signaltags liegen vor dem Einstieg. Alle Forward- und Strategie-Zahlen ab diesem Tag sind nach dieser Regel berechnet; ältere Werte im Wiki bleiben als datierte Nachweise stehen. Belegt durch tests/test_index_vergleich.py (Pick und Index laufen identisch → Vorsprung in jedem Horizont exakt null) und tests/test_exit_simulation.py.

Zwei weitere Ausrichtungsfehler aus dem Review von Price-Action-Hub und Rotation-Dashboard (13.09.): Erstens bestimmte der Index Start und Stichtag nach seinem eigenen Kalender. Bei Börsen mit eigenen Feiertagen lagen Pick und Index dadurch einen Tag auseinander — belegt am Londoner Feiertag vom 31.08.2026: Für ein Signal vom 10.08. endete SHEL.L am 01.09., ^STOXX am 31.08. Jetzt sucht der Index den jeweils ersten Bar ab genau den Handelstagen, die der Pick verwendet (index_vergleich.index_return_fuer_pick()); alle Engines übergeben dafür ihren Pick-Chart, auch die Strategie-Spur. Zweitens führt das Rotations-Logbuch keinen Markt, sodass alle Picks gegen den S&P 500 liefen — obwohl 10 von 68 Tickern europäische Werte sind (etwa SHEL.L, ASML.AS, TTE.PA). Fehlt ein gültiger Markt, leitet markt_fuer() ihn jetzt aus dem Börsensuffix ab (Punkt im Ticker = Europa) — dieselbe Regel, die ohlc_history.py schon nutzt; das gilt rückwirkend auch für bereits geloggte Einträge.

🛡️ Strategie-Spur für den Score-Backtest (seit 2026-09-12, Systemprüfung Punkt 4, Signal-Hub/src/exit_simulation.py): Die Exit-Simulation lag bis dahin ausschließlich im Pivot-Backtest — und hatte dort null Ergebnisse geliefert, weil sie ein stop-Feld braucht, das erst seit 2026-08-21 mitgeschrieben wird, und zusätzlich 78 Kalendertage Reife verlangt (erste Zahlen dort also frühestens Anfang November). Die Logik liegt jetzt als eigenes Modul daneben, damit alle Engines dieselbe Konvention nutzen, und score_backtest.py rechnet damit die Tier-A/B-Episoden zusätzlich mit Risikomanagement durch: Stop 8 % unter Einstieg plus dieselbe T1/T2/T3-Staffel. Der prozentuale Stop ist der entscheidende Unterschied — er braucht nur den Einstiegskurs und ist deshalb rückwirkend auf jede bereits geloggte Episode anwendbar, auch ohne eigenes Stop-Feld. Damit beantwortet die Auswertung die Frage, die hinter den stark negativen 12-Wochen-Zahlen steht: wie viel davon wäre real längst ausgestoppt gewesen. Sichtbar als strategie_sim im 12W-Slot von score_backtest.json, mit Anteil ausgestoppter Positionen, erreichten Zielstufen und MFE/MAE (größter Buchgewinn/-verlust im Fenster — hoher MFE bei ausgestoppten Positionen heißt, der 8-%-Stop sitzt zu eng).

Belegte Konventionen statt gesetzter Annahmen. Der Stopabstand ist nicht gewählt, sondern abgelesen: alle 252 Trades in mts_data.json nutzen exakt 8,00 % (Median = Mittelwert, kein einziger Ausreißer). Die Staffel entspricht dem Trade-Planner der Hauptapp. Berührt ein Handelstag Stop und Ziel, zählt der Stop — tagesweise ist die Reihenfolge von Hoch und Tief unbekannt, die optimistische Annahme würde die Staffel besser aussehen lassen, als sie ist. Wichtig beim Lesen: strategie und hold innerhalb von strategie_sim messen beide gegen einen festen Stichtag (Signaltag + 78 Tage) und sind damit direkt vergleichbar; alle übrigen Zahlen der Datei messen weiterhin bis „heute". Genau diese Uneinheitlichkeit räumt Punkt 2 der Systemprüfung später systemweit aus — die Simulation ist dafür schon der erste Baustein.

🧮 Pro-Faktor-Erfolgsanalyse (seit 2026-08-02, Signal-Hub/src/score_faktoren_backtest.py): score_backtest.py testet nur Tier A gegen B — dieses Skript testet die 11 einzelnen Score-Faktoren gegeneinander, um zu beantworten, welche Methoden/Filter überhaupt einen Forward-Return-Edge liefern (nicht nur der Gesamt-Score). Nutzt dieselben Logbuch-Episoden wie score_backtest.py, bucketet aber je Faktor nach Ampel (grün vs. gelb/rot) statt nach Tier — ein Faktor mit echtem Vorhersagewert sollte im grün-Eimer klar bessere Win-Rate zeigen (edge_win); nahe 0 oder negativ heißt, der Faktor trägt aktuell nicht zum Score bei und ist Kandidat für eine Gewicht-Reduktion in config.json → score_gewichte. Braucht das faktoren-Feld, das der Scorer seit 2026-08-02 zusätzlich pro Logbuch-Pick mitspeichert (wert+ampel je Faktor, kein Detail-Text) — ältere Episoden ohne dieses Feld werden übersprungen, deshalb liefert das Skript realistisch erst ab Mitte September 2026 (Episoden brauchen ≥21 Tage) eine erste Auswertung. Läuft täglich mit run.py mit (direkt nach score_backtest.py, teilt denselben Yahoo-Cache) und meldet bis dahin nur „noch keine Episode mit Faktoren alt genug" — kein gesondertes Scheduling nötig, reift einfach mit den Logbuch-Daten. Ausgabe Signal-Hub/data/score_faktoren_backtest.json.

🗂️ Quellen im Score-Logbuch (seit 2026-09-24): Jeder Logbuch-Pick trägt zusätzlich quellen mit den Quellen-Typen des Treffers (z. B. trader-zeitung, megatrend, mail:tagesperf, finviz:Enge Basen VCP, markets360) und dem Datum der jüngsten Nennung (zuletzt). Anlass war ein rückblickender Quellen-Audit über 414 TraderFox-Magazine (Oktober 2025 bis September 2026): Keine Magazin-Nennung schlug den Index belegbar, nach 8 Wochen lagen alle Magazine zusammen im Schnitt 1,05 Prozentpunkte dahinter. Jedes Magazin gegen die übrigen ergab bei agenten/kohorten_test.py „Rauschen“. Die Mail-Typen ließen sich rückwirkend gar nicht prüfen, weil das IMAP-Postfach nur rund einen Monat zurückreicht. Mit diesem Feld lässt sich künftig je Magazin, Mail-Typ und Finviz-Screener eine unverzerrte Forward-Kohorte bilden. Bis dahin bleiben quellen_konsens und mail_signal_gewichte unverändert. Das Feld kostet rund 30 KB je Tageseintrag, ältere Einträge haben es nicht.

⚖️ Gewichts-Simulation (seit 2026-09-12, Systemprüfung Punkt 1, Signal-Hub/src/gewichte_sim.py): Rechnet durch, was andere Score-Gewichte auf den bereits geloggten Episoden an Trennschärfe geliefert hätten — bevor irgendetwas umgestellt wird. Möglich ist das nur, weil das Logbuch die Faktor-Rohwerte führt und der Score eine reine Linearkombination ist (score = Σ(gewicht × wert) / Σgewicht × 100): damit lässt sich jeder Pick mit beliebigen Gewichten exakt nachrechnen, ohne einen einzigen Netzabruf (geprüft: Rekonstruktion trifft den geloggten Score auf ±0,07 Punkte). Sechs Kandidatenvektoren, darunter bewusst eine Kontrollvariante mit gleichverteilten Gewichten — ist sie so gut wie die sortierten, trägt kein Faktor und die Gewichtung ist nicht der Hebel. Jede Variante zusätzlich out-of-sample geprüft (Zeit- und Markt-Schnitt). Reines Analysewerkzeug: läuft nicht in run.py mit und schreibt nichts in die Pipeline. Aufruf einmalig python3 src/gewichte_sim.py --sammeln (holt Kurse), danach python3 src/gewichte_sim.py in Sekunden.

📉 Das Ergebnis der ersten Auswertung: Die Datenlage trägt keine Umgewichtung. Ausgangspunkt war ein deutlicher Befund — die Korrelation zwischen Gewicht und gemessenem Faktor-Edge beträgt −0,31: stage2_trend hat das höchste Gewicht (20) und den schlechtesten Edge (−6,1), cmf den besten (+5,0) bei Gewicht 6. Die Simulation zeigt aber, dass keine der geprüften Umgewichtungen aus dem Rauschen ragt: die Einzel-Episoden streuen mit rund 14 Prozentpunkten, der Standardfehler der Trennschärfe liegt bei 1–2 Pp, und alle Varianten landen bei |t| < 1. Das gilt auch für den Ausgangsbefund selbst: „Tier A schlechter als Tier B" ist mit −0,49 Pp und t = −0,40 statistisch nicht belegt.

Auch die Schwelle ist nicht der Hebel: Trüge der Score Information, müsste die Trennschärfe mit steigender Schwelle zunehmen. Sie springt stattdessen zufällig (65 → +1,06; 70 → −0,49; 75 → −1,17; 80 → +1,10), kein t-Wert über 1. Die einzige formal signifikante Variante (nur_belegt_positiv, t = +2,20) ist die beste aus sechs getesteten — bei sechs Tests liegt die korrigierte Schwelle bei t ≈ 2,64 — und sie würde 8 von 13 Faktoren auf null setzen. Das ist ein Kurvenfit, keine Erkenntnis.

Konsequenz: Statt einer Optimierung nur eine Risikoentscheidung (siehe nächster Absatz). Die Auswertung wird wiederholt, wenn mehr Episoden gereift sind — die heutige Stichprobe umfasst 666 Episoden aus einer Marktphase, und auswertbar ist bislang allein der 4-Wochen-Horizont.

Einzige Änderung an den Gewichten (2026-09-12): institutional_trend und code33 von je 16 auf je 6. Das ist bewusst keine Optimierung, sondern eine Risikoentscheidung: Beide wurden am 2026-09-06 zu Score-Faktoren und bestimmten seither zusammen 25,4 % des Scores — sie tauchen aber in keiner einzigen gereiften Episode auf, ihre Wirkung ist also schlicht unbekannt (und war bei ihrer Einführung auf einer Struktur-Simulation begründet, nicht auf realisierten Kursen). Ihr Anteil sinkt damit auf 11,3 %. Die frei werdenden Punkte werden nicht umverteilt: so bleibt die relative Gewichtung aller übrigen Faktoren exakt wie zuvor und es ändert sich genau eine Sache. Wirkung auf die aktuelle Liste: 76 → 79 Kauf-Kandidaten, 23 Wechsler — die Kandidatenzahl bleibt also stabil.

Zwei Orte, sonst wirkt die Änderung nur halb. Signal-Hub/config.json liegt nicht in Git; der Cloud-Lauf — der die tatsächlich ausgelieferten Signale erzeugt — überschreibt sie aus dem GitHub-Secret SIGNALHUB_CONFIG_JSON. Eine rein lokale Änderung wirkt deshalb nur auf Mac-mini-Läufe. Nach jeder Gewichtsänderung muss das Secret im Repo Miksch52/signal-hub neu gesetzt werden (gh secret set SIGNALHUB_CONFIG_JSON --repo Miksch52/signal-hub < Signal-Hub/config.json), sonst laufen Mac mini und Cloud dauerhaft mit unterschiedlichen Gewichten auseinander — ohne dass irgendwo ein Fehler auftaucht. Dasselbe gilt für Änderungen über das ⚙︎-Menü des Dashboards: die schreiben ebenfalls nur in die lokale Datei.

Forward-Test der Änderung (Backtest-Pflicht): Der Scorer schreibt seit demselben Tag die aktiven gewichte und schwellen in jeden Logbuch-Tageseintrag mit. Ohne diese Marke würde score_backtest.py Episoden aus zwei verschiedenen Gewichtungen in einen Topf werfen und der Vorher-/Nachher-Vergleich wäre nicht mehr rekonstruierbar.

📐 VCP-Feinmessung (seit 2026-08-29, pivot.py): Drei Kennzahlen, die Minervini ausdrücklich verlangt, die der Detektor aber nie gemessen hat. (1) Kontraktionszahl — er nennt 3–4 immer kleinere Rücksetzer als Kern des VCP; gezählt werden lokale Hoch→Tief-Wellen ab 3 % Tiefe innerhalb der erkannten Basis, zusammen mit der Angabe, wie viele Übergänge tatsächlich enger werden („3/4 enger werdend"). (2) Basisdauer — längstes Fenster, in dem der Kurs in einer ±30 %-Spanne bleibt, in Wochen; bewusst dieselbe Formel und dieselben Konstanten wie die SEPA-Checkliste der Hauptapp, damit beide für dieselbe Aktie nie verschiedene Wochenzahlen nennen. (3) Follow-Through-Volumen — Ø-Volumen der Tage nach dem Ausbruch gegen den 50-Tage-Schnitt; bislang wurde nur der Ausbruchstag selbst geprüft, obwohl ein Ausbruch ohne Folgevolumen der klassische Fehlausbruch ist. Alle drei erscheinen im Detailtext und in pivot.json.

Alle drei sind vorerst rein messend. Die Schalter in pivot.py::GATES stehen auf False — kein Status ändert sich dadurch (gegengeprüft: über 528 echte Charts null Abweichung zum Vorstand). Erst wenn die Forward-Kohorten in pivot_backtest.py (*_vcp_ge2, *_basis_ge7w, BREAKOUT_ft_ok) einen belegten Vorteil zeigen, darf einzeln scharf geschaltet werden. Zur Einordnung: von 155 ARMED-Werten hätten 28 die 7-Wochen-Mindestbasis verfehlt.
Grenze des Follow-Through-Werts. Der jüngste Kursbalken ist häufig eine angefangene Handelssitzung — zwei der vier Pipeline-Slots (15:00/17:00) laufen mitten im US-Handel. Sein Volumen ist dann naturgemäß nur ein Bruchteil eines vollen Tages (gemessen: 0,28× statt ~1×), was den Durchschnitt systematisch nach unten zieht. Dasselbe gilt seit jeher für vol_surge. Die Schwelle von 1,30× ist deshalb bis zur gemessenen Kohorte ein Platzhalter aus der Literatur, keine belegte Größe.

⚡ Cheat-Pivot-Status (seit 2026-07-22, pivot.py::_cheat_pivot, nach einem Minervini-Q&A): ein früher Ausbruch vor dem vollen Stage-2-Trend-Template — Minervini nennt das „cheaten": Kurs innerhalb ±10 % der 50-Tage-Linie (statt sauber darüber), frischer Ausbruch über ein kurzes 8-Tage-Basisfenster auf ≥1,3× Volumen. Greift nur, wenn der normale Stage-2-Gate nicht erfüllt ist (sonst hätte ARMED/BREAKOUT/WATCH längst gefeuert) — kein Ersatz, ein separates, kleineres Signal. Minervini: Position klein halten, bis sich eine saubere rechte Seite entwickelt. Badge ⚡ CHEAT, per Checkbox „⚡ Cheat-Pivots einschließen" (Standard aus) zusätzlich in die „nur Pivot-Setups"-Filterung aufnehmbar — CHEAT-Ticker sind dafür bewusst vom Pflicht-Faktor-Check „Stage-2-Trend muss grün sein" ausgenommen (sie erfüllen den per Definition noch nicht). Abschaltbar über config.json → pivot.cheat_aktiv. Wie Branchenstärke/Basis-Zählung eine unvalidierte Heuristik — deshalb bewusst ohne automatischen Push und ohne Forward-Logbuch-Eintrag, bis sich das Signal in der Praxis bewährt hat.

🌀 Megaphone-Filter & 📦 Supply-Score (seit 2026-07-22, pivot.py::_megaphone/_supply_score, rein informational, kein Einfluss auf armed_score): Die bisherige Verengungs-Logik verglich nur das letzte 15-Tage-Fenster mit dem davor — megaphone prüft zusätzlich, ob sich die rechte Seite über drei aufeinanderfolgende Wellen tatsächlich beruhigt (Range nimmt ab) statt V-förmig zu expandieren; Checkbox „🌀 Megaphone ausblenden" (Standard aus) blendet betroffene ARMED-Treffer aus. supply_score (0–1) nähert das Overhead-Angebot: Anteil des Handelsvolumens der letzten 100 Tage, der über dem Pivot stattfand — bewusst ein deutlich längeres Fenster als das 25-Tage-Basisfenster selbst, sonst wäre der Pivot (per Definition das Hoch dieses Fensters) trivial fast nie überschritten. Als Tooltip-Zusatz im bestehenden ARMED/BREAKOUT-Badge sichtbar, kein eigener Filter — wie Branchenstärke eine unvalidierte Heuristik.

🕯️ Kerzencharts in den Kacheln (seit 2026-07-24): Das Minichart je Kachel (~6 Monate) zeigt statt der bisherigen Schlusskurs-Linie echte Candlesticks — dieselbe Darstellung und Farben (grün auf / rot ab) wie im Price-Action-Hub (candlechart()), damit beide Apps dasselbe Chartbild sprechen; SMA50/150/200-Linien und Volumenbalken bleiben unverändert. Dafür persistiert scorer.py::_chartdaten jetzt zusätzlich Open/High/Low je Treffer (Felder o/h/l im chart-Block, signals.json wächst dadurch von ~7 auf ~10 MB). Abwärtskompatibel: fehlen die neuen Felder (ältere signals.js, z. B. auf Pages bis zum nächsten Cloud-Lauf), rendert minichart() automatisch weiter die alte Linien-Variante. Pivot-Detektor/Backtest lesen aus dem Chart-Block weiterhin nur c/v/SMA — keine Verhaltensänderung.

📱 Erstladung auf Mobilfunkmaß gebracht (seit 2026-09-13, Systemprüfung Punkt 9): Genau dieser chart-Block machte gemeinsam mit drei nur von setup-detail.html gelesenen Feldern (trend_template/institutional/institutional_trend) rund 85 % der Dateigröße aus — für eine Übersicht über ~535 Ticker, die davon nur den einklappbaren Mini-Chart tatsächlich zeigt (Setup-Analyse liest die drei anderen Felder weiterhin). scorer.py schreibt seither zusätzlich signals_uebersicht.json (dieselbe Struktur ohne diese vier Felder) und signals_charts.json (chart separat, nach Ticker geschlüsselt) — signals.json selbst bleibt unverändert, die Setup-Analyse liest unverändert daraus. Die Kachel-Übersicht lädt jetzt signals_uebersicht.json; ein Mini-Chart lädt signals_charts.json erst beim ersten Aufklappen nach (chartAufklappen(), einmal für die ganze Seite gecacht — auf Desktop-Breiten unverändert, weil dort ohnehin alle Charts automatisch aufklappen, siehe Absatz oben). Die vier ohnehin unabhängigen Ladefunktionen (Signale, Pivot, Pivot-Historie, Top-Setups) laufen zusätzlich parallel statt nacheinander. Gemessen mit Gzip (wie Cloudflare Pages tatsächlich ausliefert): signals_uebersicht.json ~117 KB statt ~1,44 MB für die volle Datei — der für die Übersicht blockierende Anteil der Erstladung sinkt um Faktor ~12, deutlich unter das Ziel von 500 KB.

📈 Leadership-Breadth, Score-Sparkline & Drop-Liste (seit 2026-07-22, Signal-Hub/data/pivot_history.json, geschrieben von pivot_screener.py::aktualisiere_history): eine kompakte Tages-Historie NUR für BREAKOUT/ARMED/CHEAT-Ticker (WATCH bewusst ausgeschlossen — zu breit), auf die letzten 15 Kalendertage zusammengeschnitten, ein Eintrag pro Kalendertag (mehrere Läufe am selben Tag überschreiben denselben Schlüssel). Drei Auswertungen im Dashboard, alle aus derselben Datei: Leadership-Breadth — Kopfzeile „🎯 N aktive Setups (vor 7T: M · ↑/↓/→)", ein Frühindikator für Marktbreite unabhängig von einem offiziellen Follow-Through-Day (Minervini: „führende Aktien haben erste und letzte Stimme"). Score-Sparkline — kleine Farbbalken-Reihe je Kachel (🟠 ARMED/🔴 BREAKOUT/🟣 CHEAT der letzten Tage), reines CSS ohne externe Lib. Drop-Liste — eingeklappter Abschnitt „📉 Kürzlich rausgefallen" oberhalb der Trefferliste: Ticker, die gestern noch ARMED/BREAKOUT waren und es heute nicht mehr sind (Comeback-Watch/Frühwarnung), als eigener Abschnitt statt Teil der Haupt-Filterpipeline, da sie per Definition nicht mehr im gefilterten Grid auftauchen würden. Wie pivot.js gibt es einen pivot_history.js-file://-Fallback; fehlt die Datei, bleiben alle drei Anzeigen einfach leer.

Standard-Ansicht & Filter

🔒 Standard-Ansicht (seit 2026-07-18): Das Dashboard startet nicht mehr ungefiltert, sondern mit einer strengen Kombination für „nur die aussichtsreichsten Chart-/Volumen-Setups im Minervini/Livermore-Stil": Sortierung Pivot-Qualität, Filter nur Pivot-Setups (ARMED/BREAKOUT) und Earnings ausblenden aktiv, Smart Money einrechnen an, sowie die Faktor-Chips Stage-2-Trend und Volumen vorausgewählt (alle Quellen-Typ-Chips ebenfalls aktiv, das schränkt zunächst nichts ein, gibt aber den Hebel, einzelne schwache Quellen gezielt abzuwählen).

Bewusst nicht im Standard (Test mit 1377 Tickern am 18.7.2026, Default-Ergebnis mit vollem Datensatz: 14 Treffer):

  • Basis/VCP — schränkt am stärksten ein, 14→8 Treffer bei sonst gleicher Kombi; per Chip zuschaltbar.
  • Branchenstärke — 14→2, viel zu hart für einen Default; anders als Stage-2/Volumen/Pivot ist dieser Faktor noch unvalidiert (kein Backtest wie beim Pivot-Detektor), als Ranking-Info sichtbar, aber bewusst kein Pflicht-Ausschlusskriterium.
  • „nur neue"/„ohne Depot"/„Multi-Quellen bestätigt" — Frische-/Depot-Filter, keine Chart-/Volumen-Kriterien.

„nur Pivot-Setups" abwählen → 22 Treffer. Alle Regler bleiben frei kombinierbar, das ist nur der Startzustand beim Laden.

„nur Pivot-Setups" umgeht die Volumen-Pflicht (seit 2026-09-15). Ist der Filter aktiv, muss der Faktor-Chip Volumen nicht mehr grün sein — Stage-2-Trend bleibt Pflicht. Grund: ARMED-Setups zeichnen sich gerade durch austrocknendes Volumen aus, der Volumen-Faktor misst dagegen einen Volumenschub. Die Kombination schloss sich praktisch aus: am 15.09.2026 blieben von 94 Pivot-Setups (nach Earnings/Extended/50-80) mit Volumen-Pflicht genau 1 (Cloud) bzw. 0 (Mac mini) übrig — der Hub wirkte leer. „nur Top-Setups" und „nur Hebel-Kandidaten" umgehen die Faktor-Pflicht aus demselben Grund schon länger komplett. Ohne „nur Pivot-Setups" gilt die Volumen-Pflicht unverändert.

Konsolidierung (18.7.2026): Die Filterleiste war als ein einziger undifferenzierter Kontroll-Block unübersichtlich geworden — jetzt in sichtbare Abschnitte gruppiert (Grundfilter · Qualitäts-Filter · Faktoren · Sonstiges), und die 24 Quellen-Typ-Chips stecken standardmäßig eingeklappt in einem eigenen „🌐 Quellen"-Aufklapper mit Live-Zähler (x/24 aktiv), statt permanent drei Zeilen zu belegen.

Finviz-Liquiditäts-Mindestfilter vereinheitlicht (seit 2026-07-18): „Momentum 52W-Hoch" (vorher sh_price_o1) und „High Tight Flag" (vorher sh_price_o5) auf den gemeinsamen Floor sh_avgvol_o500 + sh_price_o10 angehoben, den 5 der 8 Screener ohnehin schon nutzten — strengere Screener (Zanger o1000, Livermore Pivotal o2000/o20) bleiben unverändert, es wurde nur der Floor angehoben, nichts verwässert. Finviz-Filtercodes vorab gegen die offizielle Doku verifiziert (kein eigener Dollar-Volumen-Filter vorhanden, nur avgvol in Stück + Price in $ kombinierbar).

🚦 Markt-Regime-Ampel: Der Scorer berechnet je Markt (USA: ^GSPC UND ^IXIC kombiniert, Europa: ^STOXX) eine eigene Regime-Ampel aus vier Trend-Kriterien (Kurs>MA50, Kurs>MA200, MA50>MA200, MA200 steigend) und schreibt sie nach signals.json → marktregime. Seit 2026-08-29 kommt der 10-Monats-EMA als fünftes, bremsendes Kriterium dazu (siehe „Einheitliche Marktampel"): notiert der Leitindex darunter, wird Grün zu Gelb — was über das Score-Gate unmittelbar auf jeden Einzel-Score durchschlägt (×0,92 statt ×1,0) und über das Push-Gate die Kauf-Signale dämpft. Sie wirkt doppelt: als Push-Gate (rote Ampel unterdrückt neue Kauf-Signale aus dem betroffenen Markt, mit ehrlicher „🔴 Markt rot"-Meldung statt Stille) und als Score-Gate (dämpft den fertigen Score multiplikativ — grün ×1,0, gelb ×0,92, rot ×0,80, einstellbar). Funktioniert überall, auch ohne erreichbaren Mac mini.

DatumÄnderungGrund
07-24Regime-Ampel + multiplikative Score-Dämpfung eingeführt (löst den reinen 8080-Server-Chip vom 22.07. ab)Ampel funktioniert seither überall, nicht nur bei erreichbarem Mac mini
07-27Ampel-Schwelle an die volle Minervini-Ampel angeglichenvorher zeigten Dashboard-Chip und volle Header-Ampel bei Kurs knapp unter MA50 unterschiedliche Farben
08-16USA-Referenz auf zwei Indizes erweitert (^GSPC + ^IXIC)glich die Berechnung an Markets 360/die volle Browser-Ampel an, die USA schon immer kombinieren

🗳️ Politikzyklus-Badge (seit 2026-08-15): kleiner neutraler Chip neben der Marktampel im Header („🗳️ Zyklusjahr 2/4"), berechnet aus dem 4-Jahres-Politikzyklus nach Richard S. Love (Superperformance Stocks, 1977) — US-Präsidentschaftswahlen liegen in allen durch 4 teilbaren Jahren, Zyklusjahr 1 = Jahr nach der Wahl (historisch schwächste Phase), 3 = Vorwahljahr (historisch stärkste Phase), 4 = Wahljahr selbst (scorer.py::politikzyklus_jahr(), schreibt nach signals.json → politikzyklus). Bewusst reiner Kontext-Hinweis — anders als die Markt-Regime-Ampel im Absatz oben beeinflusst er weder den Score (kein neuer Faktor in f_marktampel_dynamik) noch die Push-Unterdrückung in notify.py: die Datenbasis sind nur wenige historische US-Wahlzyklen, ein hartes Signal daraus wäre Overfitting-Risiko. Tooltip nennt Quelle und Charakter der Heuristik explizit. Bewusst nicht nachgebaut bei derselben Anfrage: ein eigener Distribution-/Accumulation-Days-Zähler und ein Follow-Through-Day-Highlight fürs Dashboard (ebenfalls aus Loves Studie abgeleitet) — beides existiert bereits vollständig in der geteilten assets/marktampel.js (siehe „Einheitliche Marktampel" oben, Faktoren „Distribution Days" und „Follow-Through Day"), wäre also Doppelarbeit gewesen. Die eine damals noch offene Lücke — der automatisierte Cron-Push prüfte nur die einfache MA50/MA200-Ampel, nicht die Sentiment-Faktoren — ist im nächsten Absatz geschlossen.

⚠️ Distribution-Days-/Follow-Through-Day-Sentiment-Gate im Push: Der automatisierte Cron-Push (notify.py, läuft ohne Browser) berechnet Distribution Days und Follow-Through-Day serverseitig — 1:1-Portierung derselben Logik wie die volle Browser-Ampel (assets/marktampel.js), auf denselben Indexkursen, die die Regime-Ampel ohnehin schon lädt (keine neue Datenquelle nötig). USA wertet dafür wie die volle Ampel ^GSPC UND ^IXIC kombiniert aus (grün nur wenn beide grün, Distribution Days als Maximum, Follow-Through als schwächster Zustand beider Indizes), Europa bleibt bei ^STOXX allein.

DatumÄnderungGrund
08-15Distribution-Days/Follow-Through-Day serverseitig portiertder Cron-Push kannte bis dahin nur die einfache MA-Ampel, nicht die vollen Sentiment-Faktoren der Browser-Ampel
08-16USA auf zwei Indizes erweitert (^GSPC + ^IXIC)Push zeigte andere DD/FTD-Werte als Dashboard/Markets 360 — unterschiedliche Indexbasis, kein Rechenfehler

Bedeutung: Institutionelle Fonds verkaufen selten an einem Tag, sondern verteilen Positionen über Tage mit erhöhtem Volumen — 5 oder mehr solcher „Distribution Days" binnen 25 Handelstagen sind laut O'Neil/IBD ein verlässliches Frühwarnsignal, das dem eigentlichen Trendbruch (MA50/MA200 kippen) oft vorausläuft: ein Markt kann also technisch noch grün sein und trotzdem schon unter institutionellem Verkaufsdruck stehen. Der Follow-Through Day ist das Gegenstück beim Wiederaufbau: nach einem echten Rücksetzer (≥3 % vom Zwischenhoch) gilt ein neuer Aufwärtstrend laut Minervini/O'Neil erst als bestätigt, wenn ab Tag 4 ein kräftiger Anstieg (≥1,25 %) mit steigendem Volumen auftritt — ohne diese Bestätigung ist ein Rücksetzer nur „möglicherweise vorbei", keine Kaufgrundlage.

Nutzung: Beide Faktoren wirken als zusätzliches, von der Trend-Ampel unabhängiges Gate — Schalter config.json → marktregime → push_bei_sentiment_warnung_unterdruecken (Default true, Cloud-Lauf: optional im SIGNALHUB_CONFIG_JSON-Secret nachziehen, ohne den Schlüssel gilt derselbe Default). Bei sentiment_warnung = true (5+ Distribution Days oder unbestätigter Rücksetzer) hält notify.py Kandidaten aus dem betroffenen Markt zurück — im Dashboard bleiben sie sichtbar. Der Push nennt den Grund explizit: bei komplett leerem Push „⚠️ Sentiment-Warnung – Push-Signale zurückgehalten" mit Klartext-Begründung; bei teilweiser Unterdrückung ein eigener Fußtext „⚠️ N Wert(e) aus … zurückgehalten (Distribution Days/Follow-Through-Day)", getrennt vom „🔴 … unterdrückt (Markt rot)"-Fußtext, weil es zwei unabhängige Ampeln sind. Die schwächere Vorstufe (3–4 Distribution Days, „beobachten") unterdrückt nichts, taucht aber im Morning Brief als Klartext-Zeile hinter dem Ampel-Hinweis auf (_ampel_zeilen()) — bewusst nur der harte 5+-Fall (bzw. unbestätigter Rücksetzer) filtert, damit das Gate nicht zu häufig greift und seinen Frühwarnwert verliert.

🏆 Top-of-the-Day-Modus, Branchenstärke- & Leadership-Persistenz-Sortierung (seit 2026-07-22): drei neue Sortier-Optionen im Dropdown, alle aus bereits vorhandenen Daten — kein neuer Score-Faktor, keine neue Datenquelle. „Branchenstärke" sortiert nach dem bisher nur als Ranking-Info sichtbaren, ungewichteten sektor_staerke-Faktor. „Leadership-Persistenz" sortiert nach der Anzahl der im 15-Tage-Fenster erfassten Tage, an denen ein Ticker BREAKOUT/ARMED/CHEAT war (pivot_history.json, siehe Pivot-Detektor-Abschnitt) — ein Ticker mit mehrtägiger Serie zeigt echtere Führungsstärke als ein Eintagesfund. „🏆 Top-Kombi" kombiniert Pivot-Qualität (50 %), Branchenstärke (30 %) und Multi-Quellen-Bestätigung (20 %, gedeckelt bei 3 Quellen) zu einem Konzentrations-Score. Checkbox „🏆 Nur Top 5 (Fokus-Modus)" erzwingt diese Sortierung und kappt die Liste hart auf 5 Treffer — Minervini handelt konzentriert in wenige echte Marktführer statt breit gestreut; die Standard-Pivot-Sortierung bleibt bei >250 gleichzeitig scharfen Tickern sonst unhandlich für den Tagesgebrauch.

🔗 Multi-Quellen-Bestätigung im Ranking (seit 2026-07-22): das Feld quellen.unabhaengig (Anzahl unabhängiger bestätigender Engines) floss bisher nur in den Filter „Multi-Quellen bestätigt" ein, nicht in die Sortierung. Jetzt zusätzlicher Tiebreaker in der Pivot- und der Score-Standardsortierung: bei gleicher Pivot-Qualität/gleichem Score gewinnt der zusätzlich bestätigte Ticker.

🥇 Code 33 als filterbares Kriterium (seit 2026-08-09): war bis dahin nur live auf der Setup-Analyse-Detailseite verfügbar (siehe dortiger Abschnitt für die Kriterien-Herleitung) — jetzt zusätzlich als Pipeline-Post-Pass direkt im Dashboard. scorer.py::f_code33() ist eine Python-Portierung von cloudflare-worker/mts-cors-proxy.js::fetchCode33() — dieselben drei TraderFox-Schwellen (EPS-Wachstum Quartal ggü. Vorjahr >40 %, Umsatzwachstum >7 %, Nettogewinnmarge um >2 Prozentpunkte verbessert über 2–3 Quartale, seit 2026-09-24 ohne einmaliges Verlustquartal als Vergleichsbasis — siehe Hinweis bei der Setup-Analyse), dieselbe Yahoo-fundamentals-timeseries-Datenquelle, gegen mehrere Ticker mit identischen Ergebniswerten wie die JS-Fassung verifiziert. Läuft nur für Treffer ab Beobachten-Schwelle, 7-Tage-Cache, Deckel 300 Abrufe/Lauf — kein Score-Einfluss (das deckt bereits der bestehende „fundamental"-Faktor ab), reines Zusatzkriterium. Neues Badge „🥇 Code 33" auf der Kachel nur bei vollständig erfülltem 3/3 (bewusst keine Teilerfüllungs-Badges), Tooltip zeigt die drei Einzelwerte gegen ihre Schwellen. Neuer Filter „🥇 nur Code 33" in der Gruppe „Sonstiges" blendet auf dieselbe Bedingung ein.

🏦 Institutioneller Besitzanteil (seit 2026-08-29, SEPA/VCP-Methodikabgleich, scorer.py::f_institutional()): Minervini nennt einen optimalen Bereich von 30–70 % institutionellem Besitz — bis dahin maß das System dazu nichts, nur Verhaltens-Proxys (Akkumulationstage, OBV, CMF, Up/Down-Volumen). Datenquelle Yahoo quoteSummary/defaultKeyStatistics, Feld heldPercentInstitutions — vor dem Bau live gegen 27 Ticker aus USA, Europa (u. a. DE/UK/CH/FR/IT/NL/AT/Nordics) und Asien verifiziert: das Feld war für alle getesteten Einzelaktien vorhanden, fehlte aber zuverlässig bei ETFs (SPY/QQQ). Läuft wie f_code33() als Post-Pass nur für Treffer ab Beobachten-Schwelle, Deckel 300 Abrufe/Lauf. Feld institutional steht für jeden Treffer (anders als code33, das bei fehlenden Daten ganz ausgelassen wird) — fehlende Daten liefern explizit „nicht verfügbar"/„n/a" statt 0 % oder eines Absturzes. Seit 2026-09-15 erweitert (Anzeige siehe Setup-Analyse): zusätzlich die Yahoo-Module, die yfinance für major_holders (majorHoldersBreakdown: institutionsPercentHeld, institutionsFloatPercentHeld, institutionsCount) und institutional_holders (institutionOwnership, Spalte pctChange) nutzt, plus price.marketCap — per Yahoo-Devisenkurs in USD umgerechnet, Londoner „GBp"-Kurse als GBP behandelt (live gegen ULVR.L geprüft). Neue Felder u. a. prozent_float, anzahl, groesse, label, top_holder. Cache pfade.INSTITUTIONAL_CACHE jetzt max. 1 Abruf pro Tag je Ticker (vorher 7 Tage) und wird im Cloud-Lauf nach R2 _state/ gesichert, damit die vier Tagesläufe nicht viermal abrufen. Keine Quartals-Trendmessung: Yahoo liefert nur den aktuellen Stand, keine Historie — Minervinis Zusatzkriterium „steigend über Quartale" bleibt damit unabgedeckt. Kein Score-Einfluss (wie Code 33) — Backtest-Pflicht, erst messen. Bislang bewusst ohne Dashboard-Badge (wie die VCP-Feinmessung/das Trend-Template unten), rein im JSON verfügbar.

📈 Institutioneller Besitz: Richtung über Quartale (seit 2026-08-30, scorer.py::f_institutional_trend + src/institutional_13f.py): Minervinis eigentliches Besitz-Kriterium ist nicht das Niveau, sondern dass der institutionelle Anteil über aufeinanderfolgende Quartale steigt. Yahoo liefert dafür nichts (nur den aktuellen Stand, keine Historie). Zwei Quellen wurden am 2026-08-30 für den automatischen Lauf getestet: Finnhub — stock/ownership und stock/fund-ownership antworten mit 403, institutional-ownership liefert die Upgrade-Seite (im Tarif nicht enthalten); SEC Form 13F — kostenlos, kein Tageslimit, autoritativ, gewählt. Business Quant wurde beim ersten Test ohne registrierten Key (401 „API key is missing") fälschlich als „nur kostenpflichtig" verworfen — mit echtem Free-Key liefert die API anstandslos, ist aber auf 30 Requests/Tag insgesamt limitiert und damit für den automatischen Lauf über hunderte Ticker ungeeignet. Für den manuellen Einzelticker-Fall ist genau das kein Problem, siehe die 🔬 Tiefenanalyse in der Setup-Analyse unten.

Ein eigener Workflow (.github/workflows/institutional-13f.yml) baut die Tabelle viermal im Jahr (20. Feb/Mai/Aug/Nov, einige Tage nach der 13F-Meldefrist von 45 Tagen nach Quartalsende) und committet sie nach daten-13f/ — bewusst versioniert statt nach R2, da klein (~630 KB), rein öffentliche SEC-Daten und so ohne Zusatzschritt in jedem Checkout vorhanden. Der tägliche Lauf liest nur die fertige Tabelle. Ticker→CUSIP kommt aus der kostenlosen SEC-„Fails to Deliver"-Datei (~13.500 Zuordnungen, deckt auch Small Caps ab).

Ohne Normierung wäre die Kennzahl wertlos — das ist der Kern dieser Umsetzung. Roh aggregiert zeigten sechs völlig unabhängige Werte (AAPL/MSFT/NVDA/TSLA/EBAY/CSGP) exakt dasselbe Muster: rund −3 %, dann +15…+25 %. Ursache ist nicht Akkumulation, sondern die Zahl der Melder selbst (3,27 → 3,82 Mio. Meldezeilen). Roh gerechnet meldet die Kennzahl für jede Aktie „steigend". Deshalb wird je Quartalsübergang der Quermarkt-Median abgezogen (gemessen: +0,2 % / −0,6 % / +12,4 % — der letzte Übergang war fast vollständig Artefakt). Gegenprobe nach Normierung: dieselben sechs Werte stehen auf „Folge 1", erfüllen das Kriterium also nicht mehr.
Nur Richtung, kein Niveau. Die absolute Höhe aus 13F ist unbrauchbar — Konzernstrukturen und Wertpapierleihe führen zu Doppelzählung (Apple käme auf 84 % statt der realen ~66 %). Fürs Niveau bleibt Yahoo zuständig (Absatz oben), als reine Kontextzahl. Abdeckung: 444 von 548 Treffern (81 %) — 13F-meldepflichtig sind nur US-Institutionelle ab 100 Mio. USD, alle europäischen Werte fehlen systematisch und werden als „nicht verfügbar" geführt, ausdrücklich nicht als „Kriterium nicht erfüllt". Kein Score-Einfluss, Forward-Kohorte *_insttrend_ja/_nein.

Bewusst OHNE Abgleich gegen die 30–70 %-Bandbreite (Korrektur vom 2026-08-30; ursprünglich war genau dieser Abgleich eingebaut). Minervini misst den Anteil an den ausstehenden Aktien, Yahoo rechnet erkennbar gegen den Streubesitz. Beleg aus dem ersten Live-Lauf (548 Treffer): von 288 verfügbaren Werten lagen 77 über 100 % (Spitze 131,2 %), der Median bei 89,8 % — über 100 % ist nur gegen den Streubesitz erklärbar. Der Vergleich mit 30–70 % stufte dadurch 224 von 288 Werten als „oberhalb 70 % — wenig Kaufkraft-Reserve" ein und trennte damit nichts mehr. Der Prozentsatz wird deshalb als reine Kontextzahl geführt (wie basis_anzahl und extended_pct), Werte über 100 % zusätzlich mit einem Datenbasis-Hinweis markiert. Eine eigene, zur beobachteten Verteilung passende Bandbreite wurde bewusst nicht erfunden — die wäre geraten statt belegt.

Korrektur 2026-09-15: Der Abgleich gegen majorHoldersBreakdown zeigt, dass heldPercentInstitutions identisch mit institutionsPercentHeld ist (Basis ausstehende Aktien, AAPL 66,3 %); der Float-Anteil ist ein eigenes Feld (CELH: 75,2 % gehalten, 105,8 % vom Float). Werte über 100 % sind also nicht „gegen den Streubesitz gerechnet", sondern unplausible Daten (z. B. Doppelzählung in 13F) und werden seither genau so markiert. Die Einordnung niedrig/normal/hoch nutzt größenabhängige Schwellen (siehe Setup-Analyse) und bleibt reine Information ohne Score-Einfluss.

Nebenbei gefundener und behobener Fehler: scorer.py::yahoo_crumb() holte das Session-Cookie bisher von finance.yahoo.com statt wie das bereits produktive JS-Pendant im Worker von fc.yahoo.com — Ersteres lieferte nur ein wirkungsloses „dflow"-Cookie, mit dem der anschließende Crumb-Abruf zuverlässig mit 401 Unauthorized scheiterte (live gegen beide Varianten getestet, nicht nur vermutet). Das war vermutlich die Hauptursache für die im Fundamental-Post-Pass-Log wiederholt beobachteten „Crumb fehlt → Nasdaq-Fallback"-Meldungen. Fix betrifft yahoo_crumb() zentral und verbessert damit automatisch auch den bestehenden Fundamental-Faktor sowie yahoo_profil_earnings() (EU-Sektor/Branche), nicht nur Code 33.

⚖️ Score-Gewichte neu verteilt — institutioneller Zufluss und Code 33 zählen jetzt mit (seit 2026-09-06): Der Score maß bis dahin überwiegend Trend/Momentum (75 von 126 Gewichtspunkten), Geldfluss nur über die Volumen-Bestätigung (15) und fundamentale Substanz mit 8 — obwohl das Systemziel ausdrücklich lautet: führendes Momentum und institutioneller Zufluss und keine Eintagsfliege. Zwei bereits berechnete Messfelder sind deshalb zu eigenen Score-Faktoren geworden: institutional_trend (Gewicht 16, SEC-13F-Quartalsverlauf) und code33 (Gewicht 16, EPS/Umsatz/Marge). Im Gegenzug wurden Faktoren abgesenkt, die fast jeder Treffer erfüllt und die deshalb kaum zwischen Kandidaten unterscheiden (stage2_trend 25→20, minervini_5080 10→4, quellen_konsens 10→2, sektor_staerke 8→2); fundamental geht mit Gewicht 0 in code33 auf, bleibt aber als Zweitmeinung sichtbar (es kann bei fehlender Quartalsreihe auf den Finviz-SEPA-Screener zurückfallen). cmf und smart_money steigen von 0 auf 6 bzw. 4. Gewichtssumme bleibt 126, die Schwellen (70/50) gelten unverändert.

Warum nicht einfach CMF und Up/Down-Volumen hochgewichten? Genau das war der erste Vorschlag — der Faktoren-Forward-Test weist ihnen den höchsten gemessenen Vorhersagewert zu (+8,3 bzw. +3,1 Prozentpunkte Win-Rate-Differenz). Eine Simulation gegen den damaligen Bestand (496 Treffer) hat ihn widerlegt: Sie hätte den Anteil der Top-20-Werte, die beide Systemziele erfüllen (institutioneller Zufluss und Code 33 3/3), von 10 % auf 5 % gesenkt. Grund: Beide messen kurzfristigen Kursfluss über 21 bzw. 50 Handelstage, nicht echten institutionellen Zufluss. Die tatsächlich umgesetzte Variante hebt denselben Anteil auf 35 % (Top 50: 12 % → 20 %), bei praktisch unverändertem Momentum-Niveau (Ø RS-Rating 88 → 86). Tier A schrumpft dabei von 100 auf 76 Werte — die Auswahl wird strenger, nicht breiter. Wichtig: Das ist eine Struktur-Simulation („welche Aktien landen oben"), kein Performance-Beleg. Beide neuen Faktoren laufen deshalb ab sofort im Faktoren-Forward-Test mit (score_faktoren_backtest.py::FAKTOR_NAMEN) — erst diese Kohorte zeigt, ob die Umgewichtung trägt. Werte ohne 13F-Daten bzw. ohne Quartalsreihe (viele EU-Titel) werden bewusst neutral mit 0,5 gewertet, nie mit Malus, sonst kippt der Score systematisch gegen Europa.

📊 Fundamental-Faktor (seit 2026-07-24 echter Score-Faktor mit Gewicht 8, scorer.py::f_fundamental — erweitert das Pass/Fail-Badge vom 22.07.): O'Neils C-A-N bzw. Minervinis SEPA verlangen Gewinn- und Umsatzwachstum — der Score war bis dahin rein technisch. Der neue Post-Pass holt für Treffer ab Beobachten-Schwelle EPS- und Umsatzwachstum aus zwei Quellen mit gemeinsamem 7-Tage-Cache (Fundamentals ändern sich quartalsweise; auch Fehlversuche werden gecacht), max. 300 Abrufe je Lauf:

  • Yahoo quoteSummary/financialData (earningsGrowth/revenueGrowth, jüngstes Quartal vs. Vorjahresquartal) — erste Wahl, braucht aber den Crumb, der zeitweise Yahoo-seitig blockiert ist.
  • Nasdaq-Jahres-Income-Statement (scorer.py::nasdaq_fundamental) als crumb-freie Zweitquelle für US-Werte — Total Revenue und Net Income über vier Geschäftsjahre in einem Abruf → Jahres-YoY, wobei Net-Income-Wachstum als Näherung fürs EPS-Wachstum dient (Aktienzahl meist stabil). Springt ein, wenn Yahoo nichts liefert; ein erfolgloser Yahoo-only-Cache blockiert den Nasdaq-Versuch bewusst nicht, und Fehlversuche gegen Nasdaq selbst werden ebenfalls gecacht.

Bewertung: EPS 0 % → 0, ≥25 % → 1; Umsatz 0 % → 0, ≥20 % → 1; Mittel der vorhandenen Teile.

Beschleunigungs-Bonus (+0,15 gedeckelt bei 1,0): aus den vier Nasdaq-Jahresspalten lässt sich neben der aktuellen YoY-Rate auch die Vorjahres-YoY-Rate bilden — wächst der Gewinn schneller als in der Vorperiode (bei positivem Wachstum), gibt's den Bonus, denn Minervini gewichtet beschleunigendes Wachstum höher als die reine Höhe der Rate. Nur über den Nasdaq-Zweig verfügbar (US), da Yahoos financialData-Modul nur die aktuelle Rate liefert, keine Historie.

Fallback ohne Zahlen aus beiden Quellen (z. B. EU-Wert bei blockiertem Crumb, unprofitable Aktie mit Vorjahresverlust, ADR/nicht gelistet): Fund über den Finviz-SEPA-Screener (harter EPS/Sales>20%-QoQ-Filter) oder „Code 33" im Trend-Screener-Kontext → 0,75; sonst leichter Malus 0,35 (bis 2026-07-24: neutral 0,5) — komplett fehlende Fundamentaldaten sollen nicht wie eine Bestätigung wirken, bleiben aber bewusst „gelb" statt „rot", da es oft nur an der Datenquelle liegt. Der Detail-Text kennzeichnet die Herkunft (Quartal-Yahoo vs. „Jahr, YoY · Nasdaq", plus „beschleunigt ggü. Vorjahr" wenn der Bonus griff).

Erste Messung 24.07.2026 (Crumb blockiert): 277 US-Werte über die Nasdaq-Zweitquelle mit echten Zahlen versorgt (z. B. AMD EPS +164 %/Umsatz +34 %, DELL +29 %/+19 %), Rest neutral bzw. jetzt leicht negativ ohne Zahlen.

⚡ Hebel-Ampel (seit 2026-08-16, signal-hub.html::hebelAmpel()): eine Reife-Ampel speziell für Hebelprodukte (Knock-Outs/Optionsscheine, gehandelt z. B. über Finanzen.net Zero) — kombiniert ausschließlich bereits vorhandene Signale zu einem einzigen Ampelwert, keine neue Berechnung. Ein gehebelter Trade verlangt eine engere Stop-Distanz und weniger Fehlertoleranz als ein Cash-Trade, daher strenger als der bestehende „🏆 Top-Setups"-Filter: 🟢 bereit nur, wenn alle Kriterien gleichzeitig erfüllt sind — Markt-Ampel grün, Stage-2-Trend-Template grün, Basis/VCP eng (= geringe Volatilität vor dem Einstieg, nicht während der Haltedauer — das wäre ein Widerspruch zum Zweck des Hebels), ein Pivot-Trigger (ARMED/BREAKOUT, Livermores Pivotpunkt), kein Extended (<25 % über SMA50), keine akute Earnings-Sperre und kein 50/80-/Klimax-Risiko. Fehlen nur Markt-Ampel oder Trend-Template, gilt der Wert als 🔴 ungeeignet (kein Badge, wäre auf fast jeder Karte sichtbar); sind Markt/Trend intakt, aber mindestens ein weiteres Kriterium offen, 🟡 im Fokus (Tooltip nennt die offenen Punkte). Badge steht als erster Hinweis noch vor „📚 N Ausgaben" auf der Kachel; neuer Filter „⚡ nur Hebel-Kandidaten (grün)" in der Gruppe „Qualitäts-Filter" (Standard aus) — wie „🏆 Top-Setups" bewusst von der Faktor-Pflicht („Volumen muss grün sein") ausgenommen, da ARMED-Kandidaten typischerweise Volumen-austrocknung statt -schub zeigen. Identische Kriterien serverseitig in Price-Action-Hub/src/scorer.py::hebel_ampel() — siehe dortigen Wiki-Abschnitt für die Begründung, warum das dort in Python statt clientseitig berechnet wird.

Datenquellen & Cloud-Pipeline

🔗 Datenfusion Markets 360 / Trend-Screener → Signal-Hub: Markets 360 und der Trend-Screener laufen als eigene GitHub-Actions-Cloud-Workflows und liefern ihre Ergebnisse über denselben R2-Bucket (signalhub-magazine) an Signal-Hub. Der Scorer behandelt beide als eigene, unabhängige Quellen (Feld quellen.unabhaengig) — bestätigt ein Ticker von mindestens zwei unabhängigen Engines, zeigt das Dashboard ein Badge „🔗 N× bestätigt" plus eigenen Filter „🔗 Multi-Quellen bestätigt". RS-Schwellen und VCP-Erkennung bleiben bewusst je Engine eigenständig — eine echte Code-Zusammenführung wäre ein groß angelegter, risikoreicher Umbau und ist zurückgestellt.

DatumÄnderungGrund
07-17Datenfusion eingeführt (Markets 360 + Trend-Screener als unabhängige Quellen)beide laufen inzwischen als eigene Cloud-Workflows, teilen sich denselben R2-Bucket
07-18Lokal-Fallback-Fix (0 Signale bei lokalem „Aktualisieren")die Original-Dateien der beiden Engines wurden lokal nicht zuerst geprüft, nur der Cloud-R2-Pfad
08-20Qualitätsfilter für Markets 360 ergänzt (min_fab5/TT_Pass)ohne Filter zählte praktisch jeder Treffer als „bestätigt" (72 % Überschneidung gemessen) und verwässerte die Konfluenz

☁️ Cloud-Pipeline (GitHub Actions): Der komplette Lauf (PDF/Mail/Finviz/Markets 360/Trend-Screener/Scoring/Push, danach parallel Price-Action-Hub + Rotation-Dashboard) läuft zusätzlich ohne Mac mini als GitHub-Actions-Workflow (.github/workflows/pipeline.yml) im öffentlichen Repo Miksch52/signal-hub — öffentlich, weil private Repos kontoweit auf 2.000 Actions-Minuten/Monat gedeckelt sind (Free-Plan), öffentliche unbegrenzt; Miksch52/maick-trading-system selbst bleibt privat (die Git-Historie enthält alte Depot-/Kontodaten) und wird nur am Ende per Deploy-Trigger angestoßen. Vier Jobs: Signal-Hub, dann parallel Price-Action-Hub + Rotation-Dashboard (beide hängen nur von Signal-Hubs Ausgabe ab, nicht voneinander, Datenaustausch per actions/upload-artifact), zuletzt ein deploy-Job, der alle drei Ausgaben nach R2 lädt und den schlanken deploy.yml-Workflow in maick-trading-system anstößt. Secrets (IMAP-Passwort, Config, Depot-Namen) liegen ausschließlich als GitHub-Secrets im signal-hub-Repo, nie im Code. Depot-Abgleich läuft auch im Cloud-Lauf mit: Secret SIGNALHUB_DEPOT_NAMEN enthält nur die Namen offener Positionen (nie Beträge/Stückzahlen) — mts_data.json selbst darf laut Projektregel nie in die Cloud.

⏱️ Warum die Cron-Zeiten krumm sind (seit 2026-09-10): Die acht Cron-Zeilen in pipeline.yml stehen auf :37 bzw. :07 statt auf :30/:00. Grund ist ein Befund der Systemprüfung vom 10.09.2026: auf runde und halbe Stunden terminierte Läufe stehen in GitHubs globaler Warteschlange und starteten real 2–5 Stunden zu spät — der Morgen-Slot 07:30 begann an drei Messtagen in Folge erst gegen 11:56, lieferte also erst mittags Daten. Krumme Minuten treffen ein deutlich leereres Zeitfenster.

Wichtig für spätere Änderungen — zwei Dinge, die leicht kaputtgehen: (1) Die Push-Slots bleiben unverändert 07:30 / 15:00 / 17:00 / 21:30 — die stehen in der config.json (benachrichtigung.zeiten), nicht im Cron. Der Cron ist nur der Anstoß, run.py --scheduled entscheidet danach selbst, welcher Slot fällig ist. (2) Die Cron-Zeit muss deshalb immer NACH der Slot-Zeit liegen, nie davor: faellige_slots() feuert nur bei jetzt >= slot_dt. Ein Cron um 07:07 würde den 07:30-Slot also gar nicht auslösen, sondern erst der nächste Anstoß mittags. Darum konsequent +7 Minuten hinter den Slot gelegt.

⏰ Worker-Cron: pünktliche Auslösung (seit 2026-09-11): Die krummen Minuten haben die Verspätung in der ersten Nacht nicht verringert (21:37-Slot +2:12 statt vorher +2:19) – GitHubs geplante Läufe sind allgemein verzögert, nicht nur zur vollen Stunde. Ein per API ausgelöster workflow_dispatch startet dagegen in Sekunden. Deshalb löst der Cloudflare-Worker mts-cors die drei Cloud-Läufe jetzt selbst aus: ein Cron Trigger 7,37 * * * * (UTC), der Zeitplan steht in CRON_ZEITPLAN in cloudflare-worker/mts-cors-proxy.js, die Umrechnung auf Berliner Zeit samt Sommer-/Winterzeit passiert im Code. Markets 360 um 05:37, Signal-Hub um 07:37 · 15:07 · 17:07 · 21:37, Trend-Screener Mo–Fr um 23:07. Signal-Hub bekommt dabei den Eingabeparameter geplant=true und verhält sich wie ein Cron-Lauf (nur fällige Slots, mit Push); der Cloud-Button ohne Eingabe bleibt beim sofortigen Lauf ohne Push. Markets 360 und Trend-Screener laufen als „… (Worker-Cron)". Die GitHub-Crons bleiben als Fallback: kommt ein verspäteter GitHub-Lauf, überspringen sich Markets 360 und Trend-Screener per gate-Job, wenn der Worker in den letzten 20 Stunden schon ausgelöst hat; Signal-Hub erkennt den erledigten Slot über state.json und endet nach rund zwei Minuten ohne zweiten Push.

Voraussetzung: das Worker-Secret GH_DISPATCH_TOKEN, ein eigener fine-grained PAT nur mit „Actions: Read and write" auf signal-hub, minervini-markets-360 und lokaler-trend-screener (nicht mit gh_gist_token oder gh_actions_token zusammenlegen). Ohne ihn löst der Worker nichts aus, und die GitHub-Crons laufen wie bisher. Den Stand zeigt https://mts-cors.miksch267.workers.dev/cron-status – ob der Token gesetzt ist und welche Läufe als Nächstes anstehen, ohne etwas auszulösen.

🕛 Slot-Verbuchung über Mitternacht (Fix 2026-09-11): run.py --scheduled merkt sich in state.json, welche Push-Slots eines Tages erledigt sind. Bis zum 11.09. wurde dafür das Datum erst am Laufende genommen. Der verspätete 21:30-Lauf vom 10.09. startete um 23:49, war um 00:20 fertig und verbuchte 15:00, 17:00 und 21:30 dadurch unter dem 11.09. – dort galten sie dann als erledigt, und der erste Worker-Cron-Lauf um 15:07 endete nach zehn Sekunden ohne Pipeline und Push. Jetzt gilt ein Zeitpunkt für Prüfung und Verbuchung; der fehlverbuchte Zustand in R2 wurde korrigiert. Endet ein Lauf mangels fälligem Slot, schreibt er außerdem eine Logzeile mit den bereits erledigten Slots, statt still zu enden.

DatumÄnderungGrund
07-17Depot-Abgleich auch im Cloud-Lauf (Secret SIGNALHUB_DEPOT_NAMEN)mts_data.json selbst darf nie in die Cloud — nur die reine Namensliste offener Positionen. Das Secret wird nicht automatisch nachgeführt; nach jedem Kauf/Verkauf am Mac python3 agenten/depot_namen_secret.py ausführen (setzt die Namen aller offenen Positionen neu und seit 2026-09-30 zusätzlich PORTFOLIO_TICKERS_JSON in minervini-markets-360 — die Tickerliste je Depot für die Depotzeilen der Cloud-breadth.json, dieselbe Auswahl wie /depot-tickers; --dry-run zeigt nur die Listen; seit 2026-09-22 auch Punkt der Kauf-Checkliste im Trainingsplan)
07-28Repo-Split: Signal-Hub + Price-Action-Hub in eigene, öffentliche Repos ausgelagertprivate Actions-Minuten (2.000/Monat) waren durch 4×/Tag-Cron + Cloud-Trigger-Klicks aufgebraucht
08-09Vier Jobs statt einer Kette (Price-Action-Hub + Rotation-Dashboard laufen parallel)beide hängen nur von Signal-Hubs Ausgabe ab, nicht voneinander — kürzere Gesamtlaufzeit
08-10Mac-mini-Zeitplan com.maick.signalhub deaktiviertals Ursache doppelter/dreifacher Pushes identifiziert — pushte an denselben ntfy-Kanal wie die Cloud
08-16Bugfix: Config-Secret-Schreibweise auf env:-Variable umgestelltein Apostroph im Secret-Inhalt brach das Shell-Quoting und legte einen kompletten Cron-Slot lahm
08-20Automatischer PDF→R2-Sync per LaunchAgent + Postmortem (PDF/Mail zeigten 0 Signale)der manuelle Upload wurde regelmäßig vergessen; IMAP brach bei vielen Einzel-Fetches am Stück ab
08-21Fix: LaunchAgent-PATH um Homebrew-Verzeichnisse ergänztdie launchd-Umgebung kennt kein interaktives Shell-PATH — wrangler/node wurden nicht gefunden
09-12Lokaler Zeitplan com.maick.signalhub endgültig deaktiviert, ersetzt durch com.maick.cloudspiegel (Lese-Job) + cloud_lexikon_sync.shWorker-Cron liefert den pünktlichen Morgen-Push jetzt zuverlässig aus der Cloud (vier erfolgreiche Läufe belegt) — der lokale Nachrechner/Zweit-Push ist überflüssig. Der zweite, kleine Zusatzjob musste ergänzt werden, weil sonst mit dem Wegfall von com.maick.signalhub auch der bisher darüber miterledigte Minervini-Zitat-Upload (siehe minervini_lexikon.py) stillschweigend ausgefallen wäre. Details siehe Abschnitt 5.
🔑 Der stille Ausfall: Deploy-Token-Überwachung (seit 2026-08-23). Am 23.08.2026 lief der DEPLOY_TRIGGER_TOKEN zwischen 14:11 und 15:19 ab. Die Folge war heimtückisch: alle Datenjobs liefen weiter fehlerfrei durch, R2 bekam frische Daten — nur der allerletzte Schritt, der den Pages-Deploy im Hauptrepo anstößt, bekam 401. Die Live-Seite fror also ein, während im System nichts kaputt aussah. Aufgefallen ist es rein zufällig beim Nachsehen, nicht durch eine Meldung.

Zwei Wächter, beide im Deploy-Schritt von pipeline.yml:
  • Reaktiv: Antwortet GitHub mit 401/403/404, geht ein ntfy-Push mit Priorität urgent raus („🔑 Deploy-Trigger-Token abgelehnt") — inklusive dem Befehl, mit dem sich der Deploy in der Zwischenzeit von Hand anstoßen lässt. 404 zählt bewusst mit dazu: ein fine-grained PAT ohne gültigen Zugriff auf das private Hauptrepo bekommt von GitHub 404 statt 403 (dieselbe Sonderbehandlung wie in triggerCloudWorkflow() im Frontend).
  • Proaktiv: GitHub liefert bei PATs mit Ablaufdatum den Antwort-Header GitHub-Authentication-Token-Expiration. Bleiben ≤ 14 Tage, meldet sich der Wächter vorher. Fehlt der Header (OAuth-/App-Token oder PAT ohne Ablauf), wird die Prüfung still übersprungen — kein Fehler, nur keine Vorwarnung möglich.
Kein Push-Dauerfeuer: Beide Meldungen laufen über einen Anti-Spam-Zustand (_state/deploy_token_state.json in R2, gleiches Muster wie pivot_eval_state.json/top_setups_state.json) — höchstens ein Push pro Tag und Anlass. Ohne das hätte ein toter Token 4×/Tag gepusht und die 14-Tage-Vorwarnung rund 56 Pushes erzeugt.

Deckt alle drei Repos ab: price-action-hub und rotation-dashboard nutzen in ihren deploy-trigger.yml denselben Token-Wert — ein Ablauf trifft immer alle drei gleichzeitig, eine Überwachungsstelle genügt also. Beim Erneuern muss das Secret aber in allen drei Repos neu gesetzt werden.
🔇 Der leise Nullerfolg: Quellen-Wächter (seit 2026-08-23). Faellt eine der fünf Rohsignal-Quellen aus — abgelaufenes IMAP-Passwort, ein kaputter R2-Sync bei Markets 360/Trend-Screener, ein leer gescrapter Finviz-Screener —, wirft keines der fünf *_screener.py-Module einen Fehler. Jedes schreibt unconditional eine (dann leere) Liste in seine Rohdatei und beendet sich mit Code 0 (markets360_screener.py sagt es im eigenen Docstring offen: „Fehlt die Datei … wird sauber mit 0 Signalen beendet statt einen Fehler zu werfen"). Für run.py::lauf() sieht das wie ein ganz normaler, grüner Lauf aus — der Ausfall ist unsichtbar, bis jemand zufällig nachschaut. Genau dieser Ausfallmodus stand schon in der Systemprüfung: „führt zu leisem Datenverlust (0 Signale statt Fehler), nicht zu einem Absturz".

Bewusst mechanisch, keine Einzelfallentscheidung nötig: quellen_watchdog.py läuft direkt nach den fünf Screener-Skripten und schreibt für jede aktive Quelle (PDF/Mail/Finviz je nach config.json-Flag, Markets 360/Trend-Screener immer — sie haben in run.py::pipeline() gar keine aktiv-Prüfung) einen Tageseintrag ihrer Trefferzahl in _state/quellen_historie.json (ein Eintrag pro Kalendertag, mehrere Läufe am selben Tag überschreiben ihn). Meldet eine Quelle heute 0 Treffer, obwohl sie an mindestens 5 der letzten 14 Tage etwas gefunden hat, geht ein Push raus — unabhängig davon, warum sie gerade leer ist. Die Schwelle bezieht sich bewusst auf die eigene Historie jeder Quelle, nicht auf einen festen Erwartungswert: Ein PDF-Tag ohne neues Magazin ist normal und löst keinen Alarm aus, weil PDF auch vorher schon oft bei 0 lag — nur ein Bruch aus einer verlässlichen Serie zählt.

Kein Push-Dauerfeuer: gleiches Anti-Spam-Muster wie beim Token-Wächter (_state/quellen_watchdog_state.json in R2), höchstens ein Push pro Tag und Quelle — hält der Ausfall mehrere Tage an, kommt aber jeden Tag eine neue Erinnerung, bis die Quelle wieder Treffer liefert. Absichtlich nicht in den Pipeline-Exit-Code verrechnet (wie scorer_ok): ein Ausfall einer von fünf Quellen soll auffallen, aber nicht die ganze Pipeline als „fehlgeschlagen" markieren — der Push ist der eigentliche Meldeweg.

📈 OHLC-Tagessnapshot (seit 2026-08-20, Top-Setups-Roadmap Punkt 4): Bis dahin gab es im gesamten System keine tatsächlich dauerhafte Kurshistorie — alle fünf Screening-Engines cachen Yahoo-Kurse nur mit Tages-TTL, und auf dem Cloud-Runner wird dieser Cache nie nach R2 gesichert, sondern fängt bei jedem der vier täglichen Läufe leer an. src/ohlc_history.py läuft jetzt direkt nach scorer.py und zieht ohne zusätzlichen Yahoo-Abruf aus dem ohnehin schon in signals.json eingebetteten 126-Tage-Chartfenster nur den jeweils letzten Handelstag je Ticker heraus und schreibt eine kompakte, datumsgestempelte CSV (Signal-Hub/data/ohlc-history/JJJJ-MM-TT.csv, ~20 KB für alle ~860 Ticker). Beide Datenpfade sichern die Datei zusätzlich dauerhaft nach R2 — der Cloud-Lauf per rclone direkt in pipeline.yml, der lokale Mac-mini-Lauf per scripts/upload_ohlc_to_r2.sh. Anders als der _state/-Ordner wächst ohlc-history/ Tag für Tag — Grundlage für einen künftigen Punkt-in-Zeit-Backtest ohne den im RETRO-Modus dokumentierten Universums-Bias.

📅 Datierung nach echtem Handelstag (seit 2026-09-11, Befund der Systemprüfung). Bis dahin bekam jede Tagesdatei das Laufdatum und wurde von jedem der vier täglichen Läufe überschrieben. Ein Abgleich gegen Yahoos finale Schlusskurse zeigte drei Fehler: Läufe zwischen 18:30 und 22:00 Uhr schrieben für US-Titel Intraday-Kurse als Schlusskurs (nur 4–28 % Treffer), Läufe nach Mitternacht legten den Vortagesschluss unter dem Folgedatum ab, und es entstanden Dateien für Samstage und Sonntage. Jetzt legt scorer.py je Treffer das Datum des letzten Bars (chart.d) und die Abrufzeit (chart.abg) in signals.json ab. ohlc_history.py schreibt jede Zeile unter ihrem echten Handelstag, lässt einen Bar weg, der beim Abruf noch zum laufenden Handelstag gehörte (US bis 17:00 New York, Europa bis 18:00, deutsche Regionalbörsen bis 22:30 Berlin – Schluss plus Puffer für Nachmeldungen; maßgeblich ist die Abrufzeit, weil der Tages-Cache einen Intraday-Bar in einen späteren Lauf tragen kann) und hängt nur an: eine vorhandene Zeile für (Tag, Titel) bleibt unverändert. Bars, die beim Abruf älter als sieben Tage sind (ausgesetzte oder delistete Titel, Yahoo-Lücken), werden nicht geschrieben, und der Upload legt neue Tage nur an – ersetzt werden ausschließlich Tagesdateien, die die Pipeline vorher aus R2 geholt hat. Beides seit dem 11.09. mittags: ein Nachtrag für einen Titel mit Stand 17.07. hatte die vollständige 2026-07-17.csv durch eine Datei mit einer Zeile ersetzt, weil dieser alte Tag nicht aus R2 zurückgeholt worden war (aus dem Archiv wiederhergestellt). Die Cloud-Pipeline holt dafür vorher die jüngsten Tage aus R2 zurück und ersetzt bestehende Dateien nur, wenn dieser Abruf geklappt hat; der lokale Upload (upload_ohlc_to_r2.sh) lädt nur noch Tage hoch, die es in R2 nicht gibt. Eine Tagesdatei enthält damit ausschließlich Schlusskurse genau dieses Tags – an Feiertagen kann das nur ein Markt sein (Labor Day 07.09.: nur Europa).

Altbestand korrigiert (11.09.): Die Dateien ab 20.08. wurden zeilenweise gegen finale Schlusskurse neu datiert; übernommen wurden nur als final belegte Kurse (bei EU-Titeln mit Yahoo-Lücke am erwarteten Handelstag dieser Tag). Wochenend-Dateien und Intraday-Werte sind entfernt, der Bestand vor dem 20.08. blieb unverändert. Für 26.08., 31.08., 01.09., 02.09. und 08.09. gab es keine einzige US-Aufnahme nach Börsenschluss – diese Tage bleiben für US-Titel lückenhaft. Originale unter r2:signalhub-magazine/_archiv/history-vor-korrektur-2026-09-11/ohlc-history/, Zuordnung je Quelldatei in ohlc-history/_korrektur_2026-09-11.json.

Tageshoch/-tief seit 2026-09-12 (Systemprüfung, Optimierungspunkt 3): Eine Tagesdatei enthielt bis dahin nur ticker,close,volume — ohne Hoch/Tief lässt sich im Backtest nicht rekonstruieren, ob ein Stop unterwegs gerissen worden wäre (Grundlage für einen künftigen Strategie- statt reinen Signal-Backtest). Neue Zeilen bekommen die Spalten ticker,open,high,low,close,volume, aus denselben ohnehin in signals.json eingebetteten Kerzendaten wie der Schlusskurs (scorer.py::_chartdaten, kein zusätzlicher Yahoo-Abruf). Bestandsdateien bleiben unangetastet und dreispaltig — ohlc_history.py übernimmt beim Anhängen die tatsächliche Kopfzeile einer Tagesdatei, damit keine Zeile mit abweichender Spaltenzahl hineingerät. scripts/backfill_ohlc_history.py zieht seither ebenfalls Open/High/Low aus Yahoos ohnehin mitgelieferter Antwort.

Rückwirkendes Backfill (seit 2026-08-20): scripts/backfill_ohlc_history.py (einmaliger, manueller Lauf) hat die Lücke vor dem ersten echten Snapshot geschlossen: für die 891 eindeutigen Ticker aus dem Pivot-Logbuch (wächst schon seit 2026-07-02) per range=2y&interval=1d direkt von Yahoo 35 fehlende Handelstage (02.07.–19.08.) rekonstruiert und ergänzt — rein additiv, überschreibt nie einen bereits vorhandenen Tag. Deckt bewusst nur die im Logbuch tatsächlich aufgetauchten Ticker ab, nicht das komplette Tagesuniversum rückwirkend — für die Forward-Auswertung historischer Pivot-Signale ist das genau der richtige, vollständige Datensatz.

Rückmeldung & Auswertung

Push: viermal täglich (07:30 / 15:00 / 17:00 / 21:30) via ntfy an alle Geräte — nur neue Kandidaten ≥ 70. Läuft seit 2026-07 vollautomatisch in der Cloud (GitHub Actions, siehe oben), unabhängig davon ob/welcher Mac läuft. Der lokale Mac-mini-Zeitplan (com.maick.signalhub-LaunchAgent) ist damit obsolet — er pushte entgegen der ursprünglichen Doku nicht an einen separaten Test-Kanal, sondern an denselben ntfy-Kanal wie die Cloud (bestätigter Doppel-Push-Fall 2026-08-10, siehe Wartungstabelle unten). Ablösung vollzogen (2026-09-12): Nach vier erfolgreichen Worker-Cron-Läufen hat Signal-Hub/scripts/auf_cloud_umstellen.command den Agent endgültig gestoppt (launchctl bootout + disable, Plist bleibt nur deaktiviert liegen) und stattdessen den reinen Lese-Job com.maick.cloudspiegel eingerichtet (alle 30 Minuten, osascript-Wrapper wie die Server-Jobs). Der holt die in der Cloud berechneten Ausgaben aus R2 (_deploy/) auf den Mac, damit die LAN-Versionen von Startseite, Signal-Hub, Price-Action-Hub und Rotation-Dashboard aktuell bleiben; er rechnet und pusht nichts und schreibt nichts nach R2. Details/Rückbau siehe Abschnitt 5. ☀️ Morning Brief (seit 2026-08-02, notify.py::baue_nachricht(morgens=True)): der erste fällige Slot des Tages (zeiten[0], i.d.R. 07:30, wird von run.py --scheduled automatisch erkannt) bekommt vor der Kandidatenliste zusätzlich eine Ampel-Zeile je aktivem Markt (🟢/🟡/🔴 + Klartext-Hinweis aus signals.json → marktregime) vorangestellt und bleibt — anders als die drei übrigen Slots — auch ganz ohne Kauf-Kandidaten nicht stumm, sondern zeigt wenigstens die Marktlage. Die drei späteren Slots pushen unverändert nur die reine Kandidatenliste.

Zwei Zusatzabschnitte im Morning Brief (seit 2026-08-28): „🎯 Stärkste mit Pivot heute" listet die Top-3 der bereits um Markt-Regime/Sentiment bereinigten Kandidaten mit ARMED/BREAKOUT-Pivot inkl. Pivot/Stop — bewusst eine leichtere Version der Startseiten-„Top-Setups": pivot.json liegt zum Zeitpunkt dieses Pushs schon vor (läuft im selben Signal-Hub-Job direkt vor notify.py), top_setups.json dagegen noch nicht (wird erst im nachgelagerten Price-Action-Hub-Job desselben Pipeline-Laufs berechnet) — ohne Price-Action-Score/Katalysator-Kreuzung, dafür ohne die Wartezeit auf den zweiten Job. „📅 Earnings in offenen Positionen" listet Depot-Ticker (reiner Namensabgleich, keine Beträge) mit Earnings-Termin innerhalb des Warnfensters — erscheint auch dann, wenn es keine neuen Kauf-Kandidaten gibt. 🔗 Click-Header: jeder Push (nicht nur der Morning Brief) öffnet beim Antippen jetzt direkt signal-hub.html (ntfy-„Click"-Header) statt dass man das Dashboard erst manuell aufrufen muss.

📬 Push-Quellen im Überblick

Alle Push-Systeme im Trading-System schicken an denselben ntfy-Kanal (benachrichtigung.ntfy_thema aus Signal-Hub/config.json) — im Handy-Feed erscheinen sie deshalb gemischt und wirken wie „unterschiedliche Listen". Es sind aber neun unabhängige Quellen mit eigener Logik, kein Duplikat/Fehler (die Zahl stand bis 2026-09-20 noch auf „fünf", obwohl die Tabelle längst mehr Zeilen hatte):

QuelleWannInhalt
notify.py (Signal-Hub-Digest)4×/Tag: 07:30 · 15:00 · 17:00 · 21:30Neue Score-Kauf-Kandidaten ≥ 70 (nur Neuzugänge seit letztem Push, Earnings-nahe und rot-Ampel-Markt rausgefiltert). Der 07:30-Slot ist zusätzlich das ☀️ Morning Brief mit Marktampel-Status je Markt vorweg.
pivot_screener.py --notify (Pivot-Screener)läuft im selben 4×/Tag-Zyklus mitNeue ARMED/BREAKOUT-Ticker aus der Pivot-Musteranalyse — eigene Erkennungslogik, nicht der Unified Score. Titel „Pivot: X Ausbruch / Y scharf".
top_setups.py (🏆 Top-Setups, seit 2026-08-09, siehe Startseite)bei jedem Pipeline-Lauf, auch bei manuellem „☁️ Cloud-Scan starten" — bewusst nicht an die 4 Slots gekoppelt, da diese Schnittmenge selten und hochwertig genug istNeue Ticker in der dreifach bestätigten Schnittmenge Score ≥ 70 ∧ ARMED/BREAKOUT ∧ Price-Action bullisch. Eigenständiger Versand (kein Import von notify.py — Entflechtung), Titel „🏆 X neue Top-Setups", je Zeile Markt-Ampel-Icon + 🎯/🚀-Status.
mts_alarms.py (Trading-Alarme, siehe Hauptapp)alle 20 Min, nur 08–22 Uhr, kein Wochenende, läuft nur lokal am laufenden Mac miniBetrifft ausschließlich deine offenen Positionen aus mts_data.json: SL-Bruch/-Nähe, Teilverkaufszonen +8/20/40 %, Kursziel erreicht — kein Markt-Scan.
pipeline.yml Deploy-Schritt (🔑 Token-Wächter, seit 2026-08-23)jeder Pipeline-Lauf, aber höchstens ein Push pro Tag und AnlassMeldet, wenn der DEPLOY_TRIGGER_TOKEN abgelehnt wird (HTTP 401/403/404 → Priorität urgent) oder in ≤ 14 Tagen abläuft. Siehe Kasten „Der stille Ausfall" unten.
pivot_backtest.py --evaluate (Backtest-Push)jeder Pipeline-Lauf (seit 2026-08-23, vorher nur So 08:15 per com.maick.pivot-backtest.plist)Keine Ticker-Liste, sondern eine Trefferquoten-Meldung „📊 Pivot-Forward-Test reif", sobald genug Forward-Test-Daten für eine Kohorte gereift sind. Trotz 4×/Tag kein Spam: push_reife() hat eigenen Anti-Spam über pivot_eval_state.json (nur beim ersten Erreichen der Schwelle bzw. wenn n um ~50 % wächst), und dieser Zustand wird in der Cloud aus R2 wiederhergestellt und zurückgesichert.
quellen_watchdog.py (🔇 Quellen-Wächter, seit 2026-08-23)jeder Pipeline-Lauf, aber höchstens ein Push pro Tag und QuelleMeldet, wenn eine der fünf Rohsignal-Quellen (PDF/Mail/Finviz/Markets 360/Trend-Screener) heute 0 Treffer liefert, obwohl sie an mindestens 5 der letzten 14 Tage etwas gefunden hat — Verdacht auf stillen Ausfall statt echtem Nullergebnis. Siehe Kasten „Der leise Nullerfolg" unten.
wochen_dossier.py (📋 Wochen-Dossier, seit 2026-09-20)einmal pro Kalenderwoche — nach dem ersten erfolgreichen Pipeline-Lauf am Samstag, danach überspringt sich der Workflow selbstKeine Einzelticker-Meldung, sondern die Wochenübersicht: Ampel je Markt, Anzahl Kandidaten, neu/raus gegenüber der Vorwoche, Warnung bei veralteten Datenquellen. Antippen öffnet wochen-dossier.html (ntfy-„Click"-Header). Siehe Wochen-Dossier.
forward_test_review.py (📐 Forward-Test-Wächter, seit 2026-08-23)nur wenn der Schnappschuss ≥ 28 Tage alt ist — an allen anderen Tagen kein Push, egal wie oft die Pipeline läuftVergleicht alle sechs Backtest-Kohorten gegen den Stand vor ~1 Monat: neu belastbar (n ≥ 100), Konfidenz-Untergrenze hat 50 % gekreuzt, ARMED/BREAKOUT-Rangfolge gedreht, oder eine Engine reift weiterhin nicht. Siehe Backtest-Vergleich.

📖 Visuelle Referenzseite (seit 2026-08-20): Dieselben fünf Quellen stehen zusätzlich als eigene Karte „🔔 Push-Kriterien" im Karten-Grid der Startseite — anders als die App-Anleitungen (Header-Button einer einzelnen App) ist das eine Cross-App-Referenzseite, die kein Header einer einzelnen App verlinkt, sondern die Startseite selbst. Zeigt dieselben Kriterien wie die Tabelle oben visuell aufbereitet (Karte pro Quelle, Beispielnachricht, Stellschrauben-Tabelle mit den aktuellen config.json-Werten) — Push-Kriterien-Übersicht. Seit 2026-09-22 mit der Tabelle „Pushes in der Trainingsphase": je Quelle, ob sie im Training eine Reaktion verlangt (Wochen-Dossier als Startsignal, Pivot-Screener nur bei einem der drei Wochen-Kandidaten, Trading-Alarme als Sicherheitsnetz neben der Broker-Stop-Order) — inklusive Hinweis, dass die Earnings-Warnung im Morgen-Brief nur Positionen aus dem Secret SIGNALHUB_DEPOT_NAMEN kennt. Eigenes, viertes Design (IBM Plex Sans/Mono, Türkis-Akzent) — siehe CLAUDE.md für die vollständige Design-System-Übersicht.

Anti-Spam & Zustand des Top-Setups-Push: top_setups.py::_neuzugaenge() vergleicht die aktuelle Schnittmenge gegen die beim letzten Lauf gepushten Ticker (top_setups_state.json) — nur echte Neuzugänge lösen aus, ein Dropout mit späterem Wiedereinstieg zählt erneut als neu (gleiches Prinzip wie beim Pivot-Screener). Da Price-Action-Hub/src/top_setups.py in einem eigenen, oeffentlichen Repo/Cloud-Job läuft (kein Cross-App-Python-Import zu Signal-Hub/src/notify.py — Entflechtung), braucht der price-action-hub-Job in pipeline.yml dafür einen eigenen kleinen R2-Umweg: top_setups_state.json wird vor dem Lauf aus r2:signalhub-magazine/_state/ wiederhergestellt und danach zurückgesichert (sonst wäre der Zustand bei jedem frischen Runner leer), und Signal-Hub/config.json (nur für den ntfy-Kanal gebraucht) wird aus dem SIGNALHUB_CONFIG_JSON-Secret direkt in diesem Job geschrieben — bleibt im ephemeren Runner, landet nie in einem Artifact-Upload (das Repo ist öffentlich).

🔍 Setup-Analyse (seit 2026-08-07): Jede Kachel verlinkt jetzt zusätzlich direkt auf setup-detail.html?ticker=… — denselben Detail-Überblick (Score-Faktoren, Pivot-Kennzahlen, Price-Action-Muster, TradingView-Chart, Fundamentaldaten & Story), den bisher nur das 🔍-Icon der Top-Setups auf der Startseite öffnete. Der bestehende Link zur SEPA-Analyse in der Hauptapp heißt zur Unterscheidung jetzt 🔎 SEPA-Analyse (anderes Lupen-Symbol) — 🔍 steht im ganzen System einheitlich für „Setup-Analyse öffnen".

📈 Trade-Funnel: Jede Kachel hat (im HTTP-Modus) einen „📈 Trade"-Link, der in der Hauptapp das Trade-Formular vorbefüllt öffnet (Ticker + Quelle = „Signal-Hub"). Seit 2026-07-24 gibt der Link zusätzlich Entry = Pivot und Stop = Basis-Tief aus dem Pivot-Detektor mit (&entry=…&stop=…, Helfer tradeParams()/tradeTitle()), sofern für den Ticker ARMED/BREAKOUT-Pivot-Daten vorliegen — die Hauptapp füllt beide Felder und rechnet die Positionsgröße sofort (updateTradePreview()); ohne Pivot-Daten bleibt es beim reinen Ticker+Quelle-Vorbefüllen. Bei einem Earnings-gesperrten Wert (siehe unten) trägt der Link-Tooltip zusätzlich die Warnung. Die Hauptapp speichert die Quelle am Trade (Feld im Formular, frei wählbar/Vorschlagsliste). Im Psychology-Tab wertet „Performance nach Quelle" dann aus, welche Ideen-Quelle tatsächlich Gewinner bringt (Trades, Win-Rate, Ø-CRV, P&L je Quelle) — so lassen sich schwache Quellen datenbasiert aussortieren.

🔒 Earnings-Sperre für Kauf-Kandidaten (seit 2026-07-24, signal-hub.html::earnGesperrt()): Bisher wurden Werte mit baldigen Quartalszahlen nur aus dem Push genommen. Jetzt wird ein Kauf-Kandidat (Tier A) mit Earnings im Warnfenster (Standard 10 Tage) auch im Dashboard sichtbar gesperrt: die Einstufung zeigt „🔒 Earnings-Sperre" statt „Kauf-Kandidat" (rote Pille, rot getönter Kachelrahmen), und der „📈 Trade"-Link warnt im Tooltip. Minervini kauft nicht kurz vor Zahlen. Der bestehende Filter „⚠︎ Earnings ausblenden" (Standard an) nimmt solche Werte weiterhin ganz aus der Liste — die Sperre ist die sichtbare Behandlung, wenn man den Filter abschaltet.

🏆 „nur Top-Setups"-Filter + 📐 PA-Badge (seit 2026-07-24): Dieselbe dreifach bestätigte Schnittmenge wie das Startseiten-Panel — Score ≥ 70 ∧ ARMED/BREAKOUT ∧ Price-Action bullisch (PA-Score > 0) — jetzt auch als Filter im Signal-Hub, plus ein Badge 📐 PA +N auf jeder Kachel der Schnittmenge (verlinkt zum Price-Action-Hub). Damit bekommt der Signal-Hub die bisher fehlende Price-Action-Dimension direkt in die Kartenansicht. Datenquelle ist die winzige top_setups.js (~5 KB, vom Aggregator gebaut, siehe Startseite) — nicht die 5,7 MB priceaction.js, deshalb erscheint das PA-Badge nur auf den Tickern der Schnittmenge (mehr pa_score-Daten liegen dem Hub bewusst nicht vor — Entflechtung). Bewusste Besonderheit: „nur Top-Setups" umgeht die Faktor-Pflicht („müssen grün sein"), denn das Set ist bereits kuratiert und der Default „Volumen grün" widerspräche ARMED direkt (die brauchen Volumen-Austrocknung, keinen -Schub — sonst wäre die Ansicht fast immer leer); die Risiko-Filter (Earnings/Extended/50-80) bleiben dagegen aktiv. Der Zähler zeigt die Gesamtzahl der Setups, die Trefferzahl das, was nach den Risiko-Filtern übrig bleibt.

🧭 Regel-Check (Psychology-Tab): wertet automatisch deine Disziplin aus — Positionsgröße ≤ 2 % Depotrisiko, Stop-Loss eingehalten (Verlust nicht größer als geplant), Verlierer schneller geschnitten als Gewinner gehalten, Ø Gewinn ≥ 2× Ø Verlust. Jede Regel mit ✅/⚠️ und konkreten Zahlen. Verhaltensmuster sind bei Minervini der eigentliche Edge.

⭐ Fokusliste-Verknüpfung mit der SEPA-Analyse (seit 2026-07-22, Ziel seit 2026-07-23 die Hauptapp statt des Coaches — siehe Hauptapp-Abschnitt): bewusst kein eigenes Watchlist-Feature im Signal-Hub (Entflechtung) — die SEPA-Analyse in „Maick's Trading System" hat eine server-/gist-synchronisierte Watchlist (localStorage['mts_v2'].watchlist). Da beide Apps unter derselben Origin laufen (8090-Proxy/Tailscale/Cloudflare Pages), liest der Signal-Hub diese Watchlist nur lesend mit: Badge „⭐ Fokusliste" auf bereits gemerkten Tickern, Filter „⭐ nur Fokusliste". Der „🔎 SEPA-Analyse"-Link jeder Kachel öffnet mit ?ticker=… direkt die Analyse in der Hauptapp (vorher musste der Ticker manuell eingetippt werden) — „Idee prüfen → bei Bestätigung zur Fokusliste hinzufügen" ist damit ein echter Zwei-Klick-Flow. Zweiter Schreibweg seit 2026-09-23: „👀 Beobachten“ im Kauf-Review setzt Top-Setups direkt auf dieselbe Liste (ohne SEPA-Score, dafür mit Pivot/Stop) — gedacht für die Wochen-Kandidaten des Trainingsplans. Live-Nachziehen per storage-Event: Klickt man im offenen Hauptapp-Tab „⭐ Speichern", feuert das Browser-storage-Event automatisch in jedem anderen gleichzeitig offenen Tab derselben Origin (nur dort, nie im schreibenden Tab selbst) — der Signal-Hub-Tab rendert daraufhin sofort neu, Badge/Filter zeigen den frischen Stand ohne manuelles Neuladen. Gegen echte Zwei-Tab-Situation getestet.

📊 Backtest-Report (Signal-Hub/backtest-report.html, Link im Header): eigene Seite im Wochennotiz-Stil, die auswertet, welche Pivot-Setups (ARMED/BREAKOUT/WATCH) unverzerrt tatsächlich funktioniert haben — Fließtext-Einordnung je Setup, dahinter eine vollständige, live geladene Fundstellen-Tabelle mit jedem Einzelfall (Ticker, Signal-Datum, Einstiegskurs, realisierte Rendite) plus Chart-Link zu Yahoo Finance. Läuft wöchentlich mit (So 08:15), auch offline (file://) und auf der Cloudflare-Pages-Version verfügbar.

🎚️ SEPA-Kalibrierung (seit 2026-08-30, Link im Header, Claude-Artifact): Momentaufnahme der sieben Kriterien aus dem SEPA/VCP-Abgleich (10-Monats-EMA-Marktfilter, Trend-Template-K.-o., VCP-Feinmessung, institutioneller Besitz), solange deren Forward-Kohorten noch bei n=0 stehen. Bewusst nicht in backtest-report.html/backtest-vergleich.html integriert — beide blenden Kohorten mit n=0 im Code aktiv aus (if(!s || !s.n) continue;), eine Aufnahme heute würde also nirgends erscheinen. Countdown bis zur automatisch geplanten Gate-Auswertung am 19.10.2026, Verteilungsbalken je Kriterium, Kohortentabelle mit ehrlichem „sammelt seit 30.08."-Status. Kein Live-Ticker (die Artifact-Sandbox blockiert externe Netzwerkzugriffe) — auf Zuruf neu aus dem aktuellen Datenstand gebaut und auf dieselbe Adresse republished. Sobald die ersten Kohorten reif sind (ab 20.09.), wandert die Auswertung in die beiden Backtest-Werkzeuge; diese Seite verliert dann ihren Zweck.

DatumÄnderungGrund
08-07Seite eingeführt, inkl. ticker-scharfer Einzelfälle im Datenexportdie Einzelfälle lagen vorher nur im nie synchronisierten lokalen Forward-Logbuch, dem Dashboard nicht zugänglich
08-17Fünf Überarbeitungen: Dark Mode, Kennzahlen-Leiste (Win-Raten aller drei Setups auf einen Blick), Tabellen-Paginierung (50/Seite + Sortierung), deutlichere Tab-Optik, Win-Rate-Balken mit Ø-Return-Labelgroße Kohorten (z. B. n=1.710) waren unpaginiert nur noch per Strg+F nutzbar; die Seite war zudem die einzige im System ohne Dark Mode

🛒 Kauf-Review-Queue

Kauf-Review-Queue (Signal-Hub/kauf-review.html, seit 2026-08-20 — verlinkt von der Startseite und vom Signal-Hub-Header): sequenzielles, mobil-optimiertes Durchgehen aller aktiven Top-Setups (grünes Regime, kein Earnings-Fenster) statt der bisherigen Einzelticker-Ansicht von setup-detail.html — für den Anwendungsfall „unterwegs auf dem iPhone die Tagesliste durchgehen und entscheiden". Lädt ausschließlich die ohnehin schon winzige data/top_setups.js plus Rotation-Dashboard/data/rotation.js für den Leader-Abgleich, exakt wie das Top-Setups-Panel selbst.

Pro Karte: Kurs, Pivot/Stop/Abstand, dieselben Badges wie im Panel (⭐ Kern-Setup, 📌 im Depot, 🎯 Konfluenz X/2, 🔥 Leader), eine Trend-Template-Checkliste (5 Kernfaktoren mit Ampel) sowie ein grober Positionsgrößen-Rechner (Kapital × Risiko-% ÷ Stop-Abstand, best-effort per /data vorausgefüllt, wenn ein lokaler Server erreichbar ist). Das Kapital lässt sich per Schnellwahl (3.000 / 5.000 / 10.000 / 15.000 €) setzen oder frei eintippen; beide Felder merken sich ihren Wert pro Gerät. Beim Tippen wird nur der Stückzahl-Kasten neu berechnet, nicht die ganze Karte — bis 23.09.2026 verlor das Feld dadurch nach jedem Zeichen den Cursor. Bewusst keine Neuimplementierung der vollen Positionsgrößen-/Exposure-Logik der Hauptapp — die Karte zeigt nur eine grobe Schätzung, „🛒 Kaufen" verlinkt direkt in den Trade-Planner der Hauptapp, wo die verbindliche Rechnung inkl. aller Limits läuft. Drei Aktionen pro Karte (👀 Beobachten / ✕ Verwerfen / 🛒 Kaufen), Der Fortschritt durch die Karten bleibt in sessionStorage erhalten (nur dieser Tab, dieser Datenstand).

👀 Beobachten = Fokusliste der Hauptapp (seit 2026-09-23). „Beobachten“ setzt den Ticker samt Pivot, Stop und Datum auf die SEPA-Watchlist der Hauptapp (mts_v2.watchlist, im Signal-Hub als „⭐ Fokusliste“ markiert) — keine eigene zweite Liste. Oben auf der Seite zeigt der aufklappbare Bereich „👀 Beobachtet (N)“ diese Liste, auch an Tagen ohne aktives Top-Setup: je Wert aktueller Kurs, Pivot, Stop, Abstand zum Pivot, ARMED/BREAKOUT, Earnings- oder Marktwarnung (sofern der Wert heute in den Top-Setups steht, sonst „heute kein Top-Setup“) sowie 🔍 Analyse, 🛒 Trade-Planner und ✕ Entfernen. Karten bereits beobachteter Werte tragen das Badge „👀 beobachtet“. Geräteübergreifend über denselben privaten Gist wie die Hauptapp (Token gh_gist_token): das Kauf-Review schreibt im Gist ausschließlich watchlist und _meta.geloeschteWatchlist, Trades und Depots gehen unverändert zurück; ist die Gist-Datei unvollständig oder nicht auffindbar, wird nichts geschrieben. Ohne Token bleibt der Eintrag auf dem Gerät, die Seite sagt das dazu. Anzeigen und pflegen lässt sich die Liste auch in der Hauptapp: Risk-Management → SEPA-Analyse → Karte „📋 SEPA-Watchlist · Fokusliste“ (👀 = aus dem Kauf-Review). Abschluss-Screen zeigt die Zusammenfassung mit Neustart-Option. Gesperrte Setups werden nicht in die Queue aufgenommen, aber gezählt und referenziert.

▸ Verlauf & Begründung (1 Eintrag)

Deploy-Bugfix (2026-08-20): Die neue Seite landete beim ersten Cloud-Deploy nicht auf Cloudflare Pages — sowohl cloudflare-pages/deploy.command als auch .github/workflows/deploy.yml kopieren Signal-Hub-Apps über eine feste Datei-Liste statt den ganzen Ordner — beide Listen mussten um kauf-review.html ergänzt werden, sonst Redirect zur Startseite statt Anzeige der Seite. Bei künftigen neuen Signal-Hub-Einzeldateien immer beide Stellen zusammen bedenken.

📋 Wochen-Dossier

Wochen-Dossier (Signal-Hub/wochen-dossier.html, seit 2026-09-20 — verlinkt als eigene Karte unter „Screening & Marktumfeld" auf der Startseite): fasst am Wochenende die Ausgaben aller drei Engines zu höchstens acht Kandidaten für die neue Woche zusammen — der Ersatz für das bisherige manuelle Durchgehen von Signal-Hub, Pivot, Price-Action-Hub, Rotation-Dashboard und Katalysator-Layer am Samstagvormittag. Kopf der Seite: Marktampel je Markt (bei Rot ein deutlicher „keine neuen Käufe"-Hinweis), Distribution-Day-Warnung, stärkste Rotations-Themen, Pivot-Zähler des Universums. Danach die Veränderung zur Vorwoche (neu / geblieben / nicht mehr dabei), dann je Kandidat Pivot, Stop, Abstand, Chance/Risiko, Trend-Template, Code 33, Price-Action-Muster, Leader-Status, Katalysator-Zitat mit Quelle und die Trefferquote der zugehörigen Backtest-Kohorte samt n. Jede Karte verlinkt auf die volle Setup-Analyse.

Keine neue Screening-Logik: Die Auswahl sind die bestehenden Top-Setups, sortiert nach Kern-Setup → Konfluenz → Abstand zum Pivot. Die Warnsymbole (Stop > 8 % bzw. > 10 %, mehr als 5 % über Pivot, Earnings im Warnfenster, rote Ampel) benutzen dieselben Grenzen wie der Pre-Trade-Prüfer und markieren nur, sie sortieren nichts aus — ein Wert mit hoher Konfluenz und zu weitem Stop steht also weiterhin oben, aber rot markiert. Deshalb braucht das Dossier auch keinen eigenen Forward-Test: es entsteht kein neues Signal, nur eine Zusammenstellung vorhandener.

Technisch: Signal-Hub/src/wochen_dossier.py erzeugt data/wochen_dossier.json; die Seite lädt nur diese eine Datei (.js-Fallback für file:// wie die übrigen Dashboards). Ausgelöst wird der eigene Workflow wochen-dossier.yml im Repo Miksch52/signal-hub per workflow_run nach jedem erfolgreichen Pipeline-Lauf — er läuft nur Sa/So und höchstens einmal je Kalenderwoche. Grund für das Wochenende: erst dann sind die Freitags-Schlusskurse vollständig (der 21:30-Slot am Freitag sieht die US-Börse noch offen, Trend-Screener läuft Fr 23:07, Markets 360 Sa 05:37). Die Kalenderwoche kommt aus dem Datenstand, nicht aus dem Schreibdatum — gleiche Regel wie beim OHLC-Tagessnapshot. Drei Ablagen in R2: _deploy/signal-hub/ (Live-Stand), wochen-dossier/history/JJJJ-KWnn.json (unveränderliche Wochenkopie, --ignore-existing) und _state/wochen_dossier_letzte.json (Vergleichsbasis der Folgewoche). Enthält bewusst keine Depotdaten — die Seite liegt öffentlich auf Pages.

Fehlt die Seite noch oder ist sie leer, hat schlicht noch kein Wochenendlauf stattgefunden — sie zeigt dann einen Hinweis statt eines Fehlers. Ein Testlauf lässt sich jederzeit von Hand anstoßen: gh workflow run wochen-dossier.yml --repo Miksch52/signal-hub -f erzwingen=true.

Zugriff

Zugriff (iPhone/PWA): Der Signal Hub wird über denselben 8090-Origin geöffnet (relativer Link) — der 8090-Server reicht /Signal-Hub/api/* an den 8091-Server durch. So funktioniert er in der installierten PWA und über Tailscale-HTTPS, ohne dass das Gerät den separaten Port 8091 erreichen muss (früher zeigte der Link direkt auf http://…:8091 → auf dem iPhone „Seite nicht erreichbar").

🔎 SEPA-Analyse/📈 Trade-Link — Basis-URL-Fix (2026-08-03): Beide Deeplinks in die Hauptapp bauten die Ziel-URL bislang immer als location.hostname + ':8090/…' — korrekt nur bei reinem LAN-http://-Zugriff auf den Mac mini. Bei https-Zugriff (Cloudflare Pages und Tailscale, siehe Abschnitt „Primärzugang") liegen beide Apps aber bereits unter demselben Origin auf Port 443 — das angehängte :8090 zeigte dort ins Leere (bei Tailscale sogar zwingend falsch, da nur 443 nach außen exponiert ist), der Klick auf „📈 Trade" landete dadurch nirgends und der Ticker kam nie im Trade-Planner an. Neue Hilfsfunktion mtsHauptappBasis(): bei https: wird location.origin verwendet (kein Port), nur bei reinem http: weiterhin hostname:8090. Betrifft auch den identischen Mechanismus im Price-Action-Hub (siehe dort).

Token: Lesen ist offen; Schreiben (Aktualisieren/Einstellungen speichern) verlangt das Token aus Signal-Hub/config.json. Einmal mit ?token=… öffnen — danach merkt sich das Gerät den Zugang (Cookie, 1 Jahr).

🗒️ Watchlist erstellen (Header-Toolbar, seit 16.08.2026): schreibt die aktuell gefilterte Ansicht (Markt/Score/Suche/alle Toggle-Filter, CURRENT_LIST aus render()) als TradingView-importierbare .txt + lesbare Zusammenfassung + reine Ticker-Liste nach Watchlisten/Signal-Hub/ im MTS-Hauptordner — identisches Dateiformat wie beim Trend-Screener (dort zuerst gebaut), eigener Unterordner statt eines gemeinsamen Zieldateinamens. Neuer, tokengeschützter Endpunkt POST /api/watchlist in Signal-Hub/src/server.py (dasselbe Token wie /api/run//api/config) — läuft über den eigenen 8091-Server, same-origin über den 8090-Proxy erreichbar wie die übrigen /Signal-Hub/api/*-Endpunkte, kein Sonderfall im Proxy nötig.

🌗 Hell/Dunkel-Umschaltung nachgerüstet (2026-08-24). Letzte der drei Migrationen der „mittleren" Größenklasse (nach Rotation-Dashboard und Price-Action-Hub) auf das im Wiki bewährte Muster: :root[data-theme] + prefers-color-scheme, Toggle-Button oben rechts im Header. Aufwendigste der bisherigen Migrationen, da Signal-Hub weit mehr eigenständig eingefärbte Badge-Typen besitzt als die anderen Apps (Pivot-Status, Fundamental-/Konsens-/Depot-/Hebel-/Dämpfungs-Badges und mehr) — jede feste Pastellfarbe, die bisher nur auf dem dunklen Kartenhintergrund lesbar war, hat jetzt ein eigenes, für Hellmodus nachgedunkeltes Gegenstück (z. B. --clr-mint, --clr-amber, --clr-lock) statt eines fest codierten Hex-Werts. Da die App ohne Live-Server praktisch immer im „Keine Daten"-Zustand lädt, wurden die Badges nicht über echte Testdaten, sondern gezielt per Testskript in den DOM injiziert und einzeln gegen ihren tatsächlichen Kartenhintergrund geprüft — alle 32 Badge-Typen liegen in beiden Theme-Zuständen über der Warnschwelle. Nachtrag: der Kerzenchart (inkl. SMA50/150/200-Linien, Volumen, Preislinien-Fallback) hatte zunächst noch einen fest dunklen Hintergrund behalten, analog zum Price-Action-Hub (siehe dort) mit demselben Nutzer-Feedback korrigiert — jetzt über --chart-bg/--chart-vol/--chart-blue/--chart-priceline themefähig. Sparkline und Score-Verlauf bleiben unverändert (kleine Statusindikatoren ohne eigenen Kartenhintergrund, keine Kontrastprobleme).

📐 Price-Action-Hub (Price-Action-Hub/price-action-hub.html, seit 2026-07-23)

Ergänzt den Signal-Hub um eine reine Preisverlauf-Sicht auf dieselben Aktien: liest Signal-Hub/data/signals.json für Symbol/Name/Markt (bewusste Entflechtung, Signal-Hub bleibt allein zuständig für „welche Ticker sind relevant" — Price-Action-Hub übernimmt daraus nicht den Signal-Hub-Score selbst, seit 2026-08-16 aber ein eng begrenztes Set an fertigen Ampelfeldern für die Hebel-Ampel, siehe unten), holt dafür aber eigenständig echte OHLC-Kursdaten (Open/High/Low/Close/Volumen) über die Yahoo-Chart-API und wertet fünf Price-Action-Muster aus den PDF-Quellen (Al Brooks, Rayner Teo, Tom Hougaard; lokal archiviert unter Trading-System/_archiv/2026-10-02_aufraeumen/price action/) aus: Trendbar-Stärke (Close-Position + Body-Anteil in der Tagesrange), Inside Bar (Konsolidierung im Vorbar-Rahmen), Bar-Counting (High1/2 bzw. Low1/2 nach einem Swing — nur die ersten zwei Korrekturbars gelten als zuverlässig), Breakout-Stärke/Failed Breakout (neues Hoch/Tief vs. 20-Tage-Referenz, Scheitern binnen 5 Bars) und Gap + Fill-Tracking. Dazu ein Kontext-Filter Marktstadium (Advancing/Accumulation/Distribution/Declining aus MA200-Stand + 21-Tage-Steigung, bewusst derselbe Lookback wie Signal-Hub/src/scorer.py::f_stage2, damit beide Apps nie widersprüchliche Trendaussagen treffen). Der resultierende Price-Action-Score (Price-Action-Hub/src/scorer.py::pa_score) ist eine einfache, transparente Summe der Musterergebnisse, gewichtet nach Richtung des Marktstadiums (Gegentrend-Signale zählen nur halb) — reine Dashboard-Sortierhilfe, kein Ersatz für den Signal-Hub-Score. Das Dashboard zeigt einen echten Kerzenchart (candlechart()) — seit 2026-07-24 zeigt auch das Signal-Hub-Minichart Kerzen in derselben Darstellung (siehe Signal-Hub-Abschnitt), womit beide Apps dasselbe Chartbild sprechen.

Datenfluss & Betrieb: Läuft als eigener, paralleler Job im selben Cloud-Workflow wie der Signal-Hub (pipeline.yml im öffentlichen Repo Miksch52/price-action-hub) — bekommt Signal-Hubs Ausgabedatei per actions/download-artifact, kein kompletter Signal-Hub-Checkout nötig. Kein eigener Server/Port/LaunchAgent: das Dashboard wird wie Coach/Wiki vom bestehenden 8090-Server statisch mitausgeliefert, Aktualisierung über denselben „☁️ Cloud-Scan starten"-Button wie der Signal-Hub. Eigener Yahoo-Cache, getrennt vom Signal-Hub-Cache.

DatumÄnderungGrund
07-28Eigenes, öffentliches Repo (Miksch52/price-action-hub)ausgelagert für unbegrenzte Actions-Minuten (private Repos sind gedeckelt, siehe Signal-Hub-Abschnitt)
08-09Eigener paralleler Job statt Schritt nach der Signal-Hub-Pipeline + eigener deploy-trigger.ymlläuft gleichzeitig mit Rotation-Dashboard statt danach (beide hängen nur von Signal-Hub ab); Deploy muss nicht mehr bis zu 6 Std. auf den nächsten Zyklus warten

📈 Trade-Planner-Verknüpfung (seit 2026-07-23): Jede Kachel hat wie im Signal-Hub einen „📈 Trade-Planner"-Link (identischer Deeplink-Mechanismus, ?neuerTrade=TICKER&quelle=Price-Action-Hub), der in der Hauptapp das Trade-Formular samt Risk-Reward-/Pyramidisierungs-Vorschau vorbefüllt öffnet. Basis-URL-Fix 2026-08-03 (dieselbe Ursache/Lösung wie im Signal-Hub, siehe dort): mtsHauptappBasis() nutzt bei https:-Zugriff (Cloudflare Pages/Tailscale) location.origin statt fälschlich :8090 anzuhängen.

Filter & Standardansicht (seit 2026-07-23): Das Dashboard startet nicht ungefiltert, sondern mit „Nur bullisch" + Min. |Score| 2 — zeigt die aktuell stärksten Treffer statt aller ~1300 Ticker; „↺ Zurücksetzen" stellt genau diesen Zustand wieder her. Zusätzlich zu Markt/Richtung/Score-Schwelle/Sortierung/Suche gibt es Muster-Chips (Starke Trendbar, Inside Bar, Bar-Counting zuverlässig, Breakout, Failed Breakout, Gap — Mehrfachauswahl UND-verknüpft) und einen Marktstadium-Filter (Advancing/Accumulation/Distribution/Declining). Die Suche akzeptiert mehrere Komma-/Leerzeichen-getrennte Begriffe (ODER-verknüpft) und durchsucht zusätzlich die Börse.

Fix 2026-08-17: Filterleiste blockierte auf dem iPhone dauerhaft die Ergebnisliste. <details id="filterbox" open> stand fest auf „offen" im HTML — anders als im Signal-Hub, wo dieselbe Leiste auf schmalen Bildschirmen von Anfang an eingeklappt startet und beim Runterscrollen automatisch zuklappt (mehr Platz für die Trefferkarten). Auf dem iPhone nahm die immer offene, sticky Filter-&-Sortierung-Leiste dadurch dauerhaft einen großen Teil des Bildschirms ein. Fix: open-Attribut entfernt (öffnet sich jetzt nur noch automatisch, wenn window.innerWidth>700, also am Desktop) plus 1:1 aus signal-hub.html übernommenes Scroll-Throttle (100 ms), das alle offenen details.filterbox-Panels (Filter & Sortierung, Legende, Hebel-/Muster-Backtest) beim Runterscrollen über 80 px automatisch schließt — bewusst kein Wiederaufklappen beim Hochscrollen, der Nutzer klickt gezielt erneut auf.

Qualitäts-/Konfluenzfilter (seit 2026-08-13): Drei weitere, rein clientseitig aus den ohnehin im Payload enthaltenen chart-Arrays (o/h/l/c/v) bzw. den bestehenden Musterfeldern berechnete Filter — kein neuer Pipeline-Lauf nötig, gedacht um die typischerweise ~100 Treffer der Standardansicht auf eine überschaubare Kernauswahl einzudampfen: Min. Konfluenz (0–5-Slider, zählt wie viele der fünf richtungsgebenden Signale — Trendbar, Bar-Counting, Breakout ohne Failed, offenes Gap, Marktstadium — gleichzeitig in dieselbe Richtung wie der Score zeigen; ein 🔗-N/5-Badge auf jeder Kachel zeigt den Wert direkt an), Ø Volumen (Ø Handelsvolumen in USD/EUR der letzten 20 Tage, Stufen ≥1/5/10/25 Mio. — filtert marktenge, schwer handelbare Titel raus) und Abstand 6M-Hoch (% Abstand des aktuellen Kurses vom höchsten Hoch im ~6-Monats-Chartfenster, Stufen ≤5/10/15/20 % — grenzt auf Titel nah am Hoch ein, echte Momentum-/Trendfortsetzungskandidaten statt tiefer Korrekturen). Zusätzliche Sortier-Option „Konfluenz". Alle drei starten auf „Alle" (kein Effekt auf die Standardansicht), bis der Nutzer sie aktiv setzt.

Tri-State-Qualitätsfilter (seit 2026-08-13): Drei weitere Chips in einer eigenen Gruppe „Qualität", die anders als die bestehenden Muster-Chips (nur „muss vorhanden sein") per Klick zwischen drei Zuständen zyklen: aus → aktiv (grün, muss zutreffen) → negativ (rot, darf nicht zutreffen) → wieder aus. 📊 Volumen-bestätigter Breakout (Breakout mit Vol-Ratio ≥ 1.5x — aktiv zeigt nur überzeugend bestätigte Ausbrüche, negativ blendet genau diese aus und lässt schwach-volumige/keine Breakouts stehen), ⛔ Failed Breakout (aktiv = nur gescheiterte Ausbrüche, negativ = gescheiterte Ausbrüche explizit ausschließen — ersetzt den bisherigen einfachen Muster-Chip „Failed Breakout"), 🎯 Trendkonform (prüft, ob alle aktuell aktiven richtungsgebenden Muster — Trendbar, Bar-Counting, Breakout, Gap — mit dem Marktstadium-Kontext übereinstimmen, dieselbe Bull-/Bär-Kontext-Logik wie scorer.py::_gewichtet, das Gegentrend-Signale nur halb gewichtet statt hart auszuschließen; aktiv zeigt nur widerspruchsfreie Chartbilder, negativ isoliert genau die Ticker mit mindestens einem Gegentrend-Signal). Alle drei rein clientseitig aus den bestehenden Musterfeldern berechnet, kein neuer Pipeline-Lauf nötig.

🔎 Sofort-Überblick auf jeder Kachel (seit 2026-08-03, paQuickscan()): kompakter Punkte-Raster analog zum Signal-Hub, aber auf die eigenen Muster gemünzt (Trend-Bar, Inside Bar, Bar-Counting, Breakout, Gap, Marktstadium) statt der Signal-Hub-Score-Faktoren — anders als dort ist Grau hier der Normalfall („Muster nicht vorhanden"), nicht „schwach"; Grün/Rot markieren ein ausgeprägtes bullisches/bärisches Muster, Gelb ein vorhandenes, aber schwächeres (Inside Bar, Failed Breakout, gefülltes Gap, unzuverlässiges Bar-Counting). Ergänzt die bestehenden Badges, ersetzt sie nicht.

🔍 Setup-Analyse-Verknüpfung (seit 2026-08-07): Jede Kachel verlinkt jetzt zusätzlich auf ../Signal-Hub/setup-detail.html?ticker=… — derselbe Detail-Überblick wie bei den Top-Setups und im Signal-Hub (Score-Faktoren, Pivot-Kennzahlen, Price-Action-Muster, TradingView-Chart, Fundamentaldaten & Story). Einschränkung, seit 2026-09-10 abgemildert: den vollen Detail-Überblick (Score-Faktoren, Pivot, Price-Action, Minervini-Regel-Gates) gibt es weiterhin nur für einen Eintrag in Signal-Hub/data/signals.json (dort landen nur Ticker mit Score ≥ „Beobachten") — Price-Action-Hub deckt aber ein deutlich breiteres Universum ab (~1300 vs. ~740 überschneidende Ticker, ≈ 57 %). Für den Rest zeigt die Seite seit 2026-09-10 keinen reinen Fehler mehr, sondern die 🔎 Direktanalyse (Live-Kurs, Chart, Fundamentaldaten, Code 33 — siehe Signal-Hub-Abschnitt „Ticker wechseln"), nur ohne die scan-gebundenen Abschnitte.

⚡ Hebel-Ampel (seit 2026-08-16, scorer.py::hebel_ampel()): dieselbe Reife-Ampel für Hebelprodukte (Knock-Outs/Optionsscheine, z. B. Finanzen.net Zero) wie im Signal-Hub-Abschnitt beschrieben — identische Kriterien (Markt-Ampel, Stage-2-Trend, Basis/VCP, Pivot-Trigger, kein Extended/Earnings/50-80/Klimax-Risiko), hier aber serverseitig in Python berechnet, weil der Price-Action-Hub bewusst kein 8 MB großes signals.js zusätzlich clientseitig laden soll (dieselbe Überlegung wie beim „🏆 Top-Setups"-Panel auf der Startseite). Dafür liest lade_tickerliste() zusätzlich zur Tickerliste ein eng begrenztes Set an bereits fertigen Ampel-/Risikofeldern je Ticker aus signals.json (Stage-2-Trend, Basis/VCP, Extended, Earnings, 50/80, Klimax, Marktregime) sowie den Pivot-Status aus Signal-Hub/data/pivot.json — reines Durchreichen fertiger Werte, keine Neuberechnung und kein Ersatz für pa_score (siehe Entflechtungshinweis oben). Badge „🟢 Hebel bereit"/„🟡 Hebel im Fokus" steht als erster Hinweis noch vor dem Marktstadium-Badge auf der Kachel (🔴 bewusst ohne Badge); eigene Filtergruppe „Hebel-Trade" mit Checkbox „⚡ nur Hebel-Kandidaten (grün)" (Standard aus).

📊 Hebel-Ampel-Backtest (seit 2026-08-16, Price-Action-Hub/src/hebel_backtest.py): misst, OB „🟢 Hebel bereit" tatsächlich einen besseren Forward-Return liefert als „🟡 Hebel im Fokus" — unverzerrtes Forward-Logbuch nach demselben Grundprinzip wie Signal-Hubs pivot_backtest.py, hier bewusst ohne Retro-Modus: die Hebel-Ampel kombiniert mehrere fremde Ampeln (Markt-Regime, Pivot-Status, Stage-2-Trend, …), eine rückwirkende Walk-Forward-Rekonstruktion wäre für einen ersten Baustein unverhältnismäßig aufwendig. run.py hängt bei jedem Lauf die heutigen grün/gelb-Ticker mit Datum + Kurs ans lokale Logbuch und wertet zugleich die gereiften Picks (≥21/50/78 Kalendertage) gegen aktuelle Yahoo-Kurse aus. Ausgabe Price-Action-Hub/data/hebel_backtest.json, im Dashboard als eingeklapptes Panel „⚡ Trefferquoten Hebel-Ampel (Forward-Test)" mit vorangestelltem Win-Rate-Balkendiagramm sichtbar, sobald die ersten Fälle gereift sind.

Persistenz von Anfang an über R2: anders als beim ursprünglichen Signal-Hub-Pivot-Logbuch (das erst nachträglich von reinem Mac-mini-Zustand auf R2 umgestellt wurde) gibt es hier von vornherein keinen lokalen Dauerlauf, der als Notlösung einspringen könnte — Price-Action-Hub läuft ausschließlich in der Cloud, jeder Runner ist pro Lauf frisch. Ohne die R2-Sicherung hätte die Stichprobe nie über Kalenderzeit wachsen können.

📊 Price-Action-Muster-Backtest (seit 2026-08-17, Price-Action-Hub/src/muster_backtest.py): gleiches Prinzip wie der Hebel-Ampel-Backtest direkt darüber, hier für sechs Kohorten aus den eigentlichen Chartmustern: breakout_up/breakout_down (frischer Ausbruch gegen die 20-Tage-Referenz — bewusst ohne das failed-Flag aus muster.py als Vorfilter, das reift erst über die nächsten 5 Bars und ist am Signaltag selbst immer None; der 4/8/12-Wochen-Forward-Return hier ist die eigentliche, spätere Antwort auf „hat der Ausbruch gehalten"), trendbar_stark_bull/trendbar_stark_bear (starke Trendbar) und score_bullisch/score_baerisch (pa_score ≥2/≤-2, dieselbe Schwelle wie die Standardansicht im Dashboard). Ein Ticker kann an einem Tag mehrere Kohorten gleichzeitig treffen. Ausgabe Price-Action-Hub/data/muster_backtest.json, Panel „📐 Trefferquoten Price-Action-Muster (Forward-Test)" mit vorangestelltem Win-Rate-Balkendiagramm (seit 2026-08-17, gleiches Prinzip wie beim Hebel-Ampel-Backtest — Zeilenhöhe passt sich dynamisch an, wie viele Horizonte je Kohorte tatsächlich gereift sind, statt leere Balken-Slots stehen zu lassen). Logbuch ~/Library/Application Support/PriceActionHub/muster_logbuch.json, R2-Persistenz von Anfang an (_state/muster_logbuch.json, gleicher Grund wie beim Hebel-Ampel-Backtest — kein lokaler Dauerlauf als Ausweichquelle).

🔮 Vorausschau-Zähler an den Filter-Chips (seit 2026-08-16, updateChipCounts()): jeder Muster-Chip (Starke Trendbar/Inside Bar/Bar-Counting/Breakout/Gap), jeder Qualitäts-Chip (Volumen-bestätigter Breakout/Failed Breakout/Trendkonform) sowie jede Option im Marktstadium-Dropdown zeigt jetzt direkt eine Zahl („🚀 Breakout · 65", „Advancing · 504") — wie viele Ticker im gesamten Datensatz genau dieses eine Kriterium erfüllen, unabhängig von sonst aktiven Filtern (gleiches Prinzip wie die bestehenden Zähler bei „🎯 nur Pivot-Setups"/„🏆 Top-Setups" im Signal-Hub bzw. „⚡ nur Hebel-Kandidaten" hier). Einmalig nach dem Laden berechnet, kein Filterwechsel nötig, um die Selektivität einer Option vorab abzuschätzen.

📖 Betriebsanleitung: Wie beim Signal-Hub verlinkt der Header-Button „📖 Anleitung" auf eine dedizierte Symbol-/Filter-Erklärung (Claude-Artifact), inklusive Abschnitt „Trainingsphase" (die drei Wochen-Kandidaten per Suchfeld aufrufen und das Chartbild prüfen, ein gescheiterter Ausbruch ist Warnhinweis, kein Verkaufsbefehl) — Price-Action-Hub-Anleitung. Diese Seite (und dieser Wiki-Abschnitt) werden bei jeder funktionalen Änderung am Tool mit aktualisiert.

🗒️ Watchlist erstellen (Header-Toolbar, seit 16.08.2026): identisches Prinzip wie im Signal-Hub/Trend-Screener (siehe dort) — schreibt die aktuell gefilterte Ansicht (Markt/Richtung/Score/Muster/Qualität/Suche, CURRENT_LIST aus render()) nach Watchlisten/Price-Action-Hub/ im MTS-Hauptordner. Price-Action-Hub hat keinen eigenen Server/Port — der neue Endpunkt POST /Price-Action-Hub/api/watchlist läuft deshalb direkt im 8090-Server (server_mts.py::_api_watchlist, ohne Proxy-Umweg und ohne Token — die App hat ohnehin keine anderen geschützten Schreib-Endpunkte); funktioniert nur mit erreichbarem lokalem Server (Mac mini/Tailscale), nicht auf der reinen Pages-Version.

🌗 Hell/Dunkel-Umschaltung nachgerüstet (2026-08-24). Zweite von drei Migrationen der „mittleren" Größenklasse (nach Rotation-Dashboard) auf das im Wiki bewährte Muster: :root[data-theme] + prefers-color-scheme, Toggle-Button oben rechts im Header. Neben dem üblichen Umbau (Header-/Filterbox-Hintergrund von fest codiertem Farbverlauf auf themefähige Variable, Aurora-Glow-Opazität für Hellmodus deutlich reduziert) mussten hier zusätzlich alle pastellfarbenen Badge-Texte (bullisch/bärisch/neutral/Failed-Breakout, Marktstadien) auf eigene, für Hellmodus nachgedunkelte Textvariablen umgestellt werden — die bisherigen hellen Pastelltöne waren nur auf dem dunklen Kartenhintergrund lesbar. Nachtrag: der Kerzenchart selbst hatte zunächst noch einen fest dunklen Hintergrund (Trading-Terminal-Optik) behalten — im Nutzertest passte das nicht mehr zum sonst hellen Kartendesign, jetzt ebenfalls über --chart-bg/--chart-vol themefähig, Kerzenfarben laufen über die ohnehin schon themefähigen --ok/--bad.

🔄 Rotation-Dashboard (Rotation-Dashboard/rotation-dashboard.html, seit 2026-07-28)

Sektor-/Themen-Rotation à la IBD: welche der ~20 Themen-ETFs (Industrials, Halbleiter, Biotech, Cyber, Energie, Uran, Software, Regionalbanken, Defense/Aero, Solar, Gold-Miner, KI-Robotik u. a.) sind gerade am stärksten, welche Einzelaktien führen darin, und welche Aktien halten sich an roten Marktagen überdurchschnittlich gut. Drei Tabellen: Sektoren (RS-Rang, 1M/3M-Performance, Abstand vom 52-Wochen-Hoch, über 50-Tage-Linie), Leader in den stärksten Themen (Top-Aktien je Top-Sektor) und Rote-Tage-Resilienz (Aktien, die an den jüngsten Markt-Abwärtstagen relativ zur eigenen Norm überdurchschnittlich stark waren).

Echtes IBD-Relative-Strength-Rating für die Themen-ETFs: Anders als eine simple %-Performance berechnet Rotation-Dashboard/src/rs.py für jeden Themen-ETF dieselbe gewichtete Formel, die IBD für sein RS-Rating nutzt und die der Trend-Screener bereits für Einzelaktien verwendet (raw_rs_score: Outperformance ggü. ^GSPC über 3/6/9/12 Monate, gewichtet 40/20/20/20 — jüngstes Quartal zählt am meisten), anschließend Perzentil-Rang 1–99 unter den ~20 Themen-ETFs. Formel bewusst 1:1 aus dem externen Trend-Screener-Projekt übernommen (kein Neuerfinden, keine Import-Abhängigkeit zwischen den Repos — Entflechtung).

Keine doppelten Datenzüge: Sektor-Tag, RS-Rating, Kurs und Trend-Template-Kriterienzahl je Einzelaktie kommen unverändert aus dem Markets-360-Export (Rotation-Dashboard/src/markets360_reader.py liest denselben _magazine/markets360/markets360_latest.csv, den Signal-Hub im selben Workflow-Lauf schon per rclone gezogen hat, mit lokalem Fallback auf die Original-Datei wie bei Signal-Hub) — keine eigene RS-Berechnung für Einzelaktien. Neu geholt wird nur (1) der Kursverlauf der Themen-ETFs selbst (für deren eigenes RS-Rating, das sonst niemand berechnet) und (2) eine kleine Teilmenge von ~50–80 Leader-/Resilienz-Kandidaten-Tickern für 1M/3M-Performance, Volumen-Ratio und die Rote-Tage-Berechnung — nicht das ganze Aktienuniversum.

Bekannte Einschränkung — Sektor→Themen-Zuordnung: Die Markets-360-„Sektor"-Spalte ist eine grobe GICS-/TradingView-Branchenklassifizierung, keine thematische Nische. Themen-ETFs, die quer durch mehrere Branchen gehen (Biotech, Cyber, Uran/Nuklear, Regionalbanken, KI-Robotik, Gold-Miner, Solar, Defense/Aero), bekommen deshalb selten oder nie Leader-Aktien zugeordnet (sektor_mapping.py dokumentiert das bewusst, statt es über eine Ticker-Einzelfallliste zu "reparieren") — deren eigenes ETF-RS in der Sektoren-Tabelle bleibt davon unberührt und korrekt.

🔗 TradingView-Verlinkung & 📖 Anleitung (seit 2026-08-09): Jedes Ticker-Symbol in allen vier Tabellen (Sektoren-ETFs, Leader, Rote-Tage-Resilienz, Vorbörse) öffnet per Klick den TradingView-Chart in neuem Tab — dieselbe Yahoo-Suffix→TradingView-Börsencode-Zuordnung wie in der Signal-Hub-Setup-Analyse (YAHOO_SUFFIX_TV_EXCHANGE, 1:1 dupliziert, Single-File-App-Prinzip). Header-Button „📖 Anleitung" verlinkt eine dedizierte Betriebsanleitung als Claude-Artifact (Marktregime-Panel, Themen-RS-Formel, die vier Tabellen, Datenherkunft, Abschnitt „Trainingsphase" mit dem Wochenend-Gegencheck „steht der Kandidat in einem führenden Thema?" und Lernpfad-Woche 9) — Rotation-Dashboard-Anleitung. Design-System siehe CLAUDE.md.

Sortierbar, filterbar, Firmennamen, 🔍 Setup-Analyse (seit 2026-08-30): Jede Spaltenüberschrift in allen vier Tabellen ist klickbar (Pfeil zeigt aktive Spalte/Richtung, erneuter Klick kehrt um) — Sortierzustand ist je Tabelle unabhängig. Ein globales Textfeld über der Regime-Karte filtert alle vier Tabellen gleichzeitig nach Ticker, Firmenname oder Thema. Leader/Resilienz/Vorbörse zeigen jetzt zusätzlich den vollen Firmennamen (Feld name aus dem Markets-360-Export, markets360_reader.py liefert ihn ohnehin schon, run.py reicht ihn seither auch an leaders/resilient durch) sowie einen 🔍-Link zur Minervini-Setup-Analyse (../Signal-Hub/setup-detail.html?ticker=…, identisches Deeplink-Muster wie in Startseite/Signal-Hub/Price-Action-Hub — ohne Treffer in signals.json zeigt die Zielseite „kein Treffer", z. B. bei SHEL.L — TTE.PA dagegen hat aktuell einen echten Signal-Hub-Treffer).

Datenfluss & Betrieb: Läuft wie Price-Action-Hub im selben Cloud-Workflow (pipeline.yml, Repo Miksch52/signal-hub, eigenes öffentliches Repo Miksch52/rotation-dashboard ohne eigenen Pipeline-Workflow), seit 2026-08-09 als eigener, zu Price-Action-Hub paralleler Job (rotation-dashboard) statt als Schritt danach — beide hängen nur von Signal-Hubs Ausgabe ab, nie voneinander, laufen deshalb gleichzeitig. Bekommt Signal-Hubs data/signals.json (Gate) und die Markets-360-CSV aus dessen Magazin-Ordner separat per actions/download-artifact in die von pfade.py erwartete Geschwister-Struktur Signal-Hub/data/ bzw. Signal-Hub/_magazine/markets360/ gelegt (statt wie zuvor den kompletten Signal-Hub-Checkout mitzuziehen), ebenfalls gegated auf [ -f ../Signal-Hub/data/signals.json ]. Kein eigener Server/Port/LaunchAgent — wird wie Coach/Wiki vom bestehenden 8090-Server statisch mitausgeliefert; Aktualisierung über denselben „☁️ Cloud-Scan starten"-Button (triggerCloudWorkflow(), Ziel-Repo signal-hub, Workflow pipeline.yml). Eigener Yahoo-Cache (~/Library/Application Support/RotationDashboard), getrennt von den anderen beiden Apps. Eigener Deploy-Trigger (seit 2026-08-09): derselbe deploy-trigger.yml-Mechanismus wie bei Price-Action-Hub — ein Edit direkt an dieser App löst sofort einen Pages-Deploy aus, ohne auf den nächsten Signal-Hub-Zyklus zu warten (Secret DEPLOY_TRIGGER_TOKEN separat in diesem Repo nötig).

📊 Setup-Backtest (seit 2026-08-17, Rotation-Dashboard/src/rotation_backtest.py): unverzerrter Forward-Test nach demselben Grundprinzip wie die Backtests in Signal-Hub/Price-Action-Hub (kein Retro-Modus) für drei Kohorten:

  • top_rs_etf — Themen-ETF mit RS-Rating ≥80 (die IBD/Minervini-Konvention für „führend"), testet die Momentum-Persistenz des eigenen ETF-RS-Ratings.
  • leader — Leader-Aktie im stärksten Thema.
  • resilient — Rote-Tage-Resilienz-Kandidat.

Seit 2026-09-12 mit Index-Vergleich (vorher bewusst nur absoluter Forward-Return, siehe Abschnitt „Index-Vergleich in allen Engines" weiter oben). run.py hängt bei jedem Lauf die heutigen Treffer je Kategorie mit Datum + Kurs ans Logbuch und wertet zugleich die gereiften Picks (≥21/50/78 Kalendertage) gegen aktuelle Kurse aus. Ausgabe data/rotation_backtest.json, im Dashboard als Tabellenabschnitt „Trefferquoten (Forward-Test)" mit vorangestelltem Win-Rate-Balkendiagramm sichtbar, sobald die ersten Fälle gereift sind.

R2-Persistenz von Anfang an: Rotation-Dashboard hat wie Price-Action-Hub keinen eigenen LaunchAgent, läuft ausschließlich in der Cloud — ohne die R2-Sicherung hätte die Stichprobe nie über Kalenderzeit wachsen können.

🌗 Hell/Dunkel-Umschaltung nachgerüstet (2026-08-24). Erste der drei Migrationen der „mittleren" Größenklasse (nach Backtest-Vergleich, dem kleinsten und ersten migrierten Kandidaten) auf das im Wiki bewährte Muster: :root[data-theme] + prefers-color-scheme, Toggle-Button neben dem Sprachumschalter im Header. Dabei zwei bislang fest codierte, vom Theme unabhängige Hintergründe gefunden und auf themefähige Variablen umgestellt: die Regime-Karte (linear-gradient) und die Vorbörsen-Warnbox — beide hätten im Hellmodus einen dunklen Kasten mit nicht mehr lesbarem Text gezeigt. Gefunden über dieselbe Gradient-Erkennungslücke im Prüfskript, die schon bei der Backtest-Vergleich-Migration den dortigen Header-Bug aufgedeckt hatte.

📊 Backtest-Vergleich (backtest-vergleich.html, seit 2026-08-17)

Stufe 3 des Backtest-Ausbaus: ein filter- und sortierbares Panel, das alle unverzerrten Forward-Test-Kohorten der fünf Backtest-Engines an einem Ort zusammenführt — Signal-Hubs Pivot-Detektor (ARMED/BREAKOUT) und Momentum-Score (Tier A/B), Price-Action-Hubs Hebel-Ampel (grün/gelb) und Chartmuster (Breakout/Trendbar/Score), Rotation-Dashboards Setup-Kategorien (Top-RS-ETF/Leader/Resilienz). Beantwortet direkt „was ist gerade das beste Setup im ganzen System" statt fünf Apps einzeln durchklicken zu müssen. Bewusst kein Merge, sondern ein Aggregator (Entflechtungsprinzip): liest live die fünf bereits bestehenden Backtest-JSON-Dateien der drei Apps per relativem fetch() (funktioniert lokal über den 8090-Server UND auf Cloudflare Pages, da alle Apps als Geschwister-Ordner unter derselben Domain liegen — identisches Prinzip wie index.htmls bestehender Top-Setups-Fetch auf Signal-Hub/data/top_setups.js), normalisiert sie in eine flache Zeilenliste und verlinkt jede Zeile zur jeweiligen App-Detailseite zurück. Reine Retro-/Baseline-Kohorten ohne eigenen Forward-Test (z. B. WATCH) sind bewusst ausgeschlossen, damit alle gezeigten Zahlen methodisch vergleichbar bleiben.

Filter: Quelle (5 Chips, Mehrfachauswahl), Horizont (4W/8W/12W, Mehrfachauswahl), Min. n (Stichprobengröße, Standard ≥20 gegen Zufallsrauschen bei kleinen Kohorten) und Sortierung (Win-Rate/Ø-Return/n). Hero-Kennzahl oben zeigt immer das aktuell beste Setup über alle Quellen (unabhängig von der Quelle-Filterauswahl, reagiert nur auf die Datenlage selbst) mit Link zur Detailseite. Win-Rate je Zeile zusätzlich als kleiner Inline-Balken (grün/rot ggü. der 50 %-Basislinie) für den schnellen visuellen Vergleich.

📖 Betriebsanleitung (seit 2026-08-27): Icon-Button „📖 Anleitung" im Header verlinkt eine dedizierte Anleitung als Claude-Artifact — Filter-Nutzung, wann eine Backtest-Zahl belastbar ist (inklusive Spalte „vs. Index"), die SEPA-Zeilenpaare des Pivot-Detektors, automatischer Wächter; im Training gehört die Seite in den Monats-Rückblick — Backtest-Vergleich-Anleitung. Design-System und URL siehe CLAUDE.md.

🎚️ Hinweis auf die SEPA-Kalibrierung (seit 2026-08-30): Textbox direkt unter der Einleitung, eigene .neu-hinweis-Klasse (App-eigene Tokens --surface/--border/--primary2, keine Wiki-Klassen übernommen — viertes, eigenständiges Design-System, siehe CLAUDE.md). Erklärt, dass die sieben neuen SEPA-Kriterien aus dem Signal-Hub hier noch nicht als Zeilen erscheinen — ihre Forward-Kohorten stehen bei n = 0, und Zeilen mit n = 0 blendet diese Seite grundsätzlich aus (if(!s || !s.n) continue; in ladeAlles()) — eine Aufnahme in die feste kohorten-Zuordnung wäre also unsichtbar geblieben. Verlinkt stattdessen den aktuellen Messstand als Claude-Artifact (SEPA-Kalibrierung). Seit 2026-09-22 umgesetzt: Die Kohorten sind im 4W-Horizont gereift (ARMED bis n ≈ 170) und stehen jetzt als eigene Pivot-Zeilen in der Tabelle — je Kriterium ein Paar „erfüllt / nicht erfüllt" für ARMED und BREAKOUT, zusammengesetzt in SEPA_KOHORTEN und per Object.assign in QUELLEN[0].kohorten eingehängt (Labels und Schwellen wie in pivot.py). Die Textbox bleibt als kurze Einordnung stehen: nur 4W gereift, Messung statt Filter, Gate-Auswertung am 19.10.

Bugfix beim Bauen entdeckt (2026-08-17): cloudflare-pages/deploy.command hat eine explizite Datei-Whitelist (DATENDATEIEN) für das, was auf mts-hub.pages.dev landet — die neuen Stufe-1-Backtest-Ausgaben hebel_backtest.json/muster_backtest.json/rotation_backtest.json fehlten dort komplett, obwohl der Cloud-Job sie längst per Wildcard nach R2 hochlud. Da Price-Action-Hub/Rotation-Dashboard-Daten vom lokalen 8090-Server immer zu mts-hub.pages.dev durchgereicht werden (server_mts.py::_proxy_cloud_data, kein eigener Dauerlauf für diese Apps), waren die drei neuen Panels dadurch nirgends erreichbar gewesen, auch nicht über den Mac mini — jetzt nachgetragen. Gleicher Fehler auch bei den HTML-Dateien selbst behoben (backtest-vergleich.html fehlte in der HTML-Whitelist).

Verlinkt von der Startseite unter „Trading Apps" (kein eigener Server/Port/LaunchAgent, wird wie Coach/Wiki vom bestehenden 8090-Server statisch mitausgeliefert bzw. komplett client-seitig auf Cloudflare Pages).

🌗 Hell/Dunkel-Umschaltung nachgerüstet (2026-08-24). Eine Systemprüfung fand vier verschiedene Theme-Mechanismen quer über die zehn Apps (u. a. manueller JS-Toggle in Hauptapp/Coach, reiner prefers-color-scheme ohne Toggle bei Markets 360, gar nichts bei fünf weiteren Apps) — je nach App unterschiedliches Verhalten ohne erkennbares Muster. Backtest-Vergleich war die erste Migration einer laufenden Vereinheitlichung auf das bereits im Wiki und im Ruhestands-Fahrplan bewährte Muster: :root[data-theme] + prefers-color-scheme (System-Standard, manueller Toggle-Knopf oben rechts überschreibt und merkt sich die Wahl in localStorage). Dabei per neu gebautem tests/theme-audit.js (siehe Abschnitt 8 „Tests") einen echten Bug im vorher fest codierten, dunklen Header-Farbverlauf gefunden und behoben, der den Titel im Hellmodus fast unlesbar gemacht hätte — das Prüfskript prüfte zu dem Zeitpunkt nur background-color, nicht Gradient-Hintergründe (background-image), und wurde im selben Zug entsprechend nachgeschärft. Die übrigen neun Apps folgen schrittweise nach Größe/Risiko (klein zuerst), das optische Design jeder App bleibt dabei unangetastet — nur der Umschalt-Mechanismus wird vereinheitlicht.
📐 Forward-Test-Wächter (Signal-Hub/src/forward_test_review.py, seit 2026-08-23) — der teuerste Einzelbefund der Systemprüfung, automatisiert. Der Kommentar in top_setups.py sagte schon vorher selbst: „Bei genug neuen Forward-Daten erneut prüfen." Diese Anweisung existierte aber nur als Text — nie als etwas, das tatsächlich passiert ist. Die ARMED-Stichprobe wuchs von n = 83 auf n = 1005 (zwölffach), der Wert fiel von 71 % auf 48,8 %, und niemand hat es gesehen, bis die Systemprüfung von außen draufschaute. Eine monatliche Kalender-Erinnerung hätte dasselbe Problem nur eine Ebene höher verschoben — sie hängt wieder daran, dass ein Mensch sie ernst nimmt.

Läuft im deploy-Job von Signal-Hub/.github/workflows/pipeline.yml (dort liegen nach dem Artifact-Download bereits alle sechs Backtest-Ausgaben aus allen drei Repos vor — die fünf dieser Vergleichsseite plus regime_backtest.json, das die Systemprüfung explizit als „null realisierte Beobachtungen" nannte) und vergleicht automatisch gegen einen rollierenden Monats-Schnappschuss (_state/forward_test_snapshot.json in R2). Vier mechanische Auslöser, keine Einzelfallentscheidung nötig:
  • n hat 100 überschritten — eine Kohorte wird erstmals belastbar.
  • Wilson-95%-Konfidenz-Untergrenze hat 50 % gekreuzt — in beide Richtungen: neu belegt oder nicht mehr belegt (dieselbe Formel wie beim Kern-Setup-Beleg im Startseiten-Panel, siehe Kasten „Verschärfte Beleg-Regel" oben).
  • Bei Pivot: Rangfolge ARMED vs. BREAKOUT gedreht — genau der Fall aus dem Bericht, jetzt am selben Horizont automatisch verglichen (ab n ≥ 8 je Seite).
  • Eine Engine ist seit dem letzten Vergleich weiterhin komplett bei 0 — reift nicht, obwohl ein ganzer Monat vergangen ist.
Bewusst monatlich, nicht bei jedem Pipeline-Lauf: Vergleich und Schnappschuss-Erneuerung sind ein atomares Ereignis — nur wenn der gespeicherte Stand ≥ 28 Tage alt ist, wird überhaupt verglichen und erst dann auf „heute" weitergerückt. An allen anderen Tagen (also den meisten der 4×/Tag-Läufe) passiert nichts, kein zusätzlicher State-Datei-Anti-Spam nötig, da ohne Vergleich auch kein Push entstehen kann. Push über denselben ntfy-Kanal wie die übrigen Quellen — Ziel kommt hier bewusst per Umgebungsvariable statt eigener config.json-Datei (der deploy-Job hat, anders als der signal-hub-Job, keine geschriebene Konfigurationsdatei; gleiches Muster wie der Token-Wächter im selben Job).

Statistik-Skeptiker

🧮 Vor jeder Änderung aus einem Backtest-Befund (seit 2026-09-17, Claude-Code-Subagent statistik-skeptiker + Werkzeug agenten/kohorten_test.py, nur lesend): Kohortentabellen lesen sich wie Befunde, obwohl Einzel-Returns mit rund 14 Pp streuen. Bevor Score-Gewichte, Schwellen, Filter oder Setup-Regeln wegen „X schneidet besser ab als Y" geändert werden, prüft der Skeptiker den Vergleich auf den Episodenlisten (forward_einzelfaelle, bevorzugt indexbereinigt edge_idx_pct): Welch-t-Wert, Schwelle nach Mehrfachtest-Korrektur (wer die beste aus sechs Varianten nimmt, braucht |t| > 2,64 statt 1,96), t nach Signal-Tagen geclustert (Episoden desselben Tages sind nicht unabhängig), gleiches Vorzeichen in beiden Zeithälften und kleinster nachweisbarer Effekt der Stichprobe. Urteil belegt nur, wenn alles davon hält und jede Gruppe auf mindestens 20 Signal-Tagen beruht — sonst Indiz oder Rauschen, Empfehlung dann immer „nicht ändern". Beispiel am Pivot-Forward-Test (4W): BREAKOUT vs. ARMED +2,99 Pp, t = 4,34 — aber nur 11 Signal-Tage, daher „Indiz". Selbst aufrufen: python3 agenten/kohorten_test.py Signal-Hub/data/pivot_backtest.json --liste forward_einzelfaelle --gruppe status --a BREAKOUT --b ARMED --wert edge_idx_pct --filter horizont=4W --varianten 1. Änderungen aus reinen Risikogründen bleiben erlaubt, werden aber als solche gekennzeichnet.

🔍 Markets360 Screener (Streamlit, Port 8501)

Volles Screening-Werkzeug für VCP/RS-Rating/Fab-5 (Minervini-Kennzahlen) über ein wählbares Universum (S&P 500, Nasdaq-100, DAX, MDAX, EU-Kern) — nicht zu verwechseln mit „Minervini Markets 360" (Port 8080, siehe oben), der reinen Marktsicht/-ampel. Beide teilen sich dieselbe Python-Engine (minervini360-Package) und damit exakt dieselbe Marktampel-Berechnung (siehe „Woraus die Marktampel berechnet wird" im 8080-Abschnitt) — nur der Screener rechnet zusätzlich den vollen Einzeltitel-Scan.

Sidebar-Features (seit 2026-07-16): Markt-/Index-Auswahl mit automatischer Wikipedia-Aktualisierung der Konstituenten-Listen (>7 Tage alt → Refresh), Zusatz-Indikatoren (RSI/MACD/ADX/Stochastik/Bollinger/OBV) mit Erläuterungen, Pivot-Abstands-Filter („nahe am Kaufpunkt"), Marktampel-Verlauf über die Zeit und ein Scan-Vergleich (gegenüber dem letzten Lauf neu hinzugekommene/herausgefallene Ticker).

☁️ Cloud-Nachtscan (GitHub Actions, seit 2026-07-17): läuft zusätzlich ohne Mac mini als eigener Workflow im Repo Miksch52/minervini-markets-360 (nightly.yml, täglich 05:38 Europe/Berlin — krumme Minute seit 2026-09-10, siehe Cron-Absatz beim Signal Hub —, minervini360.cli screen --top 100) und lädt das Ergebnis per rclone nach r2:signalhub-magazine/markets360/ hoch — von dort liest der Daily Signal Hub es als eigene, unabhängige Quelle ein (siehe Datenfusion). Startseite/Signal-Hub haben zusätzlich einen „☁️ Cloud-Scan starten"-Button, der genau diesen Workflow von jedem Gerät aus anstößt. Seit 2026-08-20 sichert derselbe Lauf das Ergebnis zusätzlich datiert unter r2:signalhub-magazine/markets360/history/JJJJ-MM-TT.csv — der Pfad oben bleibt der überschriebene „letzter Stand", die Historie wächst separat (Top-Setups-Roadmap Punkt 5, gleiches Muster wie der OHLC-Tagessnapshot im Signal-Hub).

📅 Handelstag belegt statt Schreibdatum (seit 2026-09-11). Die datierte Historie bekam bis dahin das Datum des Schreibzeitpunkts. Ein Scan um 05:38 enthält aber den Schluss des Vortags, und weil der Workflow auch am Wochenende und an US-Feiertagen läuft, lagen bis zu vier Kopien desselben Handelstags unter verschiedenen Daten (05.–08.09. enthielten alle den 04.09.). Ein Abgleich zeigte zudem veraltete Kurse – die Datei vom 04.09. enthielt den Stand vom 02.09.; eine Kalenderrechnung hätte das nicht erkannt. scripts/history_datum.py gleicht deshalb vor dem Schreiben eine Stichprobe gegen Yahoos finale Schlusskurse ab: nur ein klar belegter US-Handelstag wird Dateiname (mindestens 85 % Treffer, deutlicher Abstand zum nächstbesten Tag, ein noch laufender Handelstag ist nie Kandidat), sonst entfällt der Eintrag für diesen Lauf. Eine vorhandene Tagesdatei wird nie überschrieben. Europa steht nur in history/_meta/JJJJ-MM-TT.json: durch die Cron-Verspätung fällt der Scan meist in die offene EU-Sitzung, die EU-Zeilen sind dann Intraday-Stände.

Altbestand korrigiert (11.09.): 12 der 21 Dateien auf ihren tatsächlichen Handelstag umbenannt (Inhalt byte-identisch), 9 aussortiert – 7 Duplikate desselben Handelstags, 2 Intraday-Stände vom 27./28.08. Für den 26.08. und 03.09. existiert keine verwertbare Aufnahme. Originale unter r2:signalhub-magazine/_archiv/history-vor-korrektur-2026-09-11/markets360/history/, Zuordnung je Datei in history/_korrektur_2026-09-11.json.

Lokaler Dauerbetrieb: LaunchAgent com.maick.markets360.dashboard (osascript-Wrapper wegen TCC + lsof-Guard gegen Doppelstarts, wie die Signal-Hub-/Trend-Screener-Server). Quellcode/Git liegt bewusst außerhalb dieses Repos in Mein Minervini Trading-Journal-System/MinerviniMarkets360 (iCloud, eigenes venv) — scripts/sync_runtime.sh spiegelt den Code nach ~/Library/Application Support/Maick Minervini System/app, von wo aus auch der 8080-Server dieselbe Engine importiert (daher garantiert identische Ampel-Werte in beiden Apps). Läuft seit 2026-08-03 automatisch: lokale Git-Hooks (.git/hooks/post-commit/post-merge in diesem Repo) rufen das Skript nach jedem Commit/Pull auf, der src/, data/, scripts/, config.yaml oder requirements.txt betrifft. Grund: das manuelle „einmal ausführen" wurde wiederholt vergessen — zuletzt fehlten dadurch tagelang die Faktoren Follow-Through-Day/10-Jahres-Rendite in der Marktampel, obwohl der Code dafür längst committet war. Details zu Server/Ports/LaunchAgents siehe Abschnitt 4/5.

Läuft der Streamlit-Prozess nicht (Startseiten-Badge „Streamlit offline"), reicht ein Neustart: launchctl kickstart gui/$(id -u)/com.maick.markets360.dashboard. Anders als die Kern-Apps hat dieses Tool keinen eigenen Offline-/Cloud-Fallback für den Live-Scan selbst — es ist bewusst ein rechenintensives lokales Analyse-Werkzeug (voller Scan über hunderte Ticker), kein leichtgewichtiges Dashboard; der nächtliche Cloud-Scan (siehe oben) deckt die Geräteunabhängigkeit stattdessen über den Signal-Hub ab.

📈 Lokaler Trend-Screener (Trend-Screener/dashboard.html, Server-Port 8765)

Screening-/Watchlist-Zentrale nach Minervini, Darvas, Dan Zanger und Jesse Livermore — rein preis-/volumenbasiert, ohne bezahltes Datenabo (yfinance primär für Kurse, stooq-Fallback; Finnhub für Fundamentaldaten). Wählbare Universen (US-Auswahl, kompletter US-Markt, Europa, DAX/MDAX u.a.) liefern je Ticker ein RS-Ranking/VCP/Trend-Template-Ergebnis.

⚡ Code 33 (Minervini-Fundamentalfilter): mindestens drei aufeinanderfolgende Quartale mit beschleunigendem Wachstum bei EPS, Umsatz und Nettomarge gleichzeitig — eigenes Badge/Filter/CSV-Spalte (code33). 🏆 Top-Picks = charttechnisches Minervini-Setup und Code 33 gleichzeitig erfüllt — eigener Chip/Filter, die Schnittmenge aus Chart und Fundamentaldaten.

⚡ Hebel-Ampel (Filter „Hebel-Kandidaten (grün)", seit 2026-08-16): Reife-Ampel speziell für Hebelprodukte (Knock-Outs/Optionsscheine, z. B. über Finanzen.net Zero) — identische Kriterien wie im Daily Signal Hub und Price-Action-Hub (Markt-Ampel und Stage-2-Trend-Template grün, sonst sofort 🔴/kein Badge; zusätzlich enge Basis/VCP, Pivot-Trigger ARMED/BREAKOUT, kein Extended ≥25 % über SMA50, kein Earnings-Fenster, kein 50/80-Gap-Risiko, kein Klimax-Warnsignal → 🟢 „bereit", sonst bei grünem Markt/Trend 🟡 „im Fokus"). Der Trend-Screener berechnet dafür nichts neu, sondern lädt die bereits vorhandenen Faktoren per same-origin-Fetch aus Signal-Hub/data/signals.json und pivot.json (dritte, unabhängige Kopie derselben Logik — kein Cross-App-Import, siehe Entflechtungsprinzip) und gleicht sie über das Ticker-Symbol ab. Nur Ticker, die auch im Signal-Hub-Universum auftauchen, bekommen ein Badge/einen Chip-Zähler — bei den übrigen bleibt es leer statt eines falschen Werts.

🔍 Setup-Analyse-Verknüpfung (seit 2026-08-16): die aufgeklappte Detailzeile jedes Tickers verlinkt zusätzlich auf ../Signal-Hub/setup-detail.html?ticker=… — denselben Detail-Überblick wie bei Signal-Hub und Price-Action-Hub (Score-Faktoren, Pivot-Kennzahlen, Price-Action-Muster, TradingView-Chart, Fundamentaldaten & Story), statt einer eigenen, duplizierten Analyse-Ansicht. Einschränkung, seit 2026-09-10 abgemildert wie beim Price-Action-Hub: den vollen Detail-Überblick gibt es weiterhin nur für einen Eintrag in Signal-Hub/data/signals.json — für Ticker außerhalb dieses Universums (Erfahrungswert ~65–80 % Überschneidung, siehe Hebel-Ampel-Absatz oben) zeigt die Zielseite seither die 🔎 Direktanalyse (Live-Kurs, Chart, Fundamentaldaten) statt eines reinen Fehlers. Die Setup-Analyse selbst hat seit demselben Tag ein Eingabefeld zum Ticker-Wechsel direkt in der geöffneten Seite (siehe Signal-Hub-Abschnitt) — praktisch bei einem breiteren Ticker-Universum wie hier.

Zugriff & Aktualisieren: Läuft über den externen hub_server.py (Port 8765), same-origin über den 8090-Server erreichbar (/Trend-Screener/* wird durchgereicht — Dashboard, signals.js, /api/status, /api/links). Drei Update-Buttons im Dashboard (USA-Auswahl / USA gesamt / Europa) rufen /api/run?markets=… auf (der Proxy erzwingt eine feste Markt-Allowlist); /api/open (öffnet Dateien/Apps auf dem Mac) bleibt mit 403 gesperrt — Ausnahme /api/watchlist (POST, siehe unten), die ist erlaubt. Der Scan läuft via osascript (sonst kein TCC-iCloud-Zugriff im Subprozess) und nutzt einen lokalen Cache außerhalb iCloud (~/Library/Application Support/LokalerTrendScreener) — sonst OSError EDEADLK bei den tausenden Kurs-CSV-Zugriffen. Nur signals.js selbst liegt im iCloud-Ordner (Auslieferung + Pages-Snapshot).

☁️ Cloud-Nachtlauf (GitHub Actions, eigenes privates Repo Miksch52/lokaler-trend-screener): läuft Mo–Fr 23:08 Europe/Berlin (krumme Minute seit 2026-09-10, siehe Cron-Absatz beim Signal Hub), Ergebnis geht per rclone nach R2 — von dort liest der Daily Signal Hub es als eigene, unabhängige Quelle ein. „☁️ Cloud (überall)"-Button im Dashboard stößt denselben Workflow von jedem Gerät aus an.

📅 Handelstag belegt statt Schreibdatum (seit 2026-09-11). Die datierte Historie (trendscreener/history/) bekam bis dahin das Datum ihres Schreibzeitpunkts. Durch die Cron-Verspätung wurde der Nachtlauf meist erst nach Mitternacht fertig – 2026-09-09.json enthielt dann den Handelsschluss vom 08.09., an Tagen mit kleiner Verspätung aber den richtigen Tag. Ein manueller Testlauf am 10.09. um 18:44 (US-Markt offen) schrieb außerdem Intraday-Kurse als Tageshistorie. history_datum.py belegt den Tag jetzt vor dem Schreiben gegen Yahoos finale Schlusskurse (gleiches Verfahren wie bei Markets 360), ohne Beleg entfällt der Eintrag, eine vorhandene Tagesdatei wird nie überschrieben. Altbestand korrigiert (11.09.): 14 von 19 Dateien behalten (10 davon auf ihren tatsächlichen Handelstag umbenannt, Inhalt byte-identisch), 5 aussortiert – 4 Duplikate desselben Handelstags und der Intraday-Stand vom 10.09. Alle 15 US-Handelstage vom 20.08. bis 10.09. sind lückenlos belegt. Originale unter r2:signalhub-magazine/_archiv/history-vor-korrektur-2026-09-11/trendscreener/history/, Zuordnung je Datei in history/_korrektur_2026-09-11.json.
DatumÄnderungGrund
07-17Cloud-Nachtlauf eingeführt (nightly.yml, ohne --notify)Push übernimmt gebündelt der Signal-Hub
08-13Eigener Deploy-Trigger ergänztein manueller Cloud-Klick löste vorher keinen Pages-Deploy aus — frische Daten erschienen erst zufällig mit dem nächsten Signal-Hub-Zyklus (bis zu 6 Std. Verzögerung)
08-20Datierte Historie zusätzlich gesichert (history/JJJJ-MM-TT.json)„letzter Stand"-Pfad bleibt überschrieben, Historie wächst separat (gleiches Muster wie der OHLC-Tagessnapshot im Signal-Hub)
09-10Cron auf 23:08 + Sommerzeit-Gateder Nachtlauf lief jeden Werktag doppelt (beide Cron-Zeilen feuerten ganzjährig) – rund 100 verschenkte Actions-Minuten im Monat
09-11History nach belegtem Handelstag benannt, Altbestand korrigiertDateien trugen das Schreibdatum und waren meist um einen Handelstag verschoben; Intraday-Stand vom 10.09. aussortiert

🗒️ Watchlist erstellen (Tools-Leiste, seit 16.08.2026): schreibt die aktuell gefilterte Tabellenansicht (Suche, RS-Min, alle Toggle-Filter inkl. Hebel-Ampel) als zwei Dateien nach Watchlisten/Trend-Screener/ im MTS-Hauptordner (nicht im Trend-Screener-eigenen Output — bewusst zentral mit eigenem Unterordner, damit künftig auch andere Apps dort ablegen können):

  • eine TradingView-importierbare .txt — Symbole laufen durch dieselbe tvSymbol()-Zuordnung wie die TradingView-Links je Zeile (europäische Suffixe → Börsencode, z. B. AIXA.DE → XETR:AIXA), dasselbe Format, das export.py::write_watchlist im Trend-Screener-eigenen Output schon länger für die Pipeline-Ergebnisse schreibt;
  • eine gleichnamige _lesbar.txt zum schnellen Durchlesen ohne TradingView — eine Zeile pro Ticker mit RS/Kurs/M-Score/Flags/Sektor, dazu eine Kopfzeile mit Zeitstempel und einer Klartext-Zusammenfassung der aktiven Filter.

Dateiname enthält Datum, Herkunft und Markt (z. B. 2026-08-16_Trend-Screener_USA-Auswahl.txt); mehrere Exporte am selben Tag/Markt bekommen automatisch einen _2/_3-Suffix statt sich still zu überschreiben. Neuer Endpunkt POST /api/watchlist in hub_server.py (Validierung: Markt aus fester Allowlist, max. 3000 Symbole mit engem Ticker-Muster, Größenlimit für den freien lesbaren Text) — dafür musste der 8090-Proxy erstmals überhaupt POST-Requests an /Trend-Screener/* durchreichen (server_mts.py::do_POST kannte bis dahin nur /data und /Signal-Hub/api/*, jeder POST an den Trend-Screener lief ins Leere).

Fix 16.08.2026 — „📂 Ergebnisse"-Button öffnete den falschen (leeren) Ordner. links.jsons Eintrag {"target":"output"} wurde in /api/open relativ zu config.BASE_DIR aufgelöst (dem iCloud-Projektordner) — der echte Ergebnisordner ist aber config.OUTPUT_DIR, bewusst außerhalb iCloud (siehe Cache-Begründung oben). Der iCloud-seitige output/-Ordner existierte zwar (uralter Rest vom 24.06.), war aber nie der befüllte — der Button öffnete faktisch nichts Aktuelles. /api/open löst "output" jetzt als symbolischen Namen direkt auf config.OUTPUT_DIR auf. Bleibt trotzdem wirkungslos, wenn die Seite über den 8090-Proxy/Tailscale/Cloud geöffnet wird — das ist keine Regression, sondern der bewusste /api/open-403-Schutz (s. o.): der Button funktioniert nur direkt auf dem Mac mini unter localhost:8765, weil er lokal Finder öffnet — auf einem iPhone/iPad oder von einem anderen Gerät aus gäbe es ohnehin nichts sinnvoll zu öffnen.

📖 Betriebsanleitung (seit 2026-08-16): Header-Button „📖 Anleitung" verlinkt wie bei den anderen Apps eine dedizierte Betriebsanleitung als Claude-Artifact (Status-Badges, Strategien-Flags M/D/Z/L/V, M-Score-Formel, Filter-Referenz, Datenherkunft, Abschnitt „Trainingsphase" als zweite Meinung am Wochenende: Status SETUP/LEADER bestätigt, „nah dran" ist fürs Training kein Kandidat, Richt-Stopp −8 % gegen das Stop-Maximum lesen) — Trend-Screener-Anleitung. Eigenes, viertes Design (dunkle Navy-Basis + Masthead/Nav-Rail wie das gemeinsame Template von Price-Action-Hub/Rotation-Dashboard/Hauptapp, aber mit dem echten Blau-Akzent (#4493F8) des Dashboards selbst statt deren jeweiliger Akzentfarbe) — siehe CLAUDE.md für die vollständige Design-System-Übersicht. Diese Seite (und dieser Wiki-Abschnitt) werden bei jeder funktionalen Änderung am Tool mit aktualisiert.

Projektort: Liegt bewusst außerhalb dieses Repos/iCloud-Ordners (GPT/KI-Projekte/Projekt-Versuche/Lokaler-Trend-Screener, eigenes Git-Repo) — der 8090-Server reicht die App nur durch, entwickelt/dokumentiert wird sie separat. Der Trend-Screener/-Ordner in diesem Repo existiert deshalb nicht dauerhaft: cloudflare-pages/deploy.command kopiert bei jedem Deploy lediglich einen Snapshot (dashboard.html + signals.js) aus dem externen Ordner hinein, damit die Pages-Version offline etwas zum Anzeigen hat — Live-Aktualisieren funktioniert dort naturgemäß nicht (kein Mac-mini-Proxy erreichbar), nur der letzte Cloud-Nachtlauf-Stand.

🤖 TraderFox KI-Agenten (traderfox-ki-agenten.html)

Kuratierte Linksammlung zu den 7 wichtigsten externen KI-Research-Agenten von TraderFox/Trader-Zeitung (Signal-Agent, News-Signal-Agent, Screener-Agent) — jeweils mit Kurzbeschreibung, Kategorie-Badge und Kadenz (sofort bei Ereignis / stündlich / Mo–Fr / Di & Do / Mo & Do). Jede Karte verlinkt direkt auf den passenden Newsticker-Filter bei traderzeitung.de.

Bewusst reine Linkseite, kein eigener Datenabruf (Entflechtung): Die Agenten laufen komplett extern bei TraderFox, hier wird nichts gespiegelt oder gecacht. Voller Zugriff auf einzelne Artikel kann ein Trader-Zeitung-/TraderFox-Abo voraussetzen. Quelle: Weekly Briefing vom 06.08.2026 (Simon Betschinger); zusätzlich verlinkt die Seite auf die vollständige Agenten-Übersicht (traderzeitung.de/agents.html).

Statische Single-File-Seite ohne eigenen Server/Port — wird wie Coach/Wiki vom 8090-Server mitausgeliefert und ist auch auf der Cloudflare-Pages-Version direkt erreichbar.

📖 Betriebsanleitung (seit 2026-08-27): Link „📖 Anleitung" neben „← Zurück zum Hub" verlinkt eine dedizierte Anleitung als Claude-Artifact — TraderFox-Anleitung. Design-System und URL siehe CLAUDE.md.

🧮 ETF-Analyse (etf-analyse.html, seit 2026-09-09)

ISIN, Ticker oder Name eingeben, „Analysieren" klicken — die Seite ruft den Cloudflare-Worker-Endpunkt /etf-analyse auf, der zum Fonds passende echte Kennzahlen von Yahoo Finance holt (TER, Fondsvolumen, Top-Holdings, Länder-/Sektorgewichtung, Performance/Volatilität aus Yahoos fundPerformance-Modul) und daraus per Workers AI (llama-3.3-70b-instruct-fp8-fast, kostenlos) einen vollständigen Bericht nach dem Core-Satellite-Analyseschema erstellt: Profil/Strategie, Fondsqualität & Sicherheit, fundamentale Portfolioanalyse, Performance/Risiko, Ausschüttungsanalyse, ein Spezialmodul je ETF-Typ (Covered Call, Dividenden, Themen, …), Alternativen-Vergleich und ein Investitionsurteil mit Punktzahlen 0–10 je Kategorie, Ampelbewertung und Kaufen/Beobachten/Meiden. Dieselbe Seite erlaubt, mehrere analysierte ETFs in einer Vergleichsliste zu sammeln (TER/Volumen/Ausschüttungsrendite/Volatilität/Ø Score/Urteil nebeneinander, lokal in localStorage, kein Server-Sync).

Zweigeteilt (echte Zahlen vs. KI-Text) — bewusst gegen Halluzinationsrisiko: harte Kennzahlen (TER, Volumen, Holdings, Rendite/Volatilität) kommen unverändert aus kontext_roh direkt von Yahoo und werden ohne KI-Umweg angezeigt; die KI liefert nur Einordnung, Scores, Alternativen-Text und das Urteil, und wird im Prompt angewiesen, fehlende Felder ausdrücklich als „nicht verfügbar" zu kennzeichnen statt zu schätzen. Enthält drei fest hinterlegte Beispiel-Berichte (iShares Core MSCI World, Vanguard FTSE All-World High Dividend Yield, Global X Nasdaq 100 Covered Call — recherchierte Echtdaten Stand September 2026) zum Ausprobieren ohne Live-Abfrage, z. B. falls der Worker gerade nicht erreichbar ist.

Zwei getrennte KI-Aufrufe statt einem (Bugfix 2026-09-09): ein einzelner Aufruf mit allen 15 Ergebnisfeldern (Fließtext + Score-Objekt + Alternativen-/Gründe-Arrays gemischt) ließ das kleine Workers-AI-Modell reproduzierbar die komplexeren Array-/Objekt-Felder weglassen, trotz Skelett-Vorlage und ausreichend Tokenbudget. Aufgeteilt in einen reinen „Urteil"-Aufruf (Scores/Urteil/Alternativen/Gründe) und einen reinen „Text"-Aufruf (die sechs Fließtext-Abschnitte), parallel per Promise.all, füllt das Modell beide zuverlässig vollständig aus.

Bekannte Grenzen: Yahoos Fonds-Module (fundProfile/topHoldings/fundPerformance) sind für europäische UCITS-ETFs oft lückenhafter als für US-Fonds — je nach ETF können einzelne Kennzahlen fehlen oder (live gefunden, TER bei manchen Fonds fälschlich als 0 statt null von Yahoo selbst geliefert) unplausibel sein; im Zweifel bei justETF/dem Anbieter-Factsheet gegenprüfen. Sharpe/Beta/Alpha kommen, wenn vorhanden, aus Yahoos eigener (Morningstar-basierter) riskOverviewStatistics — echte benannte Felder, aber die genaue Risikofreisatz-Methodik ist nicht unabhängig geprüft, nur für 3/5/10 Jahre verfügbar (nicht für 1 Jahr). Alternativen-ETFs kommen aus dem Allgemeinwissen des Modells, nicht aus einer Live-Datenbank.

Statische Single-File-Seite ohne eigenen Server/Port — wie TraderFox KI-Agenten vom 8090-Server mitausgeliefert und auf der Cloudflare-Pages-Version direkt erreichbar (cloudflare-pages/deploy.command). Der KI-/Datenaufruf läuft über denselben Cloudflare Worker wie /story//tiefenanalyse//mehrjahr (siehe Server-&-Ports-Abschnitt) — dort neu als Endpunkt /etf-analyse, 30-Minuten-Edge-Cache je Suchbegriff.

🧭 Ruhestands-Fahrplan (Ruhestands-Fahrplan/ruhestand-fahrplan.html, seit 2026-08-13)

Persönliches Ruhestands-Cockpit, kein Trading-Tool: eigene Zahlen (Ausgaben/Einnahmen, Eigenheim, Mietwohnung, Depot & Liquidität, beliebig viele Lebens-/Rentenversicherungen — auch beitragsfrei gestellte mit Restguthaben) werden live zu Netto-Rentenlücke, Ziel-Liquiditätsreserve, Depot-Entnahmerate und einer Ist-vs-Ziel-Vermögensstruktur verrechnet. Dazu ein automatisch aus den eingetragenen Terminen erzeugter Fahrplan (Zinsbindungsenden, Versicherungsauszahlungen, Renteneintritt) und eine 23-Punkte-Maßnahmenliste mit Terminierung, Haken und Notizen.

Bewusst kein Cloud-/KI-Tool (seit 2026-08-15): Lief zunächst als Claude-Artifact, läuft jetzt komplett lokal wie Coach/Wiki — statisch vom 8090-Server mitausgeliefert, kein eigener Server/Port, keine Abhängigkeit von claude.ai mehr. Alle Daten bleiben ausschließlich im localStorage des jeweiligen Browsers, kein Server-Sync. Kein geräteübergreifender Datenabgleich (anders als der Coach): Mac mini und MacBook haben je einen eigenen Datenstand — Abgleich bei Bedarf manuell über „Export JSON" → „Import JSON". Bugfix 2026-08-30: war anfangs versehentlich nicht im Cloudflare-Pages-Deploy enthalten — die Startseiten-Kachel lief auf mts-hub.pages.dev (dem primären, mac-mini-unabhängigen Zugang) dadurch ins Leere (Pages' SPA-Fallback für den fehlenden Pfad sah wie ein kaputter Klick aus, kein echter 404). cloudflare-pages/deploy.command kopiert den Ordner jetzt mit, die App läuft dort exakt wie lokal (nur die Trading-System-Vorschlagsbox bleibt dort leer, da fetch("/data") mangels 8090-Server lautlos fehlschlägt).

Vorschläge aus dem Trading-System (nur lokal): Ruft beim Öffnen per fetch("/data") same-origin mts_data.json ab — funktioniert nur, wenn die Seite tatsächlich vom 8090-Server kommt — und zeigt Depotwert/Liquidität/Jahresausgaben aus dem Trading-System als Vorschlagsbox, aber nur solange das jeweilige Zielfeld noch leer ist. Nie automatisch übernommen: immer per explizitem Klick, mit deutlicher Warnung, dass das Trading-Depot überwiegend aktiv gehandeltes Momentum-Kapital ist (SEPA/VCP-Stil), keine automatische Rentenreserve.

📖 Anleitung: Header-Button verlinkt Ruhestands-Fahrplan/anleitung.html — Workflow, Symbol-/Kennzahlen-Legende, Geräte-Hinweis und Erweiterungsvorschläge, läuft ebenfalls komplett lokal ohne Claude-Artifact.

⚖️ Vermögensübersicht (Vermoegen/vermoegen.html, seit 2026-10-05)

Persönliche Gesamtvermögens-Bilanz, kein Trading-Tool. Löst die Numbers-Datei Haus-Unterstkoppel/Vermoegensuebersicht.numbers ab und hat dieselben Blätter: Übersicht (Nettowert, Bilanz als T-Konto Aktiva | Passiva mit Eigenkapital, Zusammensetzung, Kennzahlen, Prüfliste), Vermögenswerte, Verbindlichkeiten, Verlauf, Wohnen (Hausverkauf: kaufen, mieten oder behalten) und Ruhestand (Kapitalbedarf je Szenario, Dividenden-Anlage, Entnahmeplan).

Was sich selbst aktualisiert: Depots mit der Wertquelle „MTS-Depot“ übernehmen depots[].depotwert aus dem MTS-Gist (maick-trading-system.json). Die Seite fragt die Positionen bewusst nicht selbst ab, das macht nur die Hauptapp. Sind deren Kurse älter als 24 Stunden, erscheint in der Prüfliste ein Link zur Hauptapp. Posten mit „Yahoo-Symbol“ (Gold über GC=F, Krypto z. B. BTC-EUR) rechnen Menge × Live-Kurs über den Worker mts-cors, Fremdwährung wird in Euro umgerechnet. Mit „ISIN“ kommen Derivatkurse über /derivat-kurs. Kredite mit eingetragenem Sollzins schreiben ihre Restschuld vom Auszugsdatum Monat für Monat fort. Beim ersten Aufruf jedes Monats wird der Nettowert automatisch im Verlauf gespeichert, aber nur, wenn alle Live-Quellen geantwortet haben.

Wo die Zahlen liegen: Die HTML-Datei auf Pages enthält keine einzige Zahl. Alle Werte stehen in einem eigenen, geheimen Gist mit der Datei vermoegen.json. Gelesen und geschrieben wird mit dem Gist-Token gh_gist_token aus dem localStorage (derselbe wie bei Coach und Hauptapp, je Gerät einmal einzurichten). Die Seite findet den Gist selbst über den Dateinamen. Zusätzlich hält jedes Gerät eine Kopie für den Offline-Fall, Änderungen ohne Netz werden beim nächsten Aufruf nachgeschrieben. Mehrere Geräte überschreiben sich nicht: Jeder Posten trägt einen Zeitstempel, gelöschte einen Grabstein, und vor jedem Speichern wird mit dem Gist-Stand gemischt.

Geheim heißt nicht verschlüsselt. Ein geheimer Gist ist nicht öffentlich gelistet, aber jeder mit dem direkten Link kann ihn lesen. Den Link deshalb nicht weitergeben. Zum Zusammenspiel mit dem Ruhestands-Fahrplan: dort wird ausführlich geplant, hier wird der Ist-Stand geführt.

📖 Anleitung: Der Knopf oben rechts in der App öffnet die Betriebsanleitung als Claude-Artifact (Vermögensübersicht — Betriebsanleitung): Einrichtung je Gerät, Wertquellen und Symbole, Kredit-Fortschreibung, Verlauf, Pflege im Jahreslauf und alle Markierungen.

📡 13F-Radar (13F-Radar/13f-radar.html, seit 2026-09-20)

Wertet die 13F-HR-Meldungen von rund 90 großen Adressen bei der SEC aus — Hedgefonds, Makro-Häuser, Aktivisten, Endowments. Gemessen wird nicht der Bestandswert (der steigt schon durch Kursgewinne), sondern der bewusste Netto-Zukauf: Stückzahl-Veränderung gegenüber dem Vorquartal, bewertet zum Quartalsendkurs. Zehn Abschnitte: Melder-Zensus, Kapitalfluss (größte Zu-/Abflüsse), IPO-Falle, Narrativ-Cluster, Neuaufnahmen, Sperrfristen, Konsens-Alter, Konsens-Matrix, Portfolios je Haus, Methodik. Drei Sichten umschaltbar (Conviction / Macro / Alle), dazu ein Schalter „IPO-bereinigt" vs. „wie gemeldet".

Warum quartalsweise und nicht täglich: 13F-Meldungen gibt es vier Mal im Jahr, jeweils 45 Tage nach Quartalsende (14.02. / 15.05. / 14.08. / 14.11.) — häufiger gäbe es schlicht nichts Neues. Der Lauf ist trotzdem monatlich (.github/workflows/13f-radar.yml, am 18. jedes Monats plus Handstart), weil nach der Frist noch wochenlang Nachzügler und Änderungsmeldungen (13F-HR/A) eintrudeln und der SEC-Gesamtdatensatz für Abschnitt 01 erst rund vier Wochen später erscheint. Das Skript erkennt selbst, welches Quartal fällig ist, und committet nur, wenn sich etwas geändert hat; danach stößt es deploy.yml an.

Datenquellen — alle kostenlos, ohne Anmeldung: die Einreichungsliste je Melder (data.sec.gov/submissions/) und die Original-Informationstabelle jeder Meldung (www.sec.gov/Archives/) von SEC EDGAR; die Zuordnung CUSIP→Börsenkürzel von OpenFIGI (13F nennt nur CUSIP und Emittentenname, keinen Ticker) — einmal nachgeschlagene Kürzel landen in 13F-Radar/data/cusip_ticker.json und werden mitcommittet, ein Folgelauf muss fast nichts mehr fragen; für den Melder-Zensus in Abschnitt 01 der SEC-Strukturdatensatz „Form 13F Data Sets" (ein ZIP statt 9.000 Einzelabrufe). Keine bezahlte Datenquelle, kein Mac mini nötig — der Lauf findet komplett in GitHub Actions statt.

Was automatisch entsteht und was Handarbeit bleibt: Alle Zahlen sind automatisch — Bestände, Netto-Zukäufe, Neuaufnahmen, Konsens-Matrix, Portfolios, Zensus, dazu die Deckblattprüfung (jede Fondssumme gegen tableValueTotal der SEC) und die Quartalsnotizen („wer hat nicht gemeldet", „wer meldet in Tausend USD", „wo liegt eine Änderungsmeldung vor"). Nicht automatisch ist die Einordnung frischer Börsengänge: ob ein Zufluss ein echter Kauf ist oder nur eine durch den Börsengang sichtbar gewordene Altbeteiligung, lässt sich nur per 13G-Filings, Prospekt und Berichterstattung klären. Diese Ebene liegt je Quartal in 13F-Radar/data/kuratiert/JJJJ-Qn.json (IPO-Belege, Sperrfristen, Konsens-Fallstudie, Kommentartexte). Fehlt sie, bleibt die Seite trotzdem vollständig benutzbar — die betroffenen Abschnitte 03/06/07 blenden sich aus, und statt eines alten Kommentars steht eine automatisch aus den Zahlen erzeugte Zusammenfassung.

Das Mess-Universum: Die Conviction-Häuser (Stufe C in 13F-Radar/data/fonds.json) spannen mit allen ihren Positionen das Universum auf; Quant-, Makro- und Staatsfonds werden für genau diese Titel mitgemessen, definieren es aber nicht mit. Grund: ein Multi-Strategie-Haus meldet mehrere tausend Positionen, überwiegend Restposten aus Market-Making — ohne diese Grenze bestünde die Rangliste daraus. Die Portfolio-Karten in Abschnitt 09 zeigen zusätzlich die größten eigenen Positionen jedes Hauses. Aktienklassen derselben Firma (Alphabet A und C) werden über die ersten sechs Stellen der CUSIP zusammengefasst, der Netto-Zukauf aber je Gattung zu deren eigenem Kurs gerechnet.

Historie ist unveränderlich: Jeder Quartalsstand wird zusätzlich als 13F-Radar/data/history/JJJJ-Qn.json abgelegt und nie überschrieben — dieselbe Regel wie bei den OHLC-Snapshots. Die Seite selbst lädt 13F-Radar/data/13f.json (mit 13f.js als file://-Fallback); kommt keine Datei, zeigt sie einen erklärenden Kasten statt einer leeren Seite.

Statische Single-File-Seite ohne eigenen Server/Port — vom 8090-Server mitausgeliefert und auf der Cloudflare-Pages-Version direkt erreichbar (cloudflare-pages/deploy.command). Bewusst nicht in der Precache-Liste des Service Workers: eine Quartalsseite muss nicht offline vorgehalten werden.

Keine Anlageberatung. 13F-Meldungen sind rückwirkend und bis zu 45 Tage alt — die Positionen können längst aufgelöst sein. Die Seite ist ein Blick darauf, wo das große Geld war, kein Signalgeber.

04 Server & Ports

PortServerAufgabe
8090server_mts.pyLiefert den ganzen Projektordner aus (Startseite, Hauptapp, Coach, Wiki). Preis-Proxy (yfinance + onvista für dt. Derivate), Daten-Persistenz /data, /status; reicht /markt /sector-perf /fred an den 8080-Server durch (Markets 360 über HTTPS); /depot-tickers (seit 2026-08-23, CORS nur für localhost:8080) liefert Ticker je Depot fürs Breadth-Grid in Markets 360 — bewusst ohne Beträge/P&L. Datendateien werden nie gestreamt: .json und die Daten-.js aus den data/-Ordnern werden erst komplett gelesen, dann mit der tatsächlichen Content-Length gesendet (seit 2026-09-21 auch .js) — ist die Datei gerade nicht sauber lesbar (iCloud lädt sie nach, Pipeline schreibt), kommt ein ehrlicher 503 statt eines 200 mit zu wenig Bytes. Normale Skripte (assets/*.js, sw.js) bleiben auf dem schlanken Standardweg; top_setups.* und Price-Action-Hub/Rotation-Dashboard-Daten kommen ohnehin per Cloud-Proxy.
8080Minervini/server.pyMarkets 360 + Daten-Endpunkte /markt/<symbol>, /fred/<serie> (Key im macOS-Schlüsselbund minervini-fred-api; bewusst nicht in GitHub Secrets verschoben — nur der lokale 8080-Server/Makro-Tab braucht ihn, keine der Cloud-Pipelines ruft FRED auf; relevant erst, falls die Marktampel selbst mal cloud-fähig werden soll), /sector-perf (15-Min-Cache), /marktampel (ruft die minervini360-Engine des Markets360 Screeners direkt an, siehe Marktampel-Absatz oben), /breadth (seit 2026-08-23, S&P-500-/Euro-Stoxx-50-Breadth aus der per rclone automatisch aus R2 aktuell gehaltenen Nachtscan-CSV + Small-Cap-Proxy live per TradingView, 30-Min-Cache), /status
8091Signal-Hub/src/server.pySignal-Hub-Dashboard, Screening-Lauf (POST /api/run), Konfiguration, /api/status; liefert auch den Coach aus (Fernzugriff über einen Port). „Keine Daten“ im Dashboard trotz laufendem Server: iCloud hat Dateien in Signal-Hub/data/ ausgelagert („dataless“), der Hintergrundprozess darf sie nicht zurückladen und liefert 0 Bytes (Log: Errno 11). Der Server wiederholt das Lesen und fällt auf cat zurück; hilft das nicht: Dateien einmal interaktiv lesen (cat) bzw. den Ordner im Finder mit „Heruntergeladen behalten“ markieren. Die Cloud-Version auf Pages ist davon unberührt.
8501Markets360 Screener (Streamlit, Quellordner Mein Minervini Trading-Journal-System/MinerviniMarkets360 in iCloud, LaunchAgent com.maick.markets360.dashboard, seit 2026-07-22)Screening-Engine (VCP/RS-Rating/Fab-5) + dieselbe Marktampel-Engine (minervini360.scoring.market), die auch der 8080-Server direkt importiert — beide zeigen seit 2026-07 exakt denselben Ampel-Prozentwert. Seit 2026-07-16 zusätzlich: Markt-/Index-Auswahl in der Sidebar (S&P 500, Nasdaq-100, DAX, MDAX, EU-Kern) mit automatischer Wikipedia-Aktualisierung der Listen (>7 Tage), Zusatz-Indikatoren (RSI/MACD/ADX/Stochastik/Bollinger/OBV) mit Erläuterungen, Pivot-Abstands-Filter („nahe am Kaufpunkt"), Marktampel-Verlauf und Scan-Vergleich (Neu/Rausgefallen). Quellcode/Git: Mein Minervini Trading-Journal-System/MinerviniMarkets360 (iCloud); scripts/sync_runtime.sh spiegelt nach ~/Library/Application Support/Maick Minervini System/app, von dort importiert der 8080-Server die Marktampel-Engine — läuft seit 2026-08-03 automatisch per Git-Hook (post-commit/post-merge), siehe 8080-Abschnitt oben.
8765hub_server.py (Lokaler Trend-Screener, externer iCloud-Ordner)Trend-Screener-Dashboard + /api/*. Same-origin über 8090 erreichbar: der 8090-Server reicht /Trend-Screener/* durch (dashboard.html, signals.js, /api/status, /api/links). Aktualisieren läuft über /api/run?markets=us|us_all|europe — drei Buttons im Dashboard (USA-Auswahl / USA gesamt / Europa); der Proxy lässt /api/run nur mit dieser Markt-Allowlist durch. /api/open (öffnet Dateien/Apps auf dem Mac) bleibt mit 403 gesperrt — Ausnahme /api/watchlist (POST, „🗒️ Watchlist erstellen", siehe App-Abschnitt oben), die ist erlaubt. Der Screening-Lauf startet via osascript (nur so hat der Subprozess TCC-iCloud-Zugriff) und nutzt einen lokalen Cache außerhalb iCloud (~/Library/Application Support/LokalerTrendScreener) — sonst OSError EDEADLK bei den tausenden Kurs-CSV-Zugriffen. Nur signals.js bleibt im iCloud-Ordner (Auslieferung + Pages-Deploy).
8502StreamlitTrading Cockpit (optional, manuell gestartet)

05 Dauerbetrieb auf dem Mac mini

Alle Dienste laufen als LaunchAgents: Sie starten beim Login automatisch und arbeiten als Watchdog — alle 5 Minuten wird geprüft, ob der Server noch läuft; wenn nicht, wird er neu gestartet. Ein abgestürzter Server ist also spätestens nach 5 Minuten von selbst wieder da.

💻 Auch auf einem zweiten Mac einrichten (seit 2026-08-23): ~/Library/LaunchAgents liegt bewusst außerhalb von iCloud (macOS-Vorgabe) — auf einem frisch hinzugekommenen Mac (z. B. dem MacBook) fehlen die vier Server-LaunchAgents deshalb zunächst, obwohl der Code über iCloud längst da ist. de.maickschwillo.mts-server.plist und de.maickschwillo.minervini-dashboard.plist liegen dafür jetzt (analog zum bestehenden Signal-Hub-Muster) im Hauptrepo-Root, de.maickschwillo.markets360-screener.plist im MinerviniMarkets360-Repo, com.maick.trendscreener(.server).plist waren im Trend-Screener-Repo bereits vorhanden. „MacBook einrichten.command" (Doppelklick, Vorbild Signal-Hub/Automatik installieren.command) installiert auf einem neuen Mac in einem Rutsch: Python-Pakete fürs System-Python, ein eigenes venv für den Streamlit-Screener, die Laufzeitkopie-Synchronisation, die rclone-Binary und alle vier LaunchAgents. Nicht automatisierbar: ~/.config/rclone/rclone.conf (R2-Zugangsdaten) muss einmalig sicher vom Mac mini herüberkopiert werden (z. B. AirDrop) — sonst zieht sich /breadth auf dem neuen Mac keine frischen Nachtscan-Daten.

LaunchAgentDienst
de.maickschwillo.mts-serverMTS-Server (8090)
de.maickschwillo.minervini-dashboardMarkets-360-Server (8080)
com.maick.signalhub.serverSignal-Hub-Server (8091)
com.maick.trendscreener.serverTrend-Screener-Server (8765, KeepAlive; Plist im externen Trend-Screener-Ordner). Läuft mit /usr/bin/python3 (System 3.9, hat die Screener-Deps — wie der 8090-Server). Der osascript-Start nutzt kein & (osascript bleibt am Server hängen), damit KeepAlive nicht in ein Start-Race läuft. Muss einmalig nach ~/Library/LaunchAgents kopiert und per launchctl bootstrap geladen sein — sonst überlebt er keinen Neustart.
com.maick.trendscreenerTrend-Screener-Nachtlauf: Mo–Fr 23:00 Uhr, run.py --hub --markets us,europe -f --notify (schreibt signals.js, das Dashboard liest nur diese Datei). Plist im externen Trend-Screener-Ordner, seit 2026-07-06 installiert. Bewusst ohne us_all und ohne --force-fund — diese Kombination ließ den Lauf am 2026-07-06 wegen eines yfinance-Sqlite-Locks über 30 Min hängen (siehe Abschnitt 8). Seit 2026-07-17 zusätzlich als Cloud-Workflow ohne Mac mini: der bislang ungetrackte Ordner ist jetzt ein eigenes privates Repo Miksch52/lokaler-trend-screener mit .github/workflows/nightly.yml (Mo–Fr 23:08 statt 23:00 — krumme Minute seit 2026-09-10, siehe Cron-Absatz beim Signal Hub; bewusst nach hinten verschoben, damit der Lauf sicher nach US-Börsenschluss liegt, ohne --notify — Push übernimmt Signal-Hub gebündelt); Ergebnis geht per rclone nach r2:signalhub-magazine/trendscreener/.

🔁 Doppellauf-Fix 2026-09-10: Die zwei Cron-Zeilen (Sommer/Winter) feuerten beide das ganze Jahr über — GitHub-Cron kennt keine Zeitzonen —, der Nachtlauf lief also jeden Werktag doppelt (nachgewiesen an je zwei Läufen am 03./04./07./08./09.09.). In diesem privaten Repo zählt das doppelt auf das kontoweite Kontingent von 2.000 Actions-Minuten/Monat: bei ~10 Min Laufzeit rund 100 verschenkte Minuten monatlich. Derselbe Bug war in minervini-markets-360 schon am 2026-08-23 gefixt worden, hier war er noch offen. Jetzt derselbe gate-Job: er vergleicht github.event.schedule mit der aktuellen Berliner Zeitzone und überspringt den nicht passenden Trigger. Der manuelle Weg bleibt unberührt — bei workflow_dispatch (also auch beim „☁️ Cloud (überall)"-Button im Dashboard) läuft der Gate-Job gar nicht erst, und der Scan hängt per always() && (event != schedule || proceed) nicht am übersprungenen Job; live gegengeprüft.
com.maick.signalhub (deaktiviert 2026-09-12)War der lokale Push-Zeitplan (alle 15 Min, löste je einmal pro Slot Lauf + Push aus) — pushte entgegen der ursprünglichen Doku an denselben ntfy-Kanal wie die längst produktive Cloud-Pipeline, nicht an einen separaten Test-Kanal. Führte unbemerkt seit mind. 2026-07-31 zu doppelten/dreifachen Pushes (verschärft dadurch, dass die Plist zusätzlich fälschlich auf dem MacBook statt nur dem Mac mini lag). Trotz Dokumentationsstand „entfernt seit 08-10" blieb die Plist tatsächlich geladen (launchctl list zeigte ihn bei der Nachkontrolle am 12.09. weiterhin) — Signal-Hub/scripts/auf_cloud_umstellen.command hat ihn jetzt, nach vier belegten Worker-Cron-Läufen, endgültig per launchctl bootout + disable gestoppt und durch com.maick.cloudspiegel (siehe unten) ersetzt. Plist bleibt nur deaktiviert liegen, kein rm.
com.maick.cloudspiegelErsatz seit 2026-09-12 (StartInterval 1800 s = alle 30 Min, RunAtLoad, osascript-Wrapper wegen TCC wie die Server-Jobs), ruft nacheinander zwei Skripte auf. 1) Signal-Hub/scripts/cloud_spiegel.sh — reiner Lese-Job: spiegelt die in der Cloud berechneten Ausgaben aus R2 (_deploy/) für Signal-Hub, Price-Action-Hub und Rotation-Dashboard auf den Mac (rclone --update, überschreibt nie eine lokal neuere Datei, kein Löschen). 2) Signal-Hub/scripts/cloud_lexikon_sync.sh (ergänzt, da run.py::pipeline() das vorher bei jedem lokalen com.maick.signalhub-Lauf miterledigt hat) — zieht regime_logbuch.json frisch aus R2, trägt per minervini_lexikon.py --backfill fehlenden Marktkontext nach und schiebt die Lexikon-Datei zurück nach R2, damit die Minervini-Zitat-Karte in setup-detail.html (siehe Signal-Hub-Anleitung) weiter „von selbst" aktuell bleibt. Beide Schritte brauchen ein explizit gesetztes PATH (/usr/local/bin u. a. vorangestellt) — sonst scheitert npx wrangler unter der minimalen launchd-Umgebung mit env: node: No such file or directory, derselbe Fix wie in upload_magazine_to_r2.py::_mit_node_path (08-21, siehe Abschnitt 03). Logs: /tmp/cloudspiegel.log (beide Skripte) bzw. /tmp/cloudspiegel-launchagent.log (Fehler des LaunchAgents selbst). Rückbau: launchctl bootout gui/$(id -u)/com.maick.cloudspiegel && rm ~/Library/LaunchAgents/com.maick.cloudspiegel.plist, danach launchctl enable gui/$(id -u)/com.maick.signalhub && launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.maick.signalhub.plist.
com.maick.pivot-backtestAbgeschaltet seit 2026-10-02 (plist im Archiv): der RETRO-Walk-Forward läuft jetzt in der Cloud als Workflow retro-backtest.yml im Repo signal-hub (So 08:23 UTC, plus workflow_dispatch), unabhängig vom Mac mini. Der LaunchAgent fiel aus, sobald der Mac mini sonntags früh aus war, und lud bis 2026-10-02 nicht einmal hoch — der RETRO-Stand auf Pages stand vom 09.08. an still. Handlauf weiterhin möglich: Signal-Hub/scripts/weekly_backtest_upload.sh.
com.maick.markets360.nightlyMarkets360-Screener-Nachtlauf (seit 2026-07-16): täglich 05:45 Vollscan des gewählten Universums über die CLI (minervini360.cli screen --top 50, osascript-Wrapper wegen TCC, Log /tmp/markets360-nightly.log). Schreibt zeitgestempelte CSV und den stabilen Stand exports/markets360_latest.csv. Seit 2026-07-17 zusätzlich als Cloud-Workflow ohne Mac mini (siehe Markets-360-Karte oben) — dieser LaunchAgent läuft vorerst nur noch zur Kontrolle parallel. Plist direkt in ~/Library/LaunchAgents.
com.maick.markets360.dashboardMarkets360-Screener-Dashboard (8501, Streamlit, KeepAlive; seit 2026-07-22). osascript-Wrapper wegen TCC + lsof-Guard gegen Doppelstarts (wie Signal-Hub-/Trend-Screener-Server), Kommando PYTHONPATH=src .venv/bin/streamlit run src/minervini360/dashboard.py --server.headless true --server.port 8501 im iCloud-Projektordner Mein Minervini Trading-Journal-System/MinerviniMarkets360. Läuft auf dem jeweils dort ausgecheckten Branch (Stand 2026-07-22: main). Log /tmp/markets360-dashboard.log. Plist direkt in ~/Library/LaunchAgents. Vorher musste die App manuell per Terminal gestartet werden — daher „Streamlit offline" auf der Startseite, wenn sie nach einem Neustart nicht lief.
TCC-Besonderheit: launchd-Python darf iCloud-Dateien nicht direkt öffnen („Operation not permitted"). Alle Plists nutzen deshalb einen osascript-Wrapper, der den Server entkoppelt startet. Folge: launchctl bootout beendet den Server nicht — erst den Prozess am Port beenden.

Nützliche Kommandos (Terminal am Mac mini)

# Status aller Agents
launchctl list | grep -E "maick|signalhub|minervini|mts"

# Server neu starten (Beispiel Signal-Hub, Port anpassen)
kill $(lsof -tnP -iTCP:8091 -sTCP:LISTEN)
launchctl kickstart gui/$(id -u)/com.maick.signalhub.server

# Logs
tail -f /tmp/mts-server.log
tail -f /tmp/minervini-dashboard.log
tail -f /tmp/signalhub-server.log        # Server
tail -f /tmp/signalhub-scheduled.log     # Screening-Läufe/Push

Energie: sleep 0, per Netzwerk weckbar (womp 1), nur das Display geht aus. Den Mini nicht herunterfahren.

Zwei Regeln: ① Automatik installieren.command / Mac Mini Dauerbetrieb.command nie auf dem MacBook ausführen (sonst doppelte Pushes, und das MacBook verlernt den Ruhezustand). ② Keine gleichzeitigen Screening-Läufe von zwei Macs — Risiko von iCloud-Sync-Konflikten in data/signals.json. Anschauen/Lesen ist immer unkritisch.

HTTPS für die Offline-App (einmalig)

Die PWA (Abschnitt 2) braucht HTTPS. Das liefert tailscale serve mit gültigem Zertifikat — rein im Tailnet, nichts wird öffentlich (kein funnel).

  1. Einmalig in der Admin-Konsole: login.tailscale.com/admin/dns → HTTPS Certificates aktivieren (sonst hängt der nächste Schritt beim Zertifikat).
  2. Auf dem Mini: tailscale serve --bg --https=443 http://127.0.0.1:8090
  3. Prüfen: tailscale serve status → zeigt https://…ts.net/ → http://127.0.0.1:8090

--bg macht die Weiterleitung dauerhaft (übersteht Neustarts). Rückgängig: tailscale serve --https=443 off. Nach Code-Änderungen am 8090-Server diesen wie oben neu starten, damit die PWA-Verdrahtung (Manifest/Service Worker) aktuell ist.

06 Daten & Sicherung

WasWoSicherung
Trades, Portfolio, Einstellungenmts_data.json (geschrieben vom 8090-Server bei jedem Speichern) + privater GitHub-Gist (Cross-Device-Sync, seit Fix 2026-08-15 auf jedem Origin aktiv, siehe Abschnitt 2)① Rotierende Backups: data-backups/, die letzten 30 Stände, automatisch vor jedem Überschreiben · ② iCloud-Versionierung · ③ der Gist selbst (privat, GitHub-seitig). Nicht mehr in Git (private Trading-Daten gehören nicht ins Repo — seit 2026-06 in .gitignore; Quelle der Wahrheit sind die data-backups/). Backup-Wächter: Die Hauptapp prüft über /backup-status die jüngste Sicherung (Alter, Lesbarkeit, Trade-Zahl) und warnt in der Status-Leiste, falls das Backup > 7 Tage alt, leer oder unlesbar ist.
Signal-Hub-ErgebnisseSignal-Hub/data/signals.jsonWird bei jedem Lauf neu erzeugt — kein Backup nötig
Signal-Hub-Konfiguration (inkl. Token)Signal-Hub/config.jsoniCloud; bewusst nicht in Git (Secrets). Für die Cloud-Pipeline liegt eine angepasste Kopie (PDF-Ordner umgebogen, Depot-Abgleich aus) als GitHub-Secret SIGNALHUB_CONFIG_JSON vor.
Traderfox-Magazine (Kopie für Cloud-Pipeline)Cloudflare R2, Bucket signalhub-magazineAutomatischer Sync seit 2026-08-20 (scripts/upload_magazine_to_r2.py + LaunchAgent com.maick.signalhub.pdfsync, WatchPaths auf den iCloud-Ordner); Original bleibt der iCloud-Ordner für den lokalen Mini-Lauf
Coach-LernfortschrittGitHub-GistCloud-seitig
Mail-Passwort (IMAP)macOS-Schlüsselbund signal-hub-imap (pro Mac); für die Cloud-Pipeline zusätzlich als GitHub-Secret SIGNALHUB_IMAP_PASSWORD—
Konvention: mts_data.json wird in Git nur als bewusster, separater Snapshot committet („Daten: …"), nie vermischt mit Code-Änderungen. So kann ein Code-Rollback nie Trading-Daten zurückdrehen.

07 Wartung & Fehlerbehebung

SymptomUrsache / Lösung
Unklar, ob alle Daten aktuell sind · Push „MTS-Wächter: N Befund(e)"System-Wächter (seit 2026-09-17, agenten/system_waechter.py, rein lesend): prüft von außen, was die bestehenden Wächter innerhalb der Pipelines nicht sehen — Stand der Dateien auf mts-hub.pages.dev (Signal-Hub, Pivot, Top-Setups, Price-Action-Hub, Rotation-Dashboard, Trend-Screener) gegen den Worker-Cron-Zeitplan, letzte Workflow-Läufe (Signal-Hub-Pipeline, Deploy, Markets 360, Trend-Screener), Worker-Token, R2-History (Werktage Rückstand, Wochenend-Dateien = Regression Befund B10) und Forward-Logbücher. Läuft täglich in der Cloud (.github/workflows/waechter.yml, ~09:40, GitHub-Cron bewusst unabhängig vom Worker) und pusht nur bei Befund an den Signal-Hub-ntfy-Kanal. Selbst prüfen: python3 agenten/system_waechter.py (auf dem Mac mini mit --lokal zusätzlich die Server 8090/8091/8080/8765 und von iCloud ausgelagerte Projektdateien). Die iCloud-Prüfung allein (--icloud --push) läuft außerdem alle 30 Minuten im Cloud-Spiegel auf dem Mac mini und pusht nur, wenn sich die Liste ausgelagerter Dateien ändert — ausgelagerte Dateien kann kein Hintergrunddienst lesen (wochenlang unbemerkter Ausfall von Cloud-Spiegel und PDF-Sync bis 2026-10-02). Zurückholen geht nur im Terminal: find . -type f -flags +dataless -exec cat {} + >/dev/null, dauerhaft per Finder „Heruntergeladen behalten“. In Claude Code ordnet der Subagent system-waechter die Befunde anhand der bekannten Fehlermodi ein und schlägt den Fix vor, ohne selbst etwas zu ändern. Secrets im Repo maick-trading-system: NTFY_THEMA, optional WAECHTER_GH_TOKEN (nur „Actions: Read" auf die beiden privaten Screener-Repos — ohne ihn werden deren Läufe als „übersprungen" gemeldet, ihre Datenfrische wird trotzdem geprüft).
Startseiten-Badge zeigt „iCloud-Version"Zugehöriger Server läuft nicht → der Watchdog startet ihn binnen 5 Min selbst neu; sofort: launchctl kickstart gui/$(id -u)/<agent> (siehe Abschnitt 5)
Trend-Screener: „Connection refused (8765)"Der Hub-Server (8765) läuft nicht — der 8090-Proxy kann /Trend-Screener/* nicht durchreichen. Prüfen: lsof -iTCP:8765 -sTCP:LISTEN und launchctl list | grep trendscreener. Häufige Ursachen: (1) LaunchAgent nicht in ~/Library/LaunchAgents installiert → nach jedem Neustart weg; (2) Plist zeigt auf ein fehlendes Python (früher /usr/local/bin/python3 — Homebrew fehlt auf dem Mini). Fix: Plist auf /usr/bin/python3 und ohne & am Serverstart (sonst Start-Race → OSError: Resource deadlock avoided beim gleichzeitigen iCloud-Lesen von config.py), dann cp nach ~/Library/LaunchAgents + launchctl bootstrap gui/$(id -u) <plist>.
Trend-Screener zeigt tagelang denselben Stand (behoben 2026-07-06)Ursache war nicht der Server (der lief unauffällig und meldete auch bei veralteten Daten "running": true/false), sondern der Nachtlauf: die dokumentierte Automatisierung (com.maick.trendscreener.plist) war nie nach ~/Library/LaunchAgents installiert — launchctl list | grep trendscreener zeigte nur den Server-Agent, nicht den Scan-Agent. Zusätzlich hätte die alte Plist-Fassung ohnehin nicht funktioniert: sie rief python3 direkt statt über osascript auf (TCC-Problem, siehe Hinweis oben) und ohne --hub (schreibt dann nicht signals.js, das Dashboard bleibt trotzdem alt). Fix: Plist mit osascript-Wrapper + --hub --markets us,europe -f --notify neu gebaut, installiert und geladen (Zeile „com.maick.trendscreener" oben). Bei erneutem Auftreten: launchctl list | grep com.maick.trendscreener prüfen (muss zwei Zeilen zeigen, .server und ohne Suffix) und tail /tmp/trendscreener-scheduled.log.
Startseite: „Server nicht erreichbar", obwohl der Mini läuftMini und Gerät im selben WLAN? Von unterwegs: Tailscale aktiv? Die Seite findet den Server selbst, wenn sie als Datei geöffnet wurde — erreichbar muss er aber sein
Markets 360: Marktampel/Sektoren leerDaten kommen sonst per CORS-Proxy auch ohne Server → bleibt alles leer, ist die Internet-Verbindung weg oder die öffentlichen Yahoo-Proxies (allorigins/corsproxy) gerade gestört; einfach erneut laden
„🔄 Kurse" ohne Mac mini sehr langsam (Pages-/Dock-Version)Behoben 2026-09-22. Ohne erreichbaren Mac mini fragt _yahooJson() den eigenen Cloudflare-Worker (MTS_PROXY). Meldete der „Symbol unbekannt" (Yahoo-404 — passiert bei jeder Position mehrfach, weil fetchYahooPrice() nacheinander bis zu neun Börsen-Varianten wie .DE/.F/.MU durchprobiert), liefen danach trotzdem noch allorigins und corsproxy an — allorigins hängt dabei regelmäßig die vollen 10 s. Dazu wurden die Positionen ohne Mac mini strikt nacheinander abgefragt. Ergebnis: mehrere Minuten für einen Depot-Abruf (gemessen >45 s für eine einzige US-Aktie). Jetzt: eine klare Absage des Workers (4xx außer 429) gilt als endgültig, die fremden Proxys sind nur noch Notnagel, wenn der Worker nicht erreichbar ist oder 429/5xx meldet; über den Worker laufen wie beim Mac-mini-Weg 3 Positionen parallel. Gemessen: SAP, CrowdStrike, Celsius und Apple (per ISIN) zusammen in 2 s.
Derivate-/Optionsschein-Kurse fehlen oder zeigen den BasiswertKurse deutscher Derivate (Turbos/OS/Faktor-/Knock-Out-Zertifikate) holt der 8090-Server per ISIN über die onvista-API (Yahoo listet sie nicht; daher nie den Basiswert verwenden). Zeigt eine Position „—", war onvista kurz nicht erreichbar → erneut „🔄 Kurse". Der Abruf nutzt zuerst den lokalen Proxy (8090); ohne ihn bleiben reine Derivate „—". Falle 2026-09-21 — ohne Proxy bekam ein Turbo den Kurs seines Basiswerts. „Ohne Proxy bleiben Derivate ,—‘" galt nur in der Theorie: Scheitern ISIN- und WKN-Weg, fiel fetchYahooPrice() auf die namensbasierten Stufen zurück (TICKER_MAP-Treffer über guessTicker(name), zuletzt die Yahoo-Namenssuche). Der Produktname eines Scheins enthält aber immer den Basiswert — „Goldman Sachs OE Turbo OS Amazon.com, 153…" führte deshalb auf AMZ.DE, und 748 Turbos wurden mit 222,80 € statt 7,15 € bewertet: Depotwert 166.654 € statt 5.350 €, Gesamtkapital 452.791 € statt 286.646 €. Das falsche Symbol wurde zusätzlich als yahooSymbol gespeichert und hätte sich bei jedem Lauf reproduziert. Tritt nur ohne erreichbaren Mac mini auf (mit Proxy löst onvista die ISIN korrekt auf) — also genau auf der Pages-/Dock-Version. Verwandt mit dem schon bekannten „Basiswert HOOD"-Cache, der bisher nur nachträglich bereinigt wurde, statt die Auflösung zu verhindern. Fix: _istDerivat() (Sektor Derivate/Zertifikate bzw. Namensmuster Turbo/Optionsschein/Knock-Out/Faktor/Mini-Future/Discount/Bonus/Endlos) schaltet für solche Positionen alle namensbasierten Stufen ab — erlaubt bleiben nur ISIN, WKN und Proxy; findet keiner einen Kurs, bleibt die Position ehrlich ohne Kurs. Ein bereits gespeichertes Fremdsymbol wird nicht mehr verwendet, und beim nächsten „🔄 Kurse" werden Symbol und falscher Kurs verworfen (Selbstheilung) — sonst bliebe der zu hohe Wert stehen, weil ein ergebnisloser Abruf currentPrice nicht anfasst. Gleiche Klasse, gleicher Tag — Verkaufs-Check und ATR-Stop liefen ohne Mac mini ins Leere: beide holten ihre Kurshistorie ausschließlich über /history (Mac-mini-Endpunkt). Ohne erreichbaren Server meldete der Verkaufs-Check für jede Position „Keine Kursdaten", der ATR-Rechner „Kursabruf fehlgeschlagen" — die Funktionen wirkten kaputt, waren aber nur serverabhängig. Neuer gemeinsamer Helfer _historyCandles(): Mac mini zuerst (löst auch ISINs/Namen auf), sonst Yahoo-Chart-API über die bestehende Proxy-Kette (_yahooFetchCandles(), liefert dieselbe Kerzen-Objektform wie /history — _yahooFetchHistory() daneben gibt weiter flache Arrays für die SEPA-Analyse zurück). Gleiche Daten auf beiden Wegen, da /history seinerseits die Yahoo-Chart-API holt und nichts umrechnet. Für ISINs und Derivate liefert der Cloud-Weg bewusst nichts statt über den Namen auf den Basiswert zu raten (siehe Derivate-Falle oben). Nebenbei korrigiert: der ATR-Rechner fragte range=6m an — kein gültiger Yahoo-Bereich (6mo), was nur der Mac-mini-Server stillschweigend abfing. Belegt gegen AAPL (251 Kerzen, Signal „Trend intakt"), SAP.DE (253) und einen Turbo (korrekt keine Daten). Zuvor am selben Tag: Der Cloudflare-Worker hat den onvista-Abruf als eigenen Endpunkt GET /derivat-kurs?isin=… bekommen (Portierung von server_mts.py::_onvista_isin_price — beide Stellen bei Änderungen zusammen denken). Findet die Hauptapp für ein Hebelpapier über ISIN, WKN und Mac-mini-Proxy keinen Kurs, fragt sie diesen Endpunkt — damit bekommen Turbos und Optionsscheine auf jedem Gerät ihren echten Kurs, auch auf Pages/iPhone ohne laufenden Mac mini. Belegt gegen alle vier Hebelpapiere im Bestand (Amazon-Turbo 7,612 €, Steico-Faktor 0,175 €, Adobe-Turbo 0,63 €, Nemetschek-Call 2,42 €). api.onvista.de steht bewusst nicht in der Allowlist des generischen ?url=-Proxys — nur dieser Endpunkt mit ISIN-Prüfung erreicht onvista. Antwort 5 Minuten gecacht. Nur wenn auch onvista nichts liefert, bleibt die Position ohne Kurs.
Signal-Hub: keine Mail-SignaleSchlüsselbund-Eintrag fehlt auf dem Mac, der den Lauf ausführt → security add-generic-password -a "mschwillo@freenet.de" -s "signal-hub-imap" -U -w
Signal-Hub: keine PDF-Signalepypdf fehlt → /usr/bin/python3 -m pip install --user pypdf; Magazin-Ordner-Pfad in config.json prüfen
„Speichern" im Signal-Hub geht nicht (403)Token fehlt auf dem Gerät → Seite einmal mit ?token=… öffnen
Pushes kommen doppelt/dreifachBestätigter Fall, MacBook (2026-08-10): Push-Zeitplan lief auf zwei Macs parallel — Cloud-Pipeline UND lokaler com.maick.signalhub-LaunchAgent (eigentlich nur für den Mac mini gedacht, „Vergleichsphase" vom 2026-07 nie beendet) pushten beide zu denselben 4 Slots an denselben ntfy-Kanal, seit mindestens 2026-07-31 unbemerkt. Die Cloud-Pipeline selbst pusht sauber (geprüft in den GitHub-Actions-Logs: pro Slot genau einmal). Fix: launchctl bootout gui/$(id -u)/com.maick.signalhub UND die Plist dauerhaft entfernen (rm ~/Library/LaunchAgents/com.maick.signalhub.plist, sonst lädt sie beim nächsten Login neu) — auf jedem Mac außer dem, der ihn wirklich betreiben soll (aktuell: auf keinem mehr nötig, die Cloud-Pipeline deckt alle 4 Slots unabhängig von beiden Macs ab). Prüfen: launchctl list | grep com.maick.signalhub darf auf keinem Mac mehr eine Zeile ohne .server-Suffix zeigen.
iPhone: Seite „tot", nichts klickbarHTML-Datei statt HTTP-Adresse geöffnet → http://mac-mini-von-maick.local:8090/ verwenden
Markets 360: FRED/Makro oder Marktdaten veraltetDer Service-Worker (PWA) reicht /markt, /fred, /sector-perf jetzt immer frisch ans Netz (nie aus dem Cache). Falls nach einem Update noch alte Werte erscheinen: Seite neu laden (ggf. zweimal) bzw. die PWA einmal schließen und neu öffnen, damit der neue Service-Worker (Cache-Version wird seit Fix 2026-08-03 automatisch aus einem Hash über die Precache-Dateien berechnet, siehe Abschnitt 2) aktiv wird.
Signal-Hub „Aktualisieren" tut nichts / „Lauf läuft bereits"Ein früherer Lauf hing → das in-memory-Lock blieb gehalten. Behoben: server.py bricht einen Lauf nach RUN_TIMEOUT (10 Min) hart ab und gibt das Lock frei. Sofort-Abhilfe: 8091-Server neu starten (Port-Prozess killen, dann launchctl bootstrap gui/$(id -u) Signal-Hub/com.maick.signalhub.server.plist). Der Server-Job läuft jetzt mit KeepAlive — stirbt der Server, startet launchd ihn binnen ~10 s neu (zusätzlich 5-Min-Watchdog via StartInterval). Voraussetzung: Der Job muss in launchd geladen sein (einmalig per bootstrap bzw. „Automatik installieren.command"). Der Timeout killt jetzt die ganze Prozessgruppe (run.py + scorer.py), sonst hielt ein überlebendes Kind die Pipe offen und das Lock blieb hängen.
Signal-Hub aktualisiert nicht automatisch (kein Morgen-/Abend-Push)Seit 2026-09-12 kein lokaler Job mehr dafür zuständig — der frühere Scheduler com.maick.signalhub ist auf dem Mac mini endgültig deaktiviert (war Ursache doppelter Pushes, siehe oben; die Plist blieb entgegen dem alten Dokumentationsstand „entfernt seit 08-10" bis zur Nachkontrolle am 12.09. tatsächlich geladen), Push kommt ausschließlich aus der Cloud-Pipeline (Worker-Cron, siehe Signal-Hub-Abschnitt). Prüfen bei Ausbleiben: gh run list --repo Miksch52/signal-hub --workflow=pipeline.yml --limit 5 (lief der Slot? Status failure?) statt lokal nach Plist/Log zu suchen.
Markt-Chip im Cockpit fehlt8080-Server nicht erreichbar — Chip blendet sich dann bewusst aus
Kein Zugriff von unterwegsTailscale auf iPhone und Mini aktiv? Mini wach? (Display darf aus sein)
MTS Hub startet auf dem iPhone nicht, wenn der Mac mini offline istHäufigste Ursache: Das Home-Bildschirm-Icon zeigt auf eine Mac-mini-Adresse (Tailscale-.ts.net, .local oder lokale IP) statt auf https://mts-hub.pages.dev/. Diese Adressen sind reine Weiterleitungen zum Mini ohne eigenen Service-Worker-Cache — ist der Mini offline, gibt es dort nichts zu laden (kein Bug, erwartetes Verhalten). Fix: altes Icon löschen, in Safari https://mts-hub.pages.dev/ öffnen, Teilen → „Zum Home-Bildschirm", einmal online starten (füllt den Precache), siehe Abschnitt 2.
Hub startet offline, aber Apps öffnen sich nicht (springt immer zurück zum Hub)Behoben (Fix 2026-07-05b, Abschnitt 2): target="_blank" auf den App-Karten funktioniert nicht in der installierten Standalone-PWA (nur ein Fenster) — iOS brach die Navigation ab. Falls das Verhalten nach einem Update wieder auftritt: neuen Deploy-Stand prüfen (cloudflare-pages/deploy.command erneut ausführen) und die PWA einmal online neu öffnen, damit index.html aktualisiert wird.
„☁️ Cloud-Scan starten"/„🔄 Aktualisieren" meldet „Fehler beim Starten (404)"Fix 2026-08-14 (signal-hub-Repo): Ursache ist praktisch immer der gh_actions_token auf genau diesem Gerät (localStorage, pro Gerät separat eingerichtet, siehe Abschnitt 2) — fehlt er, ist er abgelaufen, oder fehlt ihm „Actions: Read and write" auf signal-hub. GitHub meldet fehlende Repo-Berechtigung eines Fine-grained-PATs bei workflow_dispatch bewusst als 404 statt 403 (verschleiert damit, ob das Repo existiert) — triggerCloudWorkflow() behandelt seither auch 404 wie 401/403: der kaputte Token wird automatisch aus localStorage gelöscht, beim nächsten Klick fragt die App neu danach. Funktioniert der Scan auf einem anderen Gerät (z. B. MacBook) einwandfrei, bestätigt das die Diagnose. Fix: neuen Token unter github.com/settings/personal-access-tokens/new erstellen, siehe Einrichtungs-Hinweis in Abschnitt 2.
Hauptapp ohne Minervini-50/80-Badges; Konsole meldet net::ERR_CONTENT_LENGTH_MISMATCH bzw. „Failed to fetch" für signals.jsonBehoben 2026-08-28 (server_mts.py): Der 8090-Server lieferte grosse .json-Datendateien bisher ueber den Standardweg von Pythons SimpleHTTPRequestHandler aus — der nimmt Content-Length aus fstat() und streamt die Datei erst danach. Aendert sich die Datei in diesem Moment noch (der Signal-Hub-Scorer schreibt signals.json mit open(..., 'w') in-place ueber mehrere hundert Millisekunden) oder scheitert das Lesen (iCloud hatte die Datei ausgelagert und laedt sie gerade nach), sind die Header laengst raus: Der Browser bekommt ein 200 mit zu wenig Bytes und bricht mit ERR_CONTENT_LENGTH_MISMATCH ab. Betroffen war praktisch nur signals.json (~4 MB); pivot.json (99 kB) und top_setups.json (6 kB) haben ein entsprechend winziges Zeitfenster und fielen nie auf. Folge in der Hauptapp: _mtsLoadSignalsCache() scheiterte still — keine 50/80-Badges (window._mtsGap80Map blieb leer), und im Signal-Fingerabdruck fehlten tier sowie der signals-Rueckfall fuer Werte ausserhalb der Top-Setups. Fix: .json-Dateien werden jetzt zuerst vollstaendig in den Speicher gelesen und erst dann gesendet (_serve_json_snapshot()) — Content-Length stammt immer aus genau den Bytes, die auch rausgehen. Ein offensichtlich abgeschnittener Inhalt wird bis zu dreimal neu gelesen; klappt es dann immer noch nicht, kommt ein ehrlicher 503 mit Retry-After statt eines kaputten 200. Gzip ab 50 kB bleibt unveraendert. Tritt das Symptom erneut auf, ist es also nicht mehr dieser Fehler — dann meldet der Server sichtbar 503. Zweiter Teil des Fixes, an der Quelle (signal-hub-Repo): scorer.py schrieb seine Ausgaben mit open(pfad, "w") — das kürzt die Datei sofort auf 0 Bytes und füllt sie erst über die folgenden Millisekunden bis Sekunden wieder auf, wodurch jeder gleichzeitige Leser eine halbe Datei sieht. Alle neun Schreibstellen laufen jetzt über pfade.schreibe_atomar()/schreibe_json_atomar(): erst in eine Nebendatei schreiben, dann per os.replace() an den Platz schieben — ein Leser sieht entweder komplett den alten oder komplett den neuen Stand, nie etwas dazwischen (dasselbe Muster, das server.py schon beim Speichern der config.json nutzt). Das schützt zusätzlich die Cache-/State-Dateien unter ~/Library/Application Support/SignalHub: bricht ein Lauf mittendrin ab (Cloud-Runner-Timeout, Ctrl+C), blieb dort bisher eine unlesbare Datei zurück, die beim nächsten Lauf still als leerer Cache durchging. Gemessen unter Dauer-Rewrite, je 30 Abrufe: vorher 27 kaputt, nur mit dem Server-Fix 0 kaputt (aber 6 ehrliche 503), mit beiden Fixes 30/30 vollständig.
Top-Setups (Startseite) und/oder Trend-Screener zeigen tagelang denselben Stand, obwohl kein Fehler-Push kamBestätigter Fall (2026-08-27): Zwei unabhängige Ursachen gleichzeitig. (1) Der geplante Cron-Trigger der Signal-Hub-Cloud-Pipeline (Miksch52/signal-hub, pipeline.yml) blieb komplett aus — keine Fehlermeldung, einfach über ~15 Std. keine einzige Ausführung, obwohl der Workflow laut gh workflow list --repo Miksch52/signal-hub als active geführt wurde. Ein manueller Anstoß (gh workflow run pipeline.yml --repo Miksch52/signal-hub, entspricht dem „☁️ Cloud-Scan starten"-Button) lief sofort und fehlerfrei durch — das ist keine Fehlkonfiguration im Repo, sondern eine GitHub-seitige Verzögerung/Störung bei der Zustellung geplanter Workflows (von GitHub selbst dokumentiertes, aber seltenes Verhalten). Diagnose: gh run list --repo Miksch52/signal-hub --workflow=pipeline.yml --limit 20 — klafft dort eine mehrstündige Lücke ohne jeden Lauf (auch keinen fehlgeschlagenen), ist das die Ursache. (2) Unabhängig davon war kurz zuvor der DEPLOY_TRIGGER_TOKEN im lokaler-trend-screener-Repo abgelaufen (401 beim automatischen Anstoßen von deploy.yml nach dem Nachtlauf — genau die Meldung, die als GitHub-Actions-E-Mail „Trend-Screener Nachtlauf: All jobs have failed" ankommt); der eigentliche Screening-Teil lief dabei trotzdem durch, nur der Deploy-Anstoß fehlte. Token war zum Zeitpunkt der Prüfung bereits erneuert. Sofort-Check bei Verdacht auf Stillstand: eingebetteten Zeitstempel live vergleichen — curl -s https://mts-hub.pages.dev/Signal-Hub/data/top_setups.js | grep -o '"erstellt":"[^"]*"' (Top-Setups) bzw. .../Trend-Screener/signals.js, Feld "generated". Fix bei ausgebliebenem Cron: einfach „☁️ Cloud-Scan starten" drücken — läuft in ~15–20 Min. komplett durch (Signal-Hub → Price-Action-Hub/Rotation-Dashboard parallel → Deploy) und aktualisiert alle drei Anzeigen.

08 Tests

  • tests/test-hub-token.sh — prüft Token/Cookie/CORS-Logik des Signal-Hub-Servers (nur GETs bzw. nebenwirkungsfreie POSTs)
  • tests/test_pivot.py — Selbsttest des Pivot-Armed-Detektors (synthetische Charts → erwartete Klassifikation; kein Netz, keine POSTs)
  • tests/ — Playwright-Setup für Browser-Tests der Apps
  • tests/monats-ema-parity.js + tests/monats-ema-referenz.py — prüft, dass der 10-Monats-EMA-Marktfilter in allen drei Fassungen (scorer.py, assets/marktampel.js, minervini360.scoring.market) bitgenau dasselbe rechnet. Erst den Python-Teil laufen lassen (holt die Rohdaten einmal und legt die Referenz ab), dann den JS-Teil. Nach jeder Änderung an einer der drei Fassungen ausführen — genau diese Konstellation ist im System schon einmal auseinandergelaufen (Push zeigte andere Distribution Days als das Dashboard, 08/2026).
  • tests/test_checklisten.py — die Druck-Zettel unter Checklisten/ führen exakt dieselben Auswahllisten wie die Hauptapp (Verfassung, Setup-Typ, Muster, Verkaufsgrund, Fehler, Prozessnote, Stop-Moment). Läuft ohne Netz.
  • tests/theme-audit.js — Farbkontraste in allen Theme-Zuständen (siehe Abschnitt 3)
  • tests/smoke-audit.js — Alltagstauglichkeit: JS-Fehler, iPhone-Überlauf, Tap-Targets, tote Links
Eiserne Regel: Browser-Tests gegen die Live-Server dürfen keine POSTs absetzen — MTS.save() würde die echte mts_data.json überschreiben. In Playwright immer alle POST-Requests blocken.
📱 Alltagstauglichkeits-Prüfung (tests/smoke-audit.js, seit 2026-08-24). Zweites Prüfskript neben dem Kontrast-Audit, mit einer anderen Frage: läuft das überhaupt fehlerfrei, und ist es am iPhone bedienbar? Lädt alle 14 Seiten (inkl. Unterseiten wie Setup-Analyse und Kauf-Review) bei 375 px und 1280 px und meldet JS-Laufzeitfehler, horizontales Überlaufen, Tap-Targets unter 44 px und tote interne Links. Offline-bedingtes Rauschen (Fetch/CORS über file://, TradingView-Widget) ist bewusst herausgefiltert, ebenso der Trend-Screener/-Link (liegt außerhalb des Repos und wird erst beim Deploy aus R2 ergänzt — live vorhanden, im Repo erwartbar „fehlend").

Die Erstprüfung fand vier echte Fehler, die visuell nicht auffielen — alle sofort behoben: (1) Minervini Coach war am iPhone unbedienbar — der fixierte „🏠 Start"-Chip (z-index:2147483000) lag exakt über dem Menü-Knopf (z-index:200), die Seitenleiste und damit die gesamte Navigation zwischen den Coach-Seiten war nicht erreichbar; zusätzlich lief die Seite 416 px über den rechten Rand, weil .main als Flex-Kind den Vorgabewert min-width:auto hatte und nicht unter seine Inhaltsbreite schrumpfen konnte (body{overflow-x:hidden} verbarg das optisch). (2) Die Setup-Liste auf der Startseite lief 200 px über den Rand — Ticker, Name, Badges und Kennzahlen nebeneinander brauchen ~575 px, alle mit white-space:nowrap; Score, Price-Action und Konfluenz waren am Handy schlicht abgeschnitten, ausgerechnet in der Tagesliste, die morgens am Handy gelesen wird. Jetzt bricht die Zeile unter 560 px zweizeilig um. (3) Der Hauptapp-Titel war im Hellmodus fast unlesbar (Kontrast 2,1:1) — der Header ist in beiden Themes ein dunkler Petrol-Verlauf, der Titel erbte aber die themeabhängige Textfarbe und stand hell-auf-hell; jetzt fester heller Textton. (4) Dem Ruhestands-Fahrplan fehlte als einziger App das viewport-Meta — iOS Safari rendert solche Seiten mit einem virtuellen 980-px-Viewport und zoomt herunter, Text wird winzig und jede Media-Query rechnet gegen die falsche Breite.

Ergebnis nach den Fixes: 0 JS-Fehler, 0 tote Links, 0 Seiten mit iPhone-Überlauf über alle 14 Seiten. Aufruf: node tests/smoke-audit.js [Filter] [--mobil-only], Bericht unter tests/test-results/smoke-audit.json. Bei Layout-Änderungen an einer App immer mitlaufen lassen.
📲 „Erster Bildschirm"-Umbau von Startseite und Hauptapp (2026-08-24). Aus derselben Prüfung, aber keine Fehler, sondern Reibung im Alltag: In beiden Apps stand das Wichtigste unterhalb des ersten iPhone-Bildschirms (812 px). Drei Änderungen, jeweils gemessen statt geschätzt:

(1) Doppelte Marktwarnung auf der Startseite entfernt. Die Tendenz-Warnung („⚠️ Tendenz Richtung Gelb…") stand wörtlich identisch im Hero und im „Heute wichtig"-Block, keine 200 px auseinander — dazu der Marktzustand ein zweites Mal in Textform. Wenn zwei Elemente dasselbe sagen, liest man am Ende keines von beiden. Jetzt klare Aufgabenteilung: der Hero beantwortet „wie ist der Markt?" (Ampel, Aktienquote, Tendenz — visuell, mit Farbe), „Heute wichtig" beantwortet „was liegt heute konkret an?" (handelbare Setups, Kandidatenzahl). Dafür stellt renderTopSetups() jetzt window.__setups bereit und ruft renderToday() nach dem asynchronen Nachladen erneut auf.

(2) Startseiten-Kopf am Handy gestrafft. Gespart wurde ausschließlich an Zierrat: Eyebrow (wiederholt nur den App-Namen), Begrüßungssatz „Dein Cockpit läuft." und das Stier-Hintergrundbild (kostete hinter dem Text Lesbarkeit, ohne etwas auszusagen) sind unter 560 px ausgeblendet; Marktampel, Aktienquote, Tendenz und Zyklus-Chip bleiben vollständig. Zusätzlich ist die Badge-Legende jetzt einklappbar — dasselbe <details>-Muster wie in Signal-Hub und Price-Action-Hub — statt täglich 102 px Erklärtext vor der ersten Zeile. Ergebnis: erste Setup-Zeile von 920 px auf 805 px, damit im ersten Bildschirm.

(3) Hauptapp-Kopf am Handy gestrafft. Titelblock und die untereinander gestapelten Tabs (.nav-tabs{flex-direction:column}) füllten fast einen kompletten Bildschirm — ausgerechnet das Cockpit mit dem Radial-Tacho war dadurch hinter einem Scroll versteckt. Tabs jetzt horizontal wischbar (eine Zeile statt sechs), Untertitel unter 768 px ausgeblendet, Titel kleiner als Anker erhalten. Cockpit von 769 px auf 245 px, der Tacho selbst ist ab 415 px sichtbar.
CSS-Fallstrick, zweimal aufgetreten: Beide Media-Queries mussten hinter die Regeln verschoben werden, die sie überschreiben sollen. Bei gleicher Spezifität gewinnt die spätere Regel — eine @media-Block weiter oben im Stylesheet wird von einer normalen Klassenregel weiter unten wirkungslos gemacht (betraf .regime-zyklus, .topsetups, .ts-head). Fiel nur auf, weil nach jeder Änderung nachgemessen wurde statt sich auf „steht doch da" zu verlassen.
🎯 Setup-Liste: Kohorten-Werte einmal oben statt in jeder Zeile (2026-08-24). Nutzerfrage nach dem Umbau oben: „Warum haben die Top-Setups alle die gleichen Werte?" — Antwort: weil es Kohorten-Werte sind, keine Einzelwerte. „📊 49,7 % · n=1.281" ist die Trefferquote aller ARMED-Signale im Forward-Test, nicht die dieser Aktie; top_setups.py::_backtest_info schlägt sie über den Pivot-Status nach. Fachlich ist das richtig so (für einen einzelnen Titel gäbe es gar keine belastbare Stichprobe), aber untereinander in einer Liste sah die Zahl aus wie ein Wert pro Zeile, obwohl sie zwischen den Zeilen nichts unterscheidet. Dasselbe galt für das Status-Badge, das die Aufnahmebedingung ins Panel ohnehin schon verlangt, und für „🎯 1/1": von fünf Konfluenz-Kriterien sind drei Aufnahmebedingungen (immer erfüllt) und der Rotation-Leader zählt nur bei eigenem Forward-Test-Beleg — praktisch bleibt oft ein einziges zählbares Kriterium übrig. Von vier Badges pro Zeile unterschieden damit drei die Zeilen gar nicht.

Neue Regel: Ein Badge erscheint pro Zeile nur, wenn es die Zeilen unterscheidet. Die Hilfsfunktion gemeinsamkeiten() in renderTopSetups() prüft je Rendering, ob Pivot-Status und Backtest-Kohorte über alle angezeigten Setups identisch sind; wenn ja, wandern sie in die schon vorhandene Bedingungs-Zeile (#ts-sub, wo der Status als Aufnahmebedingung ohnehin hingehört) — „Score ≥ 70 · PA bullisch · 9× ARMED · Kohorte 49,7 % (n=1.281, nicht belegt)". Sind die Setups gemischt (z. B. ARMED und BREAKOUT nebeneinander), bleiben die Badges in den Zeilen, weil sie dann echte Information tragen. Der Konfluenz-Score erscheint nur noch bei Defizit — ein voller Score ist der Normalfall und braucht kein Abzeichen, ein fehlendes Kriterium ist die eigentliche Nachricht (im Test sticht genau der eine Titel mit „🎯 0/1" heraus).

Die Zeile zeigt jetzt, was sie unterscheidet: Ticker, Name und Score · PA · Q (Signal-Hub-Score, Price-Action-Score, Qualitäts-Perzentil) — die Spreizung ist real (Score 70–78, Q 68–100) und war vorher die am wenigsten prominente Information. Die Legende wanderte im selben Zug unter die Liste: man schlägt eine Zeichenerklärung nach, nachdem man ein Zeichen nicht versteht, nicht vorher — das brachte die erste Setup-Zeile von 857 px auf 790 px, also zurück in den ersten iPhone-Bildschirm, obwohl die Kohorten-Information vollständig erhalten blieb.

Übertragung auf die anderen Apps — erst gemessen, dann umgebaut. Statt das Muster überall anzuwenden, wurde für jede Listen-App gezählt, in wie vielen Elementen derselbe Badge-Text vorkommt. Nur ein weiterer klarer Fall kam heraus:
Apphäufigster BadgeKonsequenz
Price-Action-Hub100 % („Bullisch", 91/91)umgebaut
Signal-Hub85 % („🟡 Hebel im Fokus")unverändert — echte Varianz
Rotation-Dashboard8 % („8/8")unverändert — keine Redundanz
Backtest-Vergleich100 % („Pivot")unverändert — siehe unten
Im Price-Action-Hub trug das Einstufungs-Badge in allen 91 Karten denselben Text „Bullisch" — es wiederholte nur die Standardfiltereinstellung „📈 Nur bullisch", die oben ohnehin sichtbar ist. Jetzt steht es einmal in der Kopfzeile („91 von 914 Treffern · alle bullisch · Stand …"); schaltet man auf „Richtung: Alle" um, erscheinen die Badges automatisch wieder, weil sie dann unterscheiden (beide Zustände gegen echte Daten verifiziert: 91 Karten/0 Badges bzw. 100 Karten/100 Badges mit „Bullisch" und „Bärisch" gemischt).

Beim Backtest-Vergleich steht zwar auch in jeder Zeile „Pivot", das ist aber kein Designfehler, sondern der aktuelle Datenstand — die übrigen Engines haben noch keine gereiften Forward-Test-Kohorten. Sobald sie reifen, wird die Spalte von selbst informativ; in einer Tabelle mit Spaltenkopf ist ein wiederholter Wert außerdem normal, und der Quellen-Vergleich ist der Daseinszweck dieser Seite. Signal-Hub blieb ebenfalls unangetastet: dort variieren die Badges gut (höchster Wert 85 %), sie tragen also echte Information statt Rauschen.

09 Pflege dieses Wikis

Dieses Wiki ist Teil des Repos und wird bei jeder Systemänderung im selben Commit mitgepflegt (Regel in CLAUDE.md verankert — gilt damit für jede Claude-Code-Session). Das „Stand"-Datum oben rechts kommt automatisch vom Dateidatum, die Server-Status-Chips in Abschnitt 1 sind live. Da die Datei in iCloud liegt und über Port 8090 ausgeliefert wird, ist immer und überall die aktuelle Fassung sichtbar.

📝 Doku-Check nach jedem Commit (seit 2026-09-18, agenten/doku_check.py + Claude-Code-Subagent doku-pfleger): Ein Hook in .claude/settings.json prüft nach jedem git commit aus Claude Code, welche Doku zu den geänderten Dateien gehört — Wiki-Abschnitt (falls wiki.html nicht im selben Commit steckt; bei den nested Repos zählt eine offene oder bis zu 30 Minuten alte Wiki-Änderung im Root-Repo), Anleitungs-Artifact der App und CLAUDE.md bei Workflows, Servern, Worker oder Deploy. Befunde gehen als Hinweis an Claude, der doku-pfleger zieht dann wiki.html/CLAUDE.md nach — bestehende Aussagen ersetzen statt anhängen, Bugfix-Geschichten gehören in die Commit-Message — und nennt die neu zu veröffentlichenden Anleitungen. Committet wird nie automatisch. Tote interne Links prüfen: python3 agenten/doku_check.py --anker.

Multi-Device-Entwicklung: Mac mini + MacBook gleichwertig

Das Repo ist normales Git und liegt in iCloud — Mac mini und MacBook sind damit gleichwertige Entwicklungsumgebungen für Claude Code, sobald git/gh dort eingerichtet sind. Reines Bearbeiten/Committen braucht nur die normale Git-Anmeldung (war auf beiden Rechnern schon vorher aktiv). Für administrative gh-Aktionen (Secrets setzen, Workflows manuell per CLI auslösen mit gh workflow run) ist zusätzlich einmalig pro Rechner gh auth login nötig — auf dem MacBook seit 2026-07-18 eingerichtet (gh per brew install gh installiert, HTTPS-Protokoll, Scopes gist/read:org/repo/workflow). Das ist kein Bug, sondern normales, pro Gerät getrenntes GitHub-CLI-Verhalten.

Damit das so bleibt: keine Mac-mini-spezifischen absoluten Pfade hardcoden — immer os.path.expanduser("~/…") (Vorbild: Signal-Hub/src/pfade.py::LOKAL_MARKETS360), nie einen festen Benutzernamen-Pfad.