//+------------------------------------------------------------------+
//| SR_Levels.mq5                                                     |
//| Zeichnet Support-/Resistance-Linien + Zonen (Ranges), die der    |
//| Python-Bot (Oil Trading Server) nach MQL5\Files\sr_levels.csv     |
//| schreibt. Das Python-MetaTrader5-Paket kann selbst KEINE Chart-   |
//| Objekte erzeugen — daher diese Datei-Bridge.                      |
//|                                                                    |
//| Installation:                                                      |
//|  1) Diese Datei nach <Terminal>\MQL5\Indicators\ kopieren.        |
//|  2) In MetaEditor öffnen und mit F7 kompilieren.                   |
//|  3) Im Navigator auf den WTI-Chart (SpotCrude) ziehen.            |
//|  4) Der Bot muss laufen (schreibt die CSV alle ~5 s).             |
//+------------------------------------------------------------------+
#property copyright "Oil Trading Bot"
#property version   "1.44"
// v1.42 (2026-08-19): Y-Stapel oben rechts GEMESSEN statt geschaetzt. Der
//        Faktor 1,8 aus v1.41 war nur die naechste Schaetzung; jetzt liefert
//        TextGetSize die TATSAECHLICHE Pixelhoehe (LabelH). Ausserdem wird Y bei
//        JEDEM Redraw gesetzt, nicht nur beim Erzeugen -- ein bereits
//        vorhandenes Label behielt sonst seine alte Position.
// v1.41 (2026-08-19): Y-STAPEL oben rechts korrigiert (User: "oben rechts die
//        Texte ueberlagern sich"). Dieselbe Punkt-statt-Pixel-Rechnung wie
//        gestern links: bei InpPriceSize=20 lag G/V auf y=44 mitten im Kurs
//        (14..50) und HL auf y=56 mitten im G/V. Jetzt gestapelt mit Faktor
//        1,8 px je Punkt -> Kurs 14, G/V 50, HL 78.
// v1.40 (2026-08-19): MELDUNGEN ZENTRIERT statt oben links (User-Wunsch).
//        MT5 kennt keine "Mitte"-Ecke: Bezug bleibt CORNER_LEFT_UPPER, aber der
//        ANKER wird ANCHOR_UPPER (Oberkante mittig) und X = halbe Chartbreite.
//        X wird bei JEDEM Redraw neu gesetzt, sonst haengt die Zentrierung nach
//        einer Fenster-Groessenaenderung auf der alten Breite fest.
//        Startabstand 40 -> 24, weil MT5s eigene Symbol-Zeile links steht und
//        mittig nicht mehr stoert.
// v1.39 (2026-08-19): MELDUNGEN oben links neu positioniert (User: "die
//        Anzeigetexte ueberlagern sich"). Zwei Fehler: der Start lag bei y=16
//        und damit in MT5s EIGENER Symbol-/OHLC-Zeile, und der Zeilenabstand
//        rechnete FONTSIZE wie Pixel -- es ist aber PUNKT (10 pt = 16-20 px je
//        nach DPI). Jetzt Startabstand ueber InpMeldungY (40) und Abstand
//        Faktor 2,0 der Schriftgroesse.
// v1.38 (2026-08-17): Beschriftung der KONSOLIDIERUNGS-BOX entfernt (User-Wunsch).
//        Sie kam in v1.30 auf die Frage "was bedeutet die graue Box" und stand
//        seither dauerhaft im Chart. Der erklaerende Text bleibt als TOOLTIP an
//        der Box -- die Information geht nicht verloren, sie draengt sich nur
//        nicht mehr auf. Ein Alt-Objekt wird beim ersten Redraw geloescht.
// v1.37 (2026-08-17): ZONEN (RZ/SZ) — S/R zusaetzlich als farbiges Band, wie im
//        TradingView-Vorbild des Users. Die Breite ist die echte Spanne der
//        M30-Pivots im Cluster; die Vorlage leitet ihre Zone aus Fibonacci ab,
//        das ist hier gemessen und verworfen. Untere Zone heisst SUPPORT, nicht
//        "Long Liquidation" -- echte Liquidationsdaten hat das Projekt nicht.
// v1.36 (2026-08-17): MELDUNGEN oben links (MS;<text>) — dieselben Zeilen wie
//        die Dashboard-Karte "Meldungen" (Squeeze, Ueberdehnung, S/R-Ziel,
//        Stop-Seite, News-Blackout). Der Bot setzt sie nur ZUSAMMEN aus bereits
//        berechneten Snapshot-Teilen, er rechnet nichts neu -- die Lehre aus dem
//        Konsens-Pfeil, der im 5-s-Takt ein zweites _verdict() zog.
//        Abschaltbar per InpShowMeldungen oder [trading] export_meldungen=false.
// v1.35 (2026-08-13): S/R-LABEL WORTGLEICH ZUR M15-KARTE (User: „die Beschriftung
//        fuer S/R im Chart stimmt nicht mit der Analyse in der M15-Karte ueberein").
//        Das Label trug seit v1.27 die NACKTE Zahl = P(Durchbruch); die Karte klappt
//        unter 45 % auf P(Abprall) um und nennt 45-55 % „unentschieden". Dasselbe
//        Level stand damit im Chart als „33%" und in der Karte als „67 % Abprall" —
//        beide Zahlen richtig, aber die Chart-Zahl sagte nicht, WAS sie ist.
//        GEMESSEN in 88,1 % der Zeit verschieden (1.797 Vorhersagen/14 Tage).
//        Jetzt: „69% Bruch" / „unentschieden" / „67% Abprall" — identisch zu app.js.
// v1.34 (2026-08-01): HYPERLIQUID-KURS (HL;...) unter Kurs und G/V oben rechts,
//        bernstein. HL handelt 24/7, Pepperstone nicht - am Wochenende der einzige
//        lebende Kurs; die Basis (~0,45 $ Kontrakt-Differenz) ist herausgerechnet,
//        der Wert also direkt mit dem Broker-Kurs vergleichbar. InpShowHL/InpHLColor.
// v1.33 (2026-07-31): Konsens-Pfeil (K) per Default AUS + Pfeile kuerzer (8 statt
//        14 Bars). Grund: L und K starten am selben Punkt und zeigten oft in
//        ENTGEGENGESETZTE Richtungen - zwei divergierende Linien aus einem Punkt
//        sehen aus wie der Kegel, den sie ersetzen sollten. Jetzt normalerweise EIN
//        Pfeil (L, der kalibrierte); S kommt nur beim Squeeze-Ausbruch dazu.
//        InpShowConsensus=true holt den Konsens-Pfeil zurueck.
// v1.32 (2026-07-31): RICHTUNGS-PFEILE (AR;<art>;<von>;<nach>;<text>) statt des
//        Kegels (User-Wunsch). DREI Arten mit UNTERSCHIEDLICHER Beleglage:
//          L blau      = am naechsten Level, aus dem kalibrierten P(break)
//                        (AUC 0,65 oos, live kalibriert) - BEDINGTE Aussage
//          S neongruen = Squeeze-Ausbruch, das EINZIGE 2-Stichproben-validierte
//                        Richtungssignal - erscheint nur bei `active`
//          K grau      = Konsens der Verdict-Module - NICHT kalibriert, heisst
//                        deshalb "Konsens" und nicht "Prognose"
//        ⚠ KEINER ist eine freie Kursprognose: die ist im Projekt gemessen ein
//        Muenzwurf (analyze_reversal.py, AUC 0,499-0,509 oos, in-sample 0,52).
//        Der Kegel bleibt im Code (InpShowCone), der Bot exportiert ihn aber per
//        Default nicht mehr ([trading] export_cone).
// v1.31 (2026-07-31): WAHRSCHEINLICHKEITS-KEGEL (CN;<minuten>;<lo>;<hi>) — zwei sich
//        oeffnende, gepunktete Pfade vom aktuellen Bar ueber 30/60/120 min.
//        ⚠ KEINE Richtungsprognose, sondern STREUUNG: wie weit der Kurs plausibel
//        gelaufen sein kann. Out-of-sample kalibriert (analyze_cone.py): das
//        80%-Band trifft real ~76%. Bewusst NEUTRAL grau (keine Richtungsfarbe).
//        InpShowCone / InpConeColor.
// v1.30 (2026-07-30): Konsolidierungs-Box BESCHRIFTET (User-Frage „was bedeutet die
//        graue Box") — sichtbares Label an der Oberkante, rechtsbuendig wie die
//        uebrigen: „Konsolidierung (10-Bar-Spanne)" bzw. bei Kompression
//        „Konsolidierung - komprimiert, Ausbruch steht bevor".
// v1.29 (2026-07-30): KONSOLIDIERUNGS-BOX (CO;lo;hi;tight) — die Spanne der letzten
//        10 M5-Bars, aus der der validierte Squeeze-Ausbruch kommt. Komprimiert
//        (<=2,5xATR) wird sie deutlicher gezeichnet. ⚠ REINE ANZEIGE: alle
//        Konsolidierungs-SETUPS sind gemessen verworfen (backtest_consolidation.py) —
//        die Box zeigt WO gestaut wird, nicht WOHIN es geht.
// v1.28 (2026-07-30): G/V der offenen Position direkt UNTER dem Kurs oben rechts
//        (User-Wunsch) — PL;wert;waehrung, NETTO wie im Dashboard (WHT-Quellensteuer
//        bei Gewinn bereits abgezogen). Gruen im Plus, rot im Minus; ohne offene
//        Position schickt der Bot keine PL-Zeile -> Label verschwindet.
// v1.27 (2026-07-30): S/R-Label zeigt nur noch die Prozentzahl statt
//        „Durchbruch 34%" (User-Wunsch) — der ausgeschriebene Text bleibt im Tooltip.
// v1.26 (2026-07-30): Beschriftungen + Pfeile stehen jetzt GANZ rechts am
//        Chartrand — auf Hoehe des Kurs-Labels (User: „genau so angeordnet wie der
//        aktuelle Kurs"). RightTime() rechnet dafuer das Ende des Shift-Bereichs
//        aus (CHART_WIDTH_IN_BARS x InpShiftPct) statt fix 2 Kerzen hinter die
//        letzte Bar. Gilt beim Erzeugen UND beim Nachfuehren.
// v1.25 (2026-07-30): S/R-Labels zeigen die DURCHBRUCHWAHRSCHEINLICHKEIT
//        (User-Wunsch) — der Bot haengt sie als drittes CSV-Feld an (R;preis;pct).
//        Quelle = dasselbe kalibrierte P(break)-Modell wie der S/R-Auto-Close, auf
//        die M30-Level nachtrainiert (AUC 0,72 out-of-sample). Niedrig = Level
//        haelt eher. Abschaltbar via InpShowPBreak.
// v1.24 (2026-07-30): Farb-Inputs UMBENANNT — MT5 speichert Input-Werte je
//        Chart-Instanz und ueberschreibt damit NEUE Defaults, auch nach Recompile
//        (deshalb blieben die Pfeile gold). Durch die Umbenennung findet MT5 keinen
//        gespeicherten Wert mehr und nimmt den Default:
//          InpWallBidColor  -> InpArrowUpColor    (neongruen #00FF7F)
//          InpWallAskColor  -> InpArrowDownColor  (rot #FF4500)
//          InpLiqTrendColor -> InpTrendLineColor  (neongruen)
//          InpLiqZoneColor/Alpha -> InpZoneColor/InpZoneAlpha
// v1.23 (2026-07-30): (a) Pfeile wieder NEONGRUEN/ROT statt Gold (User-Wunsch).
//        (b) Liquiditaets-Trendlinie neongruen wie der Up-Pfeil (statt pink).
//        (c) FIX „Beschriftungen nicht komplett rechtsbuendig": auf den Linien war
//            OBJPROP_TEXT gesetzt — den zeichnet MT5 LINKS am Chartrand. Jetzt
//            OBJPROP_TOOLTIP + CHART_SHOW_OBJECT_DESCR=false.
//        (e) ALLE Beschriftungen weiss (InpLabelColor) — die Linien behalten
//            ihre Farbe, nur der Text ist einheitlich weiss.
//        (d) MT5-Positions-Labels ("sell 0.36") stehen systembedingt LINKS und
//            koennen nicht verschoben werden -> nur ab-/anschaltbar via
//            InpShowTradeLevels.
// v1.22 (2026-07-30): Aufraeumen nach User-Feedback „das Bild sieht komisch aus":
//        (a) Trade-Umrandungen ENTFERNT (InpShowTrades=false, Export im Bot aus).
//        (b) Liquiditaets-Trendlinie nicht mehr RAY_RIGHT — eine 60-min-Regression
//            100 Bars in die Zukunft zu ziehen riss die Linie ins Bodenlose.
//        (c) CHART_SHIFT_SIZE begrenzt (InpShiftPct=12 %) — ohne Begrenzung stand
//            der Kursverlauf ganz links gequetscht.
//        ⚠ MT5 uebernimmt beim Neukompilieren NICHT die neuen Default-Inputs, wenn
//        der Indikator schon auf dem Chart liegt: einmal ENTFERNEN und neu aufziehen.
// v1.21 (2026-07-30): Beschriftungen aller Linien RECHTSBUENDIG am rechten
//        Chart-Rand (vorher zentriert; ANCHOR_RIGHT_LOWER -> Text endet am Anker,
//        laeuft nach links, ragt nicht raus) + Schriftgroesse 8 -> 12 (+4 px,
//        User-Wunsch). Neu einstellbar via InpLabelSize.
// v1.20 (2026-07-30): aktuelle KURSLINIE weiss (User-Vorgabe) — CHART_COLOR_BID/
//        ASK/LAST auf InpThemeBidLine, Bid-Linie explizit eingeschaltet.
// v1.19 (2026-07-30): Liquiditaetszone TRANSPARENT (User: „Transparenz der Zone
//        auf 10 %"). MT5 kann bei OBJ_RECTANGLE kein echtes Alpha -> BlendBg()
//        mischt die Fuellfarbe gegen den (bekannten, einfarbigen) Chart-Hintergrund;
//        das Ergebnis sieht identisch aus. InpZoneAlpha = Deckkraft in %.
//        Die gestrichelte Kante bleibt in voller Farbe (wie im Referenzbild).
// v1.18 (2026-07-30): NEU (User-Wuensche) — (a) LIQUIDITAETSZONEN: Equal Highs /
//        Equal Lows aus den M30-Pivots als goldenes Band + gestrichelte Kante,
//        nach rechts verlaengert (EQ;<H|L>;lo;hi;zeit). REINE ANZEIGE, als Signal
//        gemessen verworfen. (b) AKTUELLER KURS oben rechts, fett (PX;bid).
// v1.17 (2026-07-30): FIX „Liquiditaets-Pfeile werden nicht angezeigt" — zwei
//        Ursachen: (1) ANCHOR_LEFT ist fuer OBJ_ARROW UNGUELTIG (nur ANCHOR_TOP/
//        ANCHOR_BOTTOM), (2) die Pfeile sitzen rechts NEBEN der letzten Kerze —
//        ohne CHART_SHIFT gibt es dort keinen sichtbaren Bereich. Beides behoben.
//        NEU: TRADE-MARKER (TR;<O|C>;zeit) — die Kerze der Eroeffnung wird HELLGRUEN,
//        die des Close DUNKELGRUEN umrandet (InpShowTrades/InpTradeOpenColor/
//        InpTradeCloseColor).
// v1.16 (2026-07-30): Beschriftung der Liquiditäts-Trendlinie per Default AUS
//        (User: „die beschreibung der liquiditätslinie kann ausgeblendet werden") —
//        Text steht jetzt im Tooltip der Linie; `InpLiqTrendLabel=true` holt sie zurück.
// v1.15 (2026-07-30): CHART-THEME (User: „Farben im MT5 wie im Bild") — setzt beim
//        Start Hintergrund/Gitter/Kerzenfarben auf die Dashboard-Palette
//        (Bull #3B82F6, Bear #EF4444, BG #0D1117, Gitter #10151C dezent).
//        Liquiditäts-Pfeile jetzt GOLD #F59E0B (= „Liquiditätszone" der Legende).
//        Abschaltbar via InpApplyTheme. ⚠ ändert das Chart dauerhaft.
// v1.14 (2026-07-30): Liquiditäts-LINIEN + Text-Labels ENTFERNT (User: „blende die
//        liquiditätslinien aus, nur die Pfeile sollen rechtsbündig angezeigt werden").
//        Es bleibt NUR der Pfeil auf dem Wand-Preis, RECHTSBÜNDIG am aktuellen Rand
//        (wird in RepositionLabels() nachgeführt, bleibt beim Scrollen rechts).
//        Details (Seite/Größe/Dominanz/Preis) jetzt im TOOLTIP statt im Chart.
//        Die pinke Liquiditäts-Trendlinie bleibt unverändert.
// v1.13 (2026-07-30): Liquiditäts-Wände bekommen einen PFEIL in ihrer Wirkrichtung
//        (Lesart „die Wand hält" — Nachfrage stützt hoch, Angebot deckelt runter;
//        die GRÖSSERE Wand wird dicker gezeichnet + "<<"). NEU: pinke
//        LIQUIDITÄTS-TRENDLINIE (LT;…) = Regression über den volumengewichteten
//        Wand-Schwerpunkt der letzten ~60 min — zeigt, wohin das Gewicht der
//        RUHENDEN Orders wandert (nicht der gehandelte Kurs).
//        ⚠ Pfeilrichtung ist INTERPRETATION, nicht gemessen (s. analyze_bookflow.py).
// v1.12 (2026-07-30): Chartmuster-Linien (PT;…) ENTFERNT (User-Wunsch, Chart war zu
//        voll — Kachel/PWA-Anzeige bleibt). NEU: ECHTE Liquiditäts-Wände (LQ;…) aus
//        dem Hyperliquid-L2-Orderbuch, basis-korrigiert auf CFD-Niveau — die einzigen
//        Linien im Chart, die nicht aus Pivots geschätzt sind, sondern tatsächlich
//        ruhende Orders zeigen (dick gestrichelt + Größen-Label). Reine Anzeige.
// v1.11: Chartmuster-Linien (jetzt entfernt).
//        ⚠ nach dem Update den Indikator NEU mit F7 kompilieren.
#property indicator_chart_window
#property indicator_plots 0

input string InpFile       = "sr_levels.csv";  // Datei in MQL5\Files
input int    InpRefreshS   = 2;                // Aktualisierung (Sekunden)
input color  InpResColor   = clrDodgerBlue;    // Widerstand (blau kräftig)
input color  InpSupColor   = clrDeepSkyBlue;   // Support (blau hell)
input bool   InpShowRange  = false;            // dynamische Handels-Range zeichnen (aus per User-Wunsch)
input int    InpRangeBars  = 50;               // Range = Hoch/Tief der letzten N Kerzen
input color  InpRangeColor = clrGoldenrod;     // Range-Farbe
input bool   InpRangeFill   = false;           // Range füllen (aus = nur Umriss)
input bool   InpShowChannel = true;            // Regressionskanal (M30) zeichnen
input color  InpChanColor   = clrYellow;       // Kanal-Farbe
input bool   InpShowWalls    = true;           // echte Liquiditäts-Wände (Hyperliquid-Orderbuch)
input color  InpArrowUpColor = C'0,255,127';   // Liquiditaet Nachfrage (Bid) — NEONGRUEN
input color  InpArrowDownColor = C'255,69,0';    // Liquiditaet Angebot (Ask) — ROT
input bool   InpShowLiqTrend  = true;          // Liquiditäts-Trendlinie zeichnen
input color  InpTrendLineColor = C'0,255,127';  // Liquiditaets-Trend — NEONGRUEN (wie Up-Pfeil)
input bool   InpLiqTrendLabel = false;         // Beschriftung der Trendlinie (aus per User-Wunsch)
input bool   InpShowTrades   = false;          // Trade-Kerzen umranden (AUS per User-Wunsch)
input color  InpTradeOpenColor  = C'134,239,172'; // Eroeffnung — hellgruen
input color  InpTradeCloseColor = C'21,128,61';   // Close — dunkelgruen
input bool   InpShowLiqZones = true;           // Liquiditaetszonen (Equal Highs/Lows)
input color  InpZoneColor = C'245,158,11';  // Zonen-Grundfarbe (Gold #F59E0B)
input int    InpZoneAlpha = 10;             // Deckkraft der Zonenfuellung in % (10 = sehr transparent)
input bool   InpShowPrice    = true;           // aktuellen Kurs oben rechts anzeigen
input int    InpPriceSize    = 20;             // Schriftgroesse des Kurses
input int    InpLabelSize    = 12;             // Schriftgroesse der Linien-Beschriftungen (+4 per User-Wunsch)
input bool   InpShowPBreak   = true;           // Durchbruchwahrscheinlichkeit am S/R-Label
input color  InpLabelColor   = clrWhite;       // Farbe ALLER Linien-Beschriftungen (User-Wunsch: weiss)
input color  InpPriceColor   = clrWhite;       // Kurs oben rechts (weiss)
input bool   InpShowConsol   = true;           // Konsolidierungs-Box (10 M5-Bars) zeichnen
input color  InpConsolColor  = C'88,166,255';  // Box-Farbe (blau)
input int    InpConsolAlpha  = 8;              // Deckkraft in % (komprimiert: x2)
input int    InpConsolBars   = 10;             // Box-Laenge in Bars (= _SQ_N im Bot)
input bool   InpShowCone     = true;           // Wahrscheinlichkeits-Kegel (Streuung, NICHT Richtung)
input bool   InpShowArrows   = true;           // Richtungs-Pfeile (Level / Squeeze / Konsens)
input color  InpArrLevel     = C'88,166,255';  // Pfeil am naechsten Level (P(break)) - blau
input color  InpArrSqueeze   = C'0,255,127';   // Squeeze-Ausbruch (validiert) - neongruen
input color  InpArrConsensus = C'139,148,158'; // Konsens der Module (NICHT kalibriert) - grau
// ⚠ Konsens-Pfeil per DEFAULT AUS (2026-07-31): er startet am selben Punkt wie der
// Level-Pfeil und lief oft in die GEGENrichtung - zwei divergierende Linien aus einem
// Punkt sehen aus wie der Kegel, den er ersetzen sollte (User: "trichter anstatt
// pfeil wird angezeigt"). Der Konsens steht ohnehin im Dashboard (Bias-Nadel).
input bool   InpShowConsensus = false;         // Konsens-Pfeil zusaetzlich zeichnen
input int    InpArrBars      = 8;              // Pfeil-Laenge in Bars (kuerzer = Spitze sichtbar)
input color  InpConeColor    = C'139,148,158';  // Kegel-Farbe (neutral grau - bewusst KEINE Richtungsfarbe)
input bool   InpShowHL       = true;           // Hyperliquid-Kurs unter dem Broker-Kurs
input color  InpHLColor      = C'232,160,0';   // HL-Kurs (bernstein, klar unterscheidbar)
input bool   InpShowPnL      = true;           // G/V der offenen Position unter dem Kurs
input color  InpPnLUpColor   = C'0,255,127';   // G/V im Plus (neongruen)
input color  InpPnLDownColor = C'255,69,0';    // G/V im Minus (rot)
// ── Chart-THEME (User-Vorgabe 2026-07-30 „Farben im MT5 wie im Bild") ────────
// Setzt beim Start die Chart-Eigenschaften auf die Dashboard-Palette, damit MT5
// und die PWA gleich aussehen. Farben in MQL5-Notation C'R,G,B'.
//   Bullish #3B82F6 · Bearish #EF4444 · Liquidität #F59E0B · Entry/Target #22C55E
//   Hintergrund #0D1117 · Gitter #10151C (sehr dezent) · Text #8B949E
// ⚠ Ändert das Chart dauerhaft (bleibt auch nach Entfernen des Indikators) —
// deshalb per Schalter abschaltbar.
input bool   InpApplyTheme   = true;           // Chart-Farben setzen (Dashboard-Look)
input color  InpThemeBull    = C'59,130,246';  // Bullish Candle (blau)
input color  InpThemeBear    = C'239,68,68';   // Bearish Candle (rot)
input color  InpThemeBg      = C'13,17,23';    // Hintergrund
input color  InpThemeGrid    = C'16,21,28';    // Gitternetz (dezent)
input color  InpThemeText    = C'139,148,158'; // Achsen/Text
input color  InpThemeBidLine = clrWhite;       // aktuelle Kurslinie (Bid) — weiss
input double InpShiftPct     = 12.0;           // Platz rechts fuer Pfeile/Labels (% der Chartbreite)
input bool   InpShowTradeLevels = true;        // MT5-Positionslinien+Labels (stehen immer LINKS)
// Meldungen oben links (User-Wunsch 2026-08-17) — dieselben Zeilen wie die
// Dashboard-Karte „Meldungen". Der Bot deckelt auf 5; abschaltbar per ini
// (`[trading] export_meldungen=false`) ODER hier.
input bool   InpShowMeldungen = true;         // Meldungen oben links einblenden
input int    InpMeldungSize   = 11;           // Schriftgroesse der Meldungen (v1.43: 10 -> 11, User "eine Groesse zu klein")
input int    InpMeldungY      = 24;           // Startabstand von oben (zentriert -> MT5s Symbol-Zeile stoert nicht)
input color  InpMeldungColor  = clrGainsboro; // Farbe der Meldungen
// Zonen statt nackter Linien (User-Wunsch 2026-08-17). Die BREITE ist die echte
// Spanne der Pivots im Cluster — NICHT Fibonacci (im Projekt gemessen und
// verworfen: haelt nicht besser als eine Zufallslinie gleicher Distanz).
input bool   InpShowZonen    = true;          // S/R als ZONE (Band) zeichnen
input color  InpZoneResColor = C'60,25,30';   // Widerstands-Zone (dunkelrot)
input color  InpZoneSupColor = C'20,45,35';   // Support-Zone (dunkelgruen)

const string PFX  = "SRB_";                    // Präfix S/R-Linien (aus Datei)
const string RPFX = "SRR_";                    // Präfix dynamische Range (aus Chart)
string g_last = "\x01";                        // letzter Datei-Inhalt (gegen Flackern)
// Kegel: Verkettung der Horizonte (30->60->120). Die CSV liefert sie aufsteigend;
// jeder Abschnitt startet dort, wo der vorherige endete -> ein sich oeffnender
// Faecher statt drei unverbundener Geraden. Wird je Redraw zurueckgesetzt.
datetime g_coneT  = 0;
double   g_coneLo = 0.0, g_coneHi = 0.0;
double   g_conePx = 0.0;                       // aktueller Kurs (aus der PX-Zeile)
bool   g_range_drawn = false;                  // hat DIESE Instanz die Range gezeichnet?

//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| ECHTE Pixelhoehe eines Labels — gemessen, nicht geschaetzt.       |
//| ⚠⚠ Die drei Labels oben rechts haben sich zweimal ueberlagert,    |
//|    weil OBJPROP_FONTSIZE in PUNKT angegeben wird und der Code mit |
//|    Pixeln rechnete. Ein Korrekturfaktor (1,8) war nur die naechste|
//|    Schaetzung — bei anderer DPI-Skalierung waere er wieder falsch.|
//|    TextGetSize liefert die TATSAECHLICHE Hoehe in Pixeln.         |
//|    TextSetFont nimmt die Groesse in 1/10 Punkt, wenn sie NEGATIV  |
//|    ist — deshalb -size*10.                                        |
//+------------------------------------------------------------------+
int LabelH(const string font, const int size)
{
   TextSetFont(font, -size * 10);
   uint w = 0, h = 0;
   TextGetSize("0123456789.", w, h);
   return (h > 0 ? (int)h + 6 : size * 2);      // +6 px Luft; Rueckfall falls 0
}

int OnInit()
{
   if(InpApplyTheme) ApplyTheme();
   EventSetTimer(MathMax(1, InpRefreshS));
   Redraw();
   DrawRange();
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Chart-Farben auf die Dashboard-Palette setzen (User-Vorgabe).     |
//| Kerzen werden GEFÜLLT gezeichnet (Body in Bull-/Bear-Farbe), damit|
//| es aussieht wie im Referenzbild.                                  |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Farbe mit dem Chart-Hintergrund mischen — simuliert TRANSPARENZ.  |
//| ⚠ MT5 kann bei OBJ_RECTANGLE KEIN echtes Alpha (nur Canvas-       |
//| Objekte könnten das). Da der Hintergrund bekannt und einfarbig    |
//| ist, sieht die Mischung exakt aus wie eine transparente Füllung.  |
//| pct = Deckkraft in % (10 = sehr transparent, 100 = voll deckend). |
//+------------------------------------------------------------------+
color BlendBg(color c, int pct)
{
   if(pct >= 100) return c;
   if(pct < 0) pct = 0;
   color bg = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);
   double a = pct / 100.0;
   int r = (int)((c        & 0xFF) * a + (bg        & 0xFF) * (1 - a));
   int g = (int)(((c >> 8) & 0xFF) * a + ((bg >> 8) & 0xFF) * (1 - a));
   int b = (int)(((c >> 16)& 0xFF) * a + ((bg >> 16)& 0xFF) * (1 - a));
   return (color)(r | (g << 8) | (b << 16));
}
//+------------------------------------------------------------------+
void ApplyTheme()
{
   ChartSetInteger(0, CHART_MODE, CHART_CANDLES);
   ChartSetInteger(0, CHART_COLOR_BACKGROUND, InpThemeBg);
   ChartSetInteger(0, CHART_COLOR_FOREGROUND, InpThemeText);
   ChartSetInteger(0, CHART_SHOW_GRID, true);
   ChartSetInteger(0, CHART_COLOR_GRID, InpThemeGrid);
   // Body-Füllung
   ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, InpThemeBull);
   ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, InpThemeBear);
   // Umriss + Docht in derselben Farbe → klare, flächige Kerzen wie im Bild
   ChartSetInteger(0, CHART_COLOR_CHART_UP,   InpThemeBull);
   ChartSetInteger(0, CHART_COLOR_CHART_DOWN, InpThemeBear);
   ChartSetInteger(0, CHART_COLOR_CHART_LINE, InpThemeText);
   // Aktuelle Kurslinie WEISS (User-Vorgabe 2026-07-30) — sie soll sich klar von
   // den blau/roten Kerzen und den farbigen Overlays abheben.
   ChartSetInteger(0, CHART_COLOR_BID, InpThemeBidLine);
   ChartSetInteger(0, CHART_COLOR_ASK, InpThemeBidLine);
   ChartSetInteger(0, CHART_COLOR_LAST, InpThemeBidLine);
   ChartSetInteger(0, CHART_SHOW_BID_LINE, true);
   ChartSetInteger(0, CHART_COLOR_STOP_LEVEL, C'239,68,68');
   ChartSetInteger(0, CHART_SHOW_PERIOD_SEP, false);
   // ⚠ CHART_SHIFT MUSS an sein: die Liquiditäts-Pfeile sitzen rechts NEBEN der
   // letzten Kerze. Ohne Shift gibt es dort keinen sichtbaren Bereich → die Pfeile
   // wären unsichtbar (das war der zweite Grund für „Pfeile werden nicht angezeigt").
   ChartSetInteger(0, CHART_SHIFT, true);
   // Shift-Bereich klein halten: nur so viel Platz rechts, dass Pfeile und
   // Beschriftungen hinpassen. Ohne Begrenzung stand der Kursverlauf ganz links
   // gequetscht (User-Feedback „das Bild sieht komisch aus", 2026-07-30).
   ChartSetDouble(0, CHART_SHIFT_SIZE, InpShiftPct);
   // MT5 zeichnet Objekt-BESCHREIBUNGEN immer LINKS an den Chartrand — aus, damit
   // nur die eigenen, rechtsbuendigen Labels sichtbar sind (User-Vorgabe).
   ChartSetInteger(0, CHART_SHOW_OBJECT_DESCR, false);
   // Positions-/Order-Beschriftungen ("sell 0.36 ...") stehen bei MT5 fest LINKS und
   // lassen sich NICHT nach rechts holen — nur ganz aus- oder einschalten.
   ChartSetInteger(0, CHART_SHOW_TRADE_LEVELS, InpShowTradeLevels);
   ChartRedraw();
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   EventKillTimer();
   ObjectsDeleteAll(0, PFX);
   ObjectsDeleteAll(0, RPFX);
   ChartRedraw();
}
//+------------------------------------------------------------------+
void OnTimer() { Redraw(); DrawRange(); RepositionLabels(); }
//+------------------------------------------------------------------+
//| Zeitpunkt der horizontalen Chart-Mitte (sichtbarer Bereich)       |
//+------------------------------------------------------------------+
datetime CenterTime()
{
   long first = ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
   long width = ChartGetInteger(0, CHART_WIDTH_IN_BARS);
   int  mid   = (int)MathMax(0, first - width / 2);
   return iTime(_Symbol, PERIOD_CURRENT, mid);
}
//+------------------------------------------------------------------+
//| Zeitpunkt am RECHTEN Chart-Rand (im Shift-Bereich hinter der      |
//| letzten Kerze) — dort sitzen ALLE Beschriftungen (User-Vorgabe    |
//| 2026-07-30 „Beschriftung aller Linien bitte rechtsbündig").       |
//+------------------------------------------------------------------+
datetime RightTime()
{
   // Ganz an den RECHTEN Chartrand — also ans Ende des Shift-Bereichs, nicht nur
   // 2 Kerzen hinter die letzte (User 2026-07-30: „genau so angeordnet wie der
   // aktuelle Kurs"). Der Shift-Bereich ist InpShiftPct % der sichtbaren Breite;
   // ein Balken Reserve, damit der Text nicht am Rand abgeschnitten wird.
   long width = ChartGetInteger(0, CHART_WIDTH_IN_BARS);
   int  bars  = (int)MathMax(2, (double)width * InpShiftPct / 100.0 - 1.0);
   return iTime(_Symbol, PERIOD_CURRENT, 0) + (datetime)(PeriodSeconds() * bars);
}
//+------------------------------------------------------------------+
//| Hält die Beschriftungen beim Scrollen/Zoomen am rechten Rand      |
//+------------------------------------------------------------------+
void RepositionLabels()
{
   datetime ct = RightTime();
   int total = ObjectsTotal(0, 0, OBJ_TEXT);
   for(int i = 0; i < total; i++)
   {
      string nm = ObjectName(0, i, 0, OBJ_TEXT);
      if(StringFind(nm, PFX + "T") == 0)
      {
         double pr = ObjectGetDouble(0, nm, OBJPROP_PRICE);
         ObjectMove(0, nm, 0, ct, pr);
         // rechtsbündig = Text endet am Anker und läuft nach LINKS
         ObjectSetInteger(0, nm, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
      }
   }
   // Liquiditäts-PFEILE rechtsbündig halten: die CSV ändert sich nicht bei jedem
   // Tick, ohne Nachführung würden die Pfeile mit jeder neuen Kerze nach links
   // wandern. Preis bleibt, nur die Zeit wird an den rechten Rand gezogen.
   datetime rt = RightTime();
   int na = ObjectsTotal(0, 0, OBJ_ARROW);
   for(int i = 0; i < na; i++)
   {
      string nm = ObjectName(0, i, 0, OBJ_ARROW);
      if(StringFind(nm, PFX + "ALQ_") == 0)
         ObjectMove(0, nm, 0, rt, ObjectGetDouble(0, nm, OBJPROP_PRICE));
   }
   ChartRedraw();
}
//+------------------------------------------------------------------+
//| Dynamische Handels-Range = Hoch/Tief der letzten N Kerzen des    |
//| AKTUELLEN Charts (TF-genau). In-place via ObjectMove → kein      |
//| Flackern, passt sich an M1/M5/…/H1 automatisch an.               |
//+------------------------------------------------------------------+
void DrawRange()
{
   string nm = RPFX + "BOX";
   if(!InpShowRange)
   {
      // Nur löschen, was DIESE Instanz gezeichnet hat — sonst kämpfen zwei
      // Instanzen (eine an/eine aus) um die Box → Blinken im 2-s-Takt.
      if(g_range_drawn) { ObjectDelete(0, nm); g_range_drawn = false; ChartRedraw(); }
      return;
   }
   int total = Bars(_Symbol, PERIOD_CURRENT);
   if(total < 5) return;
   int n = InpRangeBars;
   if(n > total - 1) n = total - 1;
   int hi_i = iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, n, 0);
   int lo_i = iLowest (_Symbol, PERIOD_CURRENT, MODE_LOW,  n, 0);
   if(hi_i < 0 || lo_i < 0) return;
   double hi = iHigh(_Symbol, PERIOD_CURRENT, hi_i);
   double lo = iLow (_Symbol, PERIOD_CURRENT, lo_i);
   datetime t0 = iTime(_Symbol, PERIOD_CURRENT, n - 1);
   datetime t1 = iTime(_Symbol, PERIOD_CURRENT, 0) + PeriodSeconds();
   if(ObjectFind(0, nm) < 0)
   {
      ObjectCreate(0, nm, OBJ_RECTANGLE, 0, t0, lo, t1, hi);
      ObjectSetInteger(0, nm, OBJPROP_COLOR, InpRangeColor);
      ObjectSetInteger(0, nm, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, nm, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, nm, OBJPROP_FILL, InpRangeFill);
      ObjectSetInteger(0, nm, OBJPROP_BACK, true);
      ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
      ObjectSetString (0, nm, OBJPROP_TEXT, "Range " + IntegerToString(n));
   }
   else
   {
      ObjectMove(0, nm, 0, t0, lo);       // linke untere Ecke
      ObjectMove(0, nm, 1, t1, hi);       // rechte obere Ecke
   }
   g_range_drawn = true;
   ChartRedraw();
}
//+------------------------------------------------------------------+
//| Pflicht-Funktion jedes Indikators — hier No-op (wir zeichnen     |
//| über OnTimer aus der Datei, nicht aus den Kursreihen).           |
//+------------------------------------------------------------------+
int OnCalculate(const int        rates_total,
                const int        prev_calculated,
                const datetime  &time[],
                const double    &open[],
                const double    &high[],
                const double    &low[],
                const double    &close[],
                const long      &tick_volume[],
                const long      &volume[],
                const int       &spread[])
{
   return(rates_total);
}
//+------------------------------------------------------------------+
void Redraw()
{
   int h = FileOpen(InpFile, FILE_READ|FILE_TXT|FILE_ANSI|FILE_SHARE_READ|FILE_SHARE_WRITE);
   if(h == INVALID_HANDLE)
      return;                                   // Datei (noch) nicht da → nächster Tick

   // Ganze Datei in einen String lesen und mit dem letzten Stand vergleichen —
   // nur bei ÄNDERUNG neu zeichnen (sonst flackert der delete/recreate alle 2 s).
   string content = "";
   while(!FileIsEnding(h))
      content += FileReadString(h) + "\n";
   FileClose(h);
   if(content == g_last)
      return;                                   // nichts geändert → nicht anfassen
   g_last = content;

   ObjectsDeleteAll(0, PFX);
   string rows[];
   int nr = StringSplit(content, '\n', rows);
   int idx = 0;
   int msg_idx = 0;      // Meldungs-Stapel oben links
   // ⚠⚠ ZEILENHOEHE MESSEN STATT SCHAETZEN (v1.44, User: „die zeilenabstaende
   //   sind immer noch zu gering"). Zweimal aus der Punktgroesse hochgerechnet
   //   (Faktor 2,0 dann 2,6) — beide Male zu knapp, weil der Zusammenhang
   //   Punkt->Pixel von der DPI-Skalierung abhaengt und NICHT vom Quelltext aus
   //   bekannt ist. `TextGetSize` liefert die TATSAECHLICHE Pixelhoehe.
   //   ⚠ NEGATIVE Groesse in `TextSetFont` = Angabe in 1/10 pt, und NUR diese
   //     Form wird von MT5 DPI-korrekt skaliert (positive Werte nicht).
   int msg_h = 0;
   {
      uint _tw = 0, _th = 0;   // ⚠ TextGetSize verlangt uint&, nicht int& (error 246)
      TextSetFont("Arial", -InpMeldungSize * 10);
      if(TextGetSize("Hg", _tw, _th) && _th > 0)
         msg_h = (int)_th + 8;                    // + Luft, sonst kleben die Zeilen
   }
   if(msg_h < 20) msg_h = (int)(InpMeldungSize * 2.6);   // Fallback wie v1.43
   // Kegel-Verkettung je Redraw zuruecksetzen (sonst haengt der Faecher am Stand
   // des vorherigen Durchlaufs)
   g_coneT = 0; g_coneLo = 0.0; g_coneHi = 0.0; g_conePx = 0.0;

   for(int r = 0; r < nr; r++)
   {
      string line = rows[r];
      if(StringLen(line) < 2) continue;
      string p[];
      int k = StringSplit(line, ';', p);
      if(k < 2) continue;
      string typ = p[0];

      // 1. Zeile: SYM;<symbol> — nur auf dem passenden Chart zeichnen
      if(typ == "SYM")
      {
         if(p[1] != _Symbol)
         {
            ObjectsDeleteAll(0, PFX);
            ChartRedraw();
            return;
         }
         continue;
      }
      // Horizontale S/R-Linien + sichtbares Text-Label an der Linie
      if(typ == "R" || typ == "S")
      {
         double price = StringToDouble(p[1]);
         if(price <= 0) continue;
         // Feld 3 (optional) = P(Durchbruch) in % aus dem kalibrierten Modell des
         // Bots. Niedrig = Level haelt eher, hoch = bricht eher.
         // ⚠⚠ WORTGLEICH ZUR M15-KARTE (Fix 2026-08-13, v1.35, User: „die
         //    Beschriftung fuer S/R im Chart stimmt nicht mit der Analyse in der
         //    M15-Karte ueberein"). Hier stand bis dahin die NACKTE Zahl = P(Bruch).
         //    Die Karte (`app.js` lesart()/pTxt()) klappt unter 45 % aber auf den
         //    GEGENWERT P(Abprall) um und nennt 45–55 % „unentschieden" — dasselbe
         //    Level trug damit im Chart „33%" und in der Karte „67 % Abprall".
         //    Beide Zahlen waren richtig, aber die Chart-Zahl sagte nicht, WAS sie
         //    ist. GEMESSEN betraf das 88,1 % der Zeit (1.797 Vorhersagen/14 Tage:
         //    66,3 % lagen unter 45 %, 21,9 % im unentschiedenen Band).
         //    ➜ Wer die Formulierung hier aendert, muss `lesart()` in app.js
         //      mitaendern — sonst laufen sie wieder auseinander.
         string lbl = (typ=="R" ? "Widerstand " : "Unterstuetzung ")
                      + DoubleToString(price, (int)_Digits);
         if(InpShowPBreak && k > 2 && StringLen(p[2]) > 0)
         {
            int pb = (int)StringToInteger(p[2]);
            if(pb >= 45 && pb <= 55)  lbl += "  ·  unentschieden";
            else if(pb > 55)          lbl += "  ·  " + IntegerToString(pb) + "% Bruch";
            else                      lbl += "  ·  " + IntegerToString(100-pb) + "% Abprall";
         }
         color  col = (typ=="R" ? InpResColor : InpSupColor);
         string nm = PFX + typ + "_" + IntegerToString(idx);
         if(ObjectCreate(0, nm, OBJ_HLINE, 0, 0, price))
         {
            ObjectSetInteger(0, nm, OBJPROP_COLOR, col);
            ObjectSetInteger(0, nm, OBJPROP_STYLE, STYLE_DASH);
            ObjectSetInteger(0, nm, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, nm, OBJPROP_BACK, true);
            ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
            // ⚠ TOOLTIP, NICHT OBJPROP_TEXT: den TEXT zeichnet MT5 LINKS am
            // Chartrand (Option „Objektbeschreibungen") — das war die
            // linksbuendige Beschriftung. Als Tooltip stoert er nicht.
            ObjectSetString (0, nm, OBJPROP_TOOLTIP, lbl);
         }
         // Beschriftung als eigenes Text-Objekt — RECHTSBÜNDIG am rechten Chart-
         // Rand (User-Vorgabe 2026-07-30; vorher horizontal zentriert). Bleibt via
         // RepositionLabels() beim Scrollen/Zoomen dort. ANCHOR_RIGHT_LOWER: der
         // Text ENDET am Anker und läuft nach links → ragt nicht aus dem Chart.
         string tn = PFX + "T" + typ + "_" + IntegerToString(idx);
         if(ObjectCreate(0, tn, OBJ_TEXT, 0, RightTime(), price))
         {
            ObjectSetString (0, tn, OBJPROP_TEXT, lbl);
            ObjectSetInteger(0, tn, OBJPROP_COLOR, InpLabelColor);
            ObjectSetInteger(0, tn, OBJPROP_FONTSIZE, InpLabelSize);
            ObjectSetInteger(0, tn, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
            ObjectSetInteger(0, tn, OBJPROP_BACK, false);
            ObjectSetInteger(0, tn, OBJPROP_SELECTABLE, false);
         }
         idx++;
      }
      // Regressionskanal: CH;<U|M|L>;t1;p1;t2;p2 → diagonale Trendlinie (OBJ_TREND),
      // nach rechts verlängert. Anker in Broker-Zeit (= Server-Zeit im Terminal).
      if(typ == "CH")
      {
         if(!InpShowChannel) continue;
         if(k < 6) continue;
         string   kind = p[1];
         datetime t1   = (datetime)StringToInteger(p[2]);
         double   pr1  = StringToDouble(p[3]);
         datetime t2   = (datetime)StringToInteger(p[4]);
         double   pr2  = StringToDouble(p[5]);
         if(pr1 <= 0 || pr2 <= 0 || t1 <= 0 || t2 <= 0) continue;
         string nm = PFX + "CH_" + kind;
         if(ObjectCreate(0, nm, OBJ_TREND, 0, t1, pr1, t2, pr2))
         {
            ObjectSetInteger(0, nm, OBJPROP_COLOR, InpChanColor);
            ObjectSetInteger(0, nm, OBJPROP_STYLE, (kind == "M" ? STYLE_DOT : STYLE_DASH));
            ObjectSetInteger(0, nm, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, nm, OBJPROP_RAY_RIGHT, true);   // nach rechts verlängern
            ObjectSetInteger(0, nm, OBJPROP_RAY_LEFT,  false);
            ObjectSetInteger(0, nm, OBJPROP_BACK, true);
            ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
            ObjectSetString (0, nm, OBJPROP_TOOLTIP, "Kanal " + kind);
         }
         continue;
      }
      // ECHTE Liquiditäts-Wände: LQ;<B|A>;preis;size  (B = Nachfrage/Bid-Wand,
      // A = Angebot/Ask-Wand). Quelle = Hyperliquid-L2-Orderbuch, größtes Level je
      // Seite, im Bot BASIS-KORRIGIERT auf CFD-Niveau (HL notiert ~0,45 $ tiefer).
      // ⚠ Das ist die EINZIGE Linie im Chart, die nicht aus Pivots geschätzt ist,
      // sondern tatsächlich ruhende Orders zeigt. Dick + gestrichelt, damit sie sich
      // klar von den dünnen S/R-Linien abhebt. REINE ANZEIGE (kein Signal).
      // (Chartmuster-Linien „PT;…" wurden 2026-07-30 auf User-Wunsch entfernt.)
      if(typ == "LQ")
      {
         if(k < 3) continue;
         if(!InpShowWalls) continue;
         string side = p[1];
         double lpx  = StringToDouble(p[2]);
         double lsz  = (k > 3) ? StringToDouble(p[3]) : 0.0;
         if(lpx <= 0) continue;
         bool   isBid = (side == "B");
         color  lcol  = isBid ? InpArrowUpColor : InpArrowDownColor;
         // ⚠ 2026-07-30 (User): KEINE waagerechte Linie und KEIN Text-Label mehr —
         // der Chart war zu voll. Es bleibt NUR der Pfeil, RECHTSBÜNDIG am aktuellen
         // Rand, auf dem Preisniveau der Wand. Die Details (Seite/Größe/Dominanz)
         // stehen im TOOLTIP (Maus drüber) statt fest im Chart.
         string dirc  = (k > 4) ? p[4] : (isBid ? "U" : "D");
         bool   isDom = (k > 5 && p[5] == "1");
         string an = PFX + "ALQ_" + side;
         // Rechter Rand = aktuelle (rechteste) Kerze + kleiner Versatz, damit der
         // Pfeil frei neben dem Kursverlauf steht und beim Scrollen rechts bleibt.
         datetime atime = RightTime();
         if(ObjectCreate(0, an, OBJ_ARROW, 0, atime, lpx))
         {
            ObjectSetInteger(0, an, OBJPROP_ARROWCODE, (dirc == "U" ? 233 : 234));
            ObjectSetInteger(0, an, OBJPROP_COLOR, lcol);
            ObjectSetInteger(0, an, OBJPROP_WIDTH, (isDom ? 4 : 2));
            // ⚠ KEIN ANCHOR_LEFT: OBJ_ARROW unterstützt NUR ANCHOR_TOP/ANCHOR_BOTTOM
            // — ein ungültiger Anker war (neben dem fehlenden Chart-Shift) der Grund,
            // warum die Pfeile nicht erschienen (Fix 2026-07-30).
            ObjectSetInteger(0, an, OBJPROP_ANCHOR, ANCHOR_TOP);
            ObjectSetInteger(0, an, OBJPROP_SELECTABLE, false);
         }
         else
         {
            ObjectMove(0, an, 0, atime, lpx);
            ObjectSetInteger(0, an, OBJPROP_WIDTH, (isDom ? 4 : 2));
            ObjectSetInteger(0, an, OBJPROP_COLOR, lcol);
            ObjectSetInteger(0, an, OBJPROP_ARROWCODE, (dirc == "U" ? 233 : 234));
         }
         ObjectSetString(0, an, OBJPROP_TOOLTIP,
                         (isBid ? "Liquiditaet NACHFRAGE " : "Liquiditaet ANGEBOT ")
                         + DoubleToString(lsz, 0) + (isDom ? " (dominant)" : "")
                         + "  @ " + DoubleToString(lpx, (int)_Digits));
         // evtl. noch vorhandene Alt-Objekte (Linie/Label aus v1.13) entfernen
         ObjectDelete(0, PFX + "LQ_" + side);
         ObjectDelete(0, PFX + "TLQ_" + side);
         continue;
      }
      // LIQUIDITÄTSZONE: EQ;<H|L>;lo;hi;<broker_time> → goldenes Band über die
      // Equal Highs / Equal Lows (M30), ab dem ersten Pivot nach rechts verlängert,
      // plus gestrichelte Kante — Darstellung wie im Referenzbild des Users.
      // ⚠ REINE ANZEIGE: als Signal gemessen und verworfen (backtest_liquidity_sweep.py).
      if(typ == "EQ")
      {
         if(k < 5) continue;
         if(!InpShowLiqZones) continue;
         string zs = p[1];
         double zlo = StringToDouble(p[2]), zhi = StringToDouble(p[3]);
         datetime zt = (datetime)StringToInteger(p[4]);
         if(zlo <= 0 || zhi <= 0 || zt <= 0) continue;
         datetime zend = iTime(_Symbol, PERIOD_CURRENT, 0) + (datetime)(PeriodSeconds() * 3);
         string zn = PFX + "EQZ_" + zs;
         color zfill = BlendBg(InpZoneColor, InpZoneAlpha);
         if(ObjectCreate(0, zn, OBJ_RECTANGLE, 0, zt, zlo, zend, zhi))
         {
            ObjectSetInteger(0, zn, OBJPROP_STYLE, STYLE_SOLID);
            ObjectSetInteger(0, zn, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, zn, OBJPROP_FILL, true);
            ObjectSetInteger(0, zn, OBJPROP_BACK, true);
            ObjectSetInteger(0, zn, OBJPROP_SELECTABLE, false);
            ObjectSetString (0, zn, OBJPROP_TOOLTIP,
                             (zs == "H" ? "Equal Highs - Sell-Side Liquidity"
                                        : "Equal Lows - Buy-Side Liquidity"));
         }
         else
         {
            ObjectMove(0, zn, 0, zt, zlo);
            ObjectMove(0, zn, 1, zend, zhi);
         }
         // Farbe IMMER setzen (auch beim Bewegen) — sonst bliebe nach einer
         // Alpha-Änderung die alte Füllung stehen, bis das Objekt neu entsteht.
         ObjectSetInteger(0, zn, OBJPROP_COLOR, zfill);
         // gestrichelte Kante auf Höhe des Levels (wie im Bild)
         string zl = PFX + "EQL_" + zs;
         double edge = (zs == "H" ? zhi : zlo);
         if(ObjectCreate(0, zl, OBJ_TREND, 0, zt, edge, zend, edge))
         {
            ObjectSetInteger(0, zl, OBJPROP_COLOR, InpZoneColor);
            ObjectSetInteger(0, zl, OBJPROP_STYLE, STYLE_DASH);
            ObjectSetInteger(0, zl, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, zl, OBJPROP_RAY_RIGHT, true);
            ObjectSetInteger(0, zl, OBJPROP_BACK, true);
            ObjectSetInteger(0, zl, OBJPROP_SELECTABLE, false);
         }
         else
         {
            ObjectMove(0, zl, 0, zt, edge);
            ObjectMove(0, zl, 1, zend, edge);
         }
         continue;
      }
      // KONSOLIDIERUNGS-BOX: CO;lo;hi;<0|1> — die Spanne der letzten 10 M5-Bars
      // (dieselbe Box, aus der der validierte Squeeze-Ausbruch kommt). Feld 3 = 1
      // bedeutet KOMPRIMIERT (≤2,5×ATR) → Ausbruch steht bevor, dann kräftiger.
      // ⚠ REINE ANZEIGE: alle Konsolidierungs-SETUPS (False Breakout, Distribution,
      // Accumulation, Retest) sind gemessen verworfen — die Box sagt WO gestaut
      // wird, nicht WOHIN es geht.
      if(typ == "CO")
      {
         if(k < 3) continue;
         if(!InpShowConsol) continue;
         double clo = StringToDouble(p[1]), chi = StringToDouble(p[2]);
         bool   tight = (k > 3 && p[3] == "1");
         if(clo <= 0 || chi <= clo) continue;
         datetime ct0 = iTime(_Symbol, PERIOD_CURRENT, 0)
                      - (datetime)(PeriodSeconds() * InpConsolBars);
         datetime ct1 = RightTime();
         string cn = PFX + "CONSOL";
         if(ObjectCreate(0, cn, OBJ_RECTANGLE, 0, ct0, clo, ct1, chi))
         {
            ObjectSetInteger(0, cn, OBJPROP_STYLE, STYLE_SOLID);
            ObjectSetInteger(0, cn, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, cn, OBJPROP_FILL, true);
            ObjectSetInteger(0, cn, OBJPROP_BACK, true);
            ObjectSetInteger(0, cn, OBJPROP_SELECTABLE, false);
         }
         else
         {
            ObjectMove(0, cn, 0, ct0, clo);
            ObjectMove(0, cn, 1, ct1, chi);
         }
         // komprimiert = deutlicher sichtbar (Ausbruch steht bevor)
         ObjectSetInteger(0, cn, OBJPROP_COLOR,
                          BlendBg(InpConsolColor, tight ? InpConsolAlpha * 2
                                                        : InpConsolAlpha));
         string ctip = (tight ? "Konsolidierung (komprimiert - Ausbruch steht bevor)"
                              : "aktuelle 10-Bar-Spanne")
                       + "  " + DoubleToString(clo, (int)_Digits) + " - "
                       + DoubleToString(chi, (int)_Digits);
         ObjectSetString (0, cn, OBJPROP_TOOLTIP, ctip);
         // ⚠ SICHTBARE BESCHRIFTUNG ENTFERNT (User-Wunsch 2026-08-17). Sie kam
         //   in v1.30 auf die Frage „was bedeutet die graue Box" und war seither
         //   dauerhaft im Chart. Der erklaerende Text bleibt als TOOLTIP an der
         //   Box (`ctip`, Zeile darueber) — die Information geht also nicht
         //   verloren, sie draengt sich nur nicht mehr auf.
         // ⚠ Ein evtl. noch vorhandenes Alt-Objekt aus v1.30-v1.37 wird hier
         //   geloescht: `ObjectsDeleteAll(0, PFX)` raeumt zwar bei jedem Redraw,
         //   aber nur solange der Indikator laeuft — nach einem Wechsel auf die
         //   neue Version ohne Redraw bliebe es sonst stehen.
         ObjectDelete(0, PFX + "TCONSOL");
         continue;
      }
      // WAHRSCHEINLICHKEITS-KEGEL: CN;<minuten>;<lo>;<hi>
      // Zwei sich oeffnende Pfade vom aktuellen Bar aus. ⚠ Das ist KEINE Prognose
      // der Richtung, sondern der STREUUNG: wie weit der Kurs in 30/60/120 min
      // plausibel gelaufen sein kann. Out-of-sample kalibriert (analyze_cone.py):
      // das 80%-Band trifft real ~76%. Bewusst NEUTRAL grau, kein gruen/rot.
      if(typ == "CN")
      {
         if(k < 4) continue;
         if(!InpShowCone) continue;
         int    mins = (int)StringToInteger(p[1]);
         double clo  = StringToDouble(p[2]);
         double chi  = StringToDouble(p[3]);
         if(clo <= 0 || chi <= 0 || mins <= 0) continue;
         // Anker: vom aktuellen Bar bis +mins Minuten (in Broker-Zeit)
         datetime t0 = iTime(_Symbol, PERIOD_CURRENT, 0);
         datetime t1 = t0 + (datetime)(mins * 60);
         // Vorgaenger-Punkt: der naechstkleinere Horizont, sonst der aktuelle Kurs.
         // g_coneT/g_coneLo/g_coneHi halten ihn zwischen den CSV-Zeilen fest; die
         // Zeilen kommen aufsteigend sortiert vom Bot.
         datetime pt = (g_coneT > 0 ? g_coneT : t0);
         double   plo = (g_coneT > 0 ? g_coneLo : g_conePx);
         double   phi = (g_coneT > 0 ? g_coneHi : g_conePx);
         if(plo <= 0 || phi <= 0) { plo = clo; phi = chi; pt = t0; }
         string ln = PFX + "CONE_L" + IntegerToString(mins);
         string un = PFX + "CONE_U" + IntegerToString(mins);
         string tip = "Streuung (keine Richtung): in " + IntegerToString(mins)
                    + " min liegt der Kurs mit ~76% zwischen "
                    + DoubleToString(clo, (int)_Digits) + " und "
                    + DoubleToString(chi, (int)_Digits);
         for(int s = 0; s < 2; s++)
         {
            string nm2 = (s == 0 ? ln : un);
            double a2  = (s == 0 ? plo : phi);
            double b2  = (s == 0 ? clo : chi);
            if(ObjectCreate(0, nm2, OBJ_TREND, 0, pt, a2, t1, b2))
            {
               ObjectSetInteger(0, nm2, OBJPROP_COLOR, InpConeColor);
               ObjectSetInteger(0, nm2, OBJPROP_STYLE, STYLE_DOT);
               ObjectSetInteger(0, nm2, OBJPROP_WIDTH, 1);
               ObjectSetInteger(0, nm2, OBJPROP_RAY_RIGHT, false);
               ObjectSetInteger(0, nm2, OBJPROP_BACK, true);
               ObjectSetInteger(0, nm2, OBJPROP_SELECTABLE, false);
            }
            else
            {
               ObjectMove(0, nm2, 0, pt, a2);
               ObjectMove(0, nm2, 1, t1, b2);
            }
            ObjectSetString(0, nm2, OBJPROP_TOOLTIP, tip);
         }
         g_coneT = t1; g_coneLo = clo; g_coneHi = chi;
         continue;
      }
      // RICHTUNGS-PFEILE: AR;<art>;<von>;<nach>;<text>   art = L | S | K
      // ⚠ KEINE freie Kursprognose (die ist gemessen ein Muenzwurf) - jede Art hat
      // ihre eigene Beleglage, s. Versionshinweis oben.
      if(typ == "AR")
      {
         if(k < 5) continue;
         if(!InpShowArrows) continue;
         string art = p[1];
         if(art == "K" && !InpShowConsensus) continue;   // Konsens-Pfeil optional
         double a_from = StringToDouble(p[2]);
         double a_to   = StringToDouble(p[3]);
         string a_txt  = p[4];
         if(a_from <= 0 || a_to <= 0) continue;
         color  ac = (art == "S") ? InpArrSqueeze
                   : (art == "K") ? InpArrConsensus : InpArrLevel;
         int    aw = (art == "S") ? 3 : 2;      // der validierte Pfeil dicker
         datetime at0 = iTime(_Symbol, PERIOD_CURRENT, 0);
         datetime at1 = at0 + (datetime)(PeriodSeconds() * InpArrBars);
         string an = PFX + "ARR_" + art;
         if(ObjectCreate(0, an, OBJ_ARROWED_LINE, 0, at0, a_from, at1, a_to))
         {
            ObjectSetInteger(0, an, OBJPROP_COLOR, ac);
            ObjectSetInteger(0, an, OBJPROP_WIDTH, aw);
            ObjectSetInteger(0, an, OBJPROP_STYLE, STYLE_SOLID);
            ObjectSetInteger(0, an, OBJPROP_RAY_RIGHT, false);
            ObjectSetInteger(0, an, OBJPROP_BACK, false);
            ObjectSetInteger(0, an, OBJPROP_SELECTABLE, false);
         }
         else
         {
            ObjectMove(0, an, 0, at0, a_from);
            ObjectMove(0, an, 1, at1, a_to);
            ObjectSetInteger(0, an, OBJPROP_COLOR, ac);
         }
         ObjectSetString(0, an, OBJPROP_TOOLTIP, a_txt);
         // Beschriftung an der Pfeilspitze (rechtsbuendig wie alle anderen Labels)
         string atn = PFX + "ALBL_" + art;   // NICHT "T..." (s. RepositionLabels)
         if(ObjectCreate(0, atn, OBJ_TEXT, 0, at1, a_to))
         {
            ObjectSetInteger(0, atn, OBJPROP_COLOR, ac);
            ObjectSetInteger(0, atn, OBJPROP_FONTSIZE, InpLabelSize);
            ObjectSetInteger(0, atn, OBJPROP_ANCHOR, ANCHOR_LEFT);
            ObjectSetInteger(0, atn, OBJPROP_BACK, false);
            ObjectSetInteger(0, atn, OBJPROP_SELECTABLE, false);
         }
         else
            ObjectMove(0, atn, 0, at1, a_to);
         ObjectSetString(0, atn, OBJPROP_TEXT, "  " + a_txt);
         continue;
      }
      // HYPERLIQUID-KURS unter dem Broker-Kurs: HL;<mt5_aequiv>;<alter_s>;<bid>;<ask>;<U|D|->
      // HL (`xyz:CL`) handelt 24/7, Pepperstone nicht - am Wochenende der einzige
      // lebende Kurs. Bereits basis-korrigiert, also direkt vergleichbar.
      if(typ == "HL")
      {
         if(k < 6) continue;
         if(!InpShowHL) continue;
         double hlp = StringToDouble(p[1]);
         if(hlp <= 0) continue;
         int    hage = (int)StringToInteger(p[2]);
         string htr  = p[5];
         string hn = PFX + "HLPX";
         if(ObjectFind(0, hn) < 0)
         {
            ObjectCreate(0, hn, OBJ_LABEL, 0, 0, 0);
            ObjectSetInteger(0, hn, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
            ObjectSetInteger(0, hn, OBJPROP_XDISTANCE, 12);
            // unter Kurs (14) und G/V (14+PriceSize+6) einreihen
            // Y wird unten jeden Redraw gesetzt
            ObjectSetInteger(0, hn, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
            ObjectSetInteger(0, hn, OBJPROP_SELECTABLE, false);
            ObjectSetString (0, hn, OBJPROP_FONT, "Arial");
         }
         ObjectSetInteger(0, hn, OBJPROP_FONTSIZE, (int)MathMax(10, InpPriceSize - 7));
         ObjectSetInteger(0, hn, OBJPROP_YDISTANCE,
                          14 + LabelH("Arial Black", InpPriceSize)
                             + LabelH("Arial", (int)MathMax(8, InpPriceSize - 4)));
         ObjectSetInteger(0, hn, OBJPROP_COLOR, InpHLColor);
         ObjectSetString (0, hn, OBJPROP_TEXT,
                          "HL " + DoubleToString(hlp, (int)_Digits)
                          + (htr == "U" ? " ^" : htr == "D" ? " v" : ""));
         ObjectSetString (0, hn, OBJPROP_TOOLTIP,
                          "Hyperliquid xyz:CL, basis-korrigiert auf MT5-Niveau - "
                          + IntegerToString(hage) + " s alt. Waende "
                          + p[3] + " / " + p[4]);
         continue;
      }
      // AKTUELLER KURS oben rechts, fett (User-Wunsch): PX;bid
      if(typ == "PX")
      {
         if(k < 2) continue;
         double pxv = StringToDouble(p[1]);
         if(pxv <= 0) continue;
         // Kegel-Startpunkt IMMER merken - auch wenn das Kurs-Label aus ist
         // (sonst verliert der Faecher seinen Anker).
         g_conePx = pxv;
         if(!InpShowPrice) continue;
         string pn = PFX + "PRICE";
         if(ObjectFind(0, pn) < 0)
         {
            ObjectCreate(0, pn, OBJ_LABEL, 0, 0, 0);
            ObjectSetInteger(0, pn, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
            ObjectSetInteger(0, pn, OBJPROP_XDISTANCE, 12);
            // ⚠ Y-STAPEL oben rechts (Fix 2026-08-19). Reihenfolge von oben:
            //   Kurs (20 pt) -> G/V (16 pt) -> HL (13 pt). Die Abstaende
            //   rechnen mit Faktor 1,8 px je PUNKT — FONTSIZE ist in Punkt,
            //   nicht Pixel. Die alte Formel behandelte sie wie Pixel, dadurch
            //   lag G/V (y=44) IM Kurs (14..50) und HL (y=56) IM G/V (44..73).
            ObjectSetInteger(0, pn, OBJPROP_YDISTANCE, 14);   // s. u. — jeden Redraw
            ObjectSetInteger(0, pn, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
            ObjectSetString (0, pn, OBJPROP_FONT, "Arial Black");
            ObjectSetInteger(0, pn, OBJPROP_FONTSIZE, InpPriceSize);
            ObjectSetInteger(0, pn, OBJPROP_SELECTABLE, false);
         }
         // ⚠ JEDEN Redraw setzen, nicht nur beim Erzeugen — sonst behaelt ein
         //   bereits vorhandenes Label seine ALTE Position fuer immer.
         ObjectSetInteger(0, pn, OBJPROP_YDISTANCE, 14);
         ObjectSetString (0, pn, OBJPROP_TEXT, DoubleToString(pxv, (int)_Digits));
         ObjectSetInteger(0, pn, OBJPROP_COLOR, InpPriceColor);
         continue;
      }
      // ZONEN (RZ;lo;hi = Widerstand · SZ;lo;hi = Support) — User-Wunsch
      // 2026-08-17 nach einem TradingView-Bild. Farbiges Band statt nackter
      // Linie; die BREITE ist die echte Spanne der Pivots, die den Cluster
      // gebildet haben (nicht Fibonacci — das ist im Projekt gemessen und
      // verworfen). Nach rechts verlaengert, damit es wie im Vorbild wirkt.
      if(typ == "RZ" || typ == "SZ")
      {
         if(!InpShowZonen || k < 3) continue;
         double zlo = StringToDouble(p[1]), zhi = StringToDouble(p[2]);
         if(zlo <= 0 || zhi <= 0) continue;
         string zn = PFX + "ZONE_" + typ;
         color  zc = (typ == "RZ" ? InpZoneResColor : InpZoneSupColor);
         datetime t1 = iTime(_Symbol, PERIOD_CURRENT, MathMin(60, Bars(_Symbol, PERIOD_CURRENT) - 1));
         if(ObjectFind(0, zn) < 0)
         {
            ObjectCreate(0, zn, OBJ_RECTANGLE, 0, t1, zlo, RightTime(), zhi);
            ObjectSetInteger(0, zn, OBJPROP_FILL, true);
            ObjectSetInteger(0, zn, OBJPROP_BACK, true);
            ObjectSetInteger(0, zn, OBJPROP_SELECTABLE, false);
         }
         ObjectSetInteger(0, zn, OBJPROP_COLOR, zc);
         ObjectMove(0, zn, 0, t1, zlo);
         ObjectMove(0, zn, 1, RightTime(), zhi);
         // Beschriftung an der Oberkante, rechtsbuendig wie die S/R-Labels
         string zt = PFX + "TZONE_" + typ;
         if(ObjectFind(0, zt) < 0)
         {
            ObjectCreate(0, zt, OBJ_TEXT, 0, RightTime(), zhi);
            ObjectSetInteger(0, zt, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
            ObjectSetInteger(0, zt, OBJPROP_FONTSIZE, InpLabelSize);
            ObjectSetInteger(0, zt, OBJPROP_SELECTABLE, false);
         }
         ObjectSetString (0, zt, OBJPROP_TEXT,
                          (typ == "RZ" ? "Resistance Zone " : "Support Zone ")
                          + DoubleToString(zlo, (int)_Digits) + " - "
                          + DoubleToString(zhi, (int)_Digits));
         ObjectSetInteger(0, zt, OBJPROP_COLOR, zc);
         ObjectMove(0, zt, 0, RightTime(), zhi);
         continue;
      }
      // MELDUNGEN oben LINKS (MS;<text>) — User-Wunsch 2026-08-17.
      // Dieselben Zeilen wie die Dashboard-Karte „Meldungen"; der Bot setzt sie
      // zusammen und deckelt auf 5. Hier nur gestapelt darstellen.
      // ⚠ Eigener Praefix MSPFX, damit sie beim Redraw sauber verschwinden, wenn
      //   der Bot keine MS-Zeile mehr schickt (sonst blieben Alt-Meldungen
      //   stehen — genau der Fehler, den die HL-Pfeile schon einmal hatten).
      if(typ == "MS")
      {
         if(!InpShowMeldungen || k < 2) continue;
         string mn = PFX + "MSG_" + IntegerToString(msg_idx);
         if(ObjectFind(0, mn) < 0)
         {
            ObjectCreate(0, mn, OBJ_LABEL, 0, 0, 0);
            ObjectSetInteger(0, mn, OBJPROP_CORNER, CORNER_LEFT_UPPER);
            // ⚠ ZENTRIERT (User-Wunsch 2026-08-19). MT5 kennt keine „Mitte"-Ecke:
            //   der Bezugspunkt bleibt LINKS OBEN, aber der ANKER wird auf
            //   ANCHOR_UPPER (Text-Oberkante MITTIG) gesetzt und der X-Abstand
            //   auf die halbe Chartbreite gelegt. Damit waechst der Text nach
            //   beiden Seiten und bleibt bei jeder Fensterbreite zentriert.
            ObjectSetInteger(0, mn, OBJPROP_ANCHOR, ANCHOR_UPPER);
            ObjectSetString (0, mn, OBJPROP_FONT, "Arial");
            ObjectSetInteger(0, mn, OBJPROP_FONTSIZE, InpMeldungSize);
            ObjectSetInteger(0, mn, OBJPROP_SELECTABLE, false);
            ObjectSetInteger(0, mn, OBJPROP_BACK, false);
         }
         // ⚠⚠ POSITION KORRIGIERT (User 2026-08-19: „die Anzeigetexte oben links
         //   ueberlagern sich"). ZWEI Fehler steckten im alten `16 + i*(size+6)`:
         //   (1) START zu hoch: MT5 zeichnet oben links SELBST die Symbol-/
         //       Zeitrahmen-Beschriftung ("SpotCrude,M5") und ggf. die
         //       OHLC-Zeile. y=16 landete mitten darin. Der Beleg war, dass sich
         //       etwas ueberlagerte, obwohl nur EINE MS-Zeile aktiv war — es
         //       konnten also gar nicht zwei Meldungen sein.
         //   (2) ABSTAND zu knapp: `OBJPROP_FONTSIZE` ist in PUNKT, nicht Pixel.
         //       10 pt rendern je nach DPI-Skalierung 16-20 px hoch — bei 16 px
         //       Zeilenabstand ueberlappen mehrere Meldungen zwangslaeufig.
         //       Jetzt Faktor 2,0 (10 pt -> 20 px), das traegt auch 125 % DPI.
         // ⚠ X JEDEN Redraw neu setzen, nicht nur beim Erzeugen: die Chartbreite
         //   aendert sich beim Groessenaendern des Fensters, sonst bliebe die
         //   Zentrierung auf der alten Breite haengen.
         ObjectSetInteger(0, mn, OBJPROP_XDISTANCE,
                          (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2);
         ObjectSetInteger(0, mn, OBJPROP_YDISTANCE,
                          InpMeldungY + msg_idx * msg_h);
         // ⚠⚠ FONTSIZE JEDEN Redraw setzen, nicht nur beim Erzeugen (v1.44).
         //   Genau daran scheiterte v1.43: ein bereits existierendes Label
         //   behaelt seine Schriftgroesse, die Erhoehung 10 -> 11 pt kam auf
         //   dem Chart also nie an. Gleiche Klasse wie die X-Position darueber.
         ObjectSetInteger(0, mn, OBJPROP_FONTSIZE, InpMeldungSize);
         ObjectSetString (0, mn, OBJPROP_TEXT, p[1]);
         ObjectSetInteger(0, mn, OBJPROP_COLOR, InpMeldungColor);
         msg_idx++;
         continue;
      }
      // G/V der offenen Position — direkt UNTER dem Kurs (User-Wunsch 2026-07-30):
      // PL;wert;waehrung. Wert ist NETTO (Bot rechnet die WHT-Quellensteuer schon
      // raus, wie im Dashboard). Gruen im Plus, rot im Minus. Ohne offene Position
      // schickt der Bot keine PL-Zeile -> das Label wird unten geloescht.
      if(typ == "PL")
      {
         if(k < 2) continue;
         if(!InpShowPnL) continue;
         double plv = StringToDouble(p[1]);
         string cur = (k > 2) ? p[2] : "";
         string qn = PFX + "PNL";
         if(ObjectFind(0, qn) < 0)
         {
            ObjectCreate(0, qn, OBJ_LABEL, 0, 0, 0);
            ObjectSetInteger(0, qn, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
            ObjectSetInteger(0, qn, OBJPROP_XDISTANCE, 12);
            // direkt unter dem Kurs: dessen Y (14) + Schrifthoehe + kleiner Abstand
            // Y wird unten jeden Redraw gesetzt
            ObjectSetInteger(0, qn, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
            ObjectSetString (0, qn, OBJPROP_FONT, "Arial Black");
            ObjectSetInteger(0, qn, OBJPROP_SELECTABLE, false);
         }
         ObjectSetInteger(0, qn, OBJPROP_FONTSIZE, (int)MathMax(8, InpPriceSize - 4));
         ObjectSetInteger(0, qn, OBJPROP_YDISTANCE,
                          14 + LabelH("Arial Black", InpPriceSize));
         ObjectSetString (0, qn, OBJPROP_TEXT,
                          (plv >= 0 ? "+" : "") + DoubleToString(plv, 2)
                          + (StringLen(cur) > 0 ? " " + cur : ""));
         ObjectSetInteger(0, qn, OBJPROP_COLOR,
                          (plv >= 0 ? InpPnLUpColor : InpPnLDownColor));
         continue;
      }
      // TRADE-MARKER: TR;<O|C>;<broker_time> → die Kerze, in der eröffnet bzw.
      // geschlossen wurde, wird umrandet (User-Vorgabe 2026-07-30):
      //   O = Eröffnung → HELLGRÜN · C = Close → DUNKELGRÜN
      // Umrandung = OBJ_RECTANGLE über die Bar-Spanne (Zeit) und High/Low (Preis),
      // ungefüllt. Die Zeit wird auf den Bar-Beginn der aktuellen Chart-TF gerundet,
      // damit die Box exakt auf einer Kerze sitzt — egal auf welcher TF man schaut.
      if(typ == "TR")
      {
         if(k < 3) continue;
         if(!InpShowTrades) continue;
         string tkind = p[1];
         datetime tts = (datetime)StringToInteger(p[2]);
         if(tts <= 0) continue;
         int bi = iBarShift(_Symbol, PERIOD_CURRENT, tts, false);
         if(bi < 0) continue;                       // Zeit liegt außerhalb der History
         datetime bt = iTime(_Symbol, PERIOD_CURRENT, bi);
         double bh = iHigh(_Symbol, PERIOD_CURRENT, bi);
         double bl = iLow (_Symbol, PERIOD_CURRENT, bi);
         if(bh <= 0 || bl <= 0) continue;
         double pad = (bh - bl) * 0.12;             // etwas Luft um die Kerze
         if(pad <= 0) pad = _Point * 10;
         string tn2 = PFX + "TRD_" + tkind + "_" + IntegerToString((int)tts);
         if(ObjectCreate(0, tn2, OBJ_RECTANGLE, 0, bt, bl - pad,
                         bt + PeriodSeconds(), bh + pad))
         {
            ObjectSetInteger(0, tn2, OBJPROP_COLOR,
                             (tkind == "O" ? InpTradeOpenColor : InpTradeCloseColor));
            ObjectSetInteger(0, tn2, OBJPROP_STYLE, STYLE_SOLID);
            ObjectSetInteger(0, tn2, OBJPROP_WIDTH, 2);
            ObjectSetInteger(0, tn2, OBJPROP_FILL, false);
            ObjectSetInteger(0, tn2, OBJPROP_BACK, false);
            ObjectSetInteger(0, tn2, OBJPROP_SELECTABLE, false);
            ObjectSetString (0, tn2, OBJPROP_TOOLTIP,
                             (tkind == "O" ? "Trade EROEFFNET" : "Trade GESCHLOSSEN"));
         }
         continue;
      }
      // Liquiditäts-TRENDLINIE: LT;t1;p1;t2;p2;<U|D>  (pink, nach rechts verlängert).
      // Regression über den VOLUMENGEWICHTETEN Schwerpunkt der beiden Wände über die
      // letzten ~60 min: zeigt, wohin das Gewicht der RUHENDEN Orders wandert —
      // NICHT wohin der gehandelte Kurs läuft. Steigt sie, ziehen die Käufer ihre
      // großen Gebote hoch. Zeiten kommen bereits in Broker-Zeit aus dem Bot.
      if(typ == "LT")
      {
         if(k < 5) continue;
         if(!InpShowLiqTrend) continue;
         datetime t1 = (datetime)StringToInteger(p[1]);
         double   q1 = StringToDouble(p[2]);
         datetime t2 = (datetime)StringToInteger(p[3]);
         double   q2 = StringToDouble(p[4]);
         string   td = (k > 5) ? p[5] : "";
         if(t1 <= 0 || t2 <= 0 || q1 <= 0 || q2 <= 0) continue;
         string nm = PFX + "LIQTREND";
         if(ObjectCreate(0, nm, OBJ_TREND, 0, t1, q1, t2, q2))
         {
            ObjectSetInteger(0, nm, OBJPROP_COLOR, InpTrendLineColor);
            ObjectSetInteger(0, nm, OBJPROP_STYLE, STYLE_SOLID);
            ObjectSetInteger(0, nm, OBJPROP_WIDTH, 2);
            // ⚠ KEIN RAY_RIGHT: eine 60-min-Regression 100 Bars in die Zukunft zu
            // verlängern ergibt keinen Sinn und riss die Linie optisch ins
            // Bodenlose (User-Feedback „das Bild sieht komisch aus", 2026-07-30).
            // Die Linie endet jetzt am aktuellen Rand.
            ObjectSetInteger(0, nm, OBJPROP_RAY_RIGHT, false);
            ObjectSetInteger(0, nm, OBJPROP_RAY_LEFT,  false);
            ObjectSetInteger(0, nm, OBJPROP_BACK, true);
            ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
         }
         else
         {
            ObjectMove(0, nm, 0, t1, q1);
            ObjectMove(0, nm, 1, t2, q2);
            ObjectSetInteger(0, nm, OBJPROP_RAY_RIGHT, false);
         }
         // Beschriftung der Liquiditäts-Trendlinie: auf User-Wunsch (2026-07-30)
         // per Default AUS — der Chart soll sauber bleiben. Die Richtung liest man
         // an der Linie selbst ab; Details stehen im Tooltip der Linie.
         string tl = PFX + "TLIQTREND";
         string tt = "Liquiditaets-Trend " + (td == "U" ? CharToString(94) : CharToString(118));
         ObjectSetString(0, nm, OBJPROP_TOOLTIP, tt);
         if(InpLiqTrendLabel)
         {
            if(ObjectCreate(0, tl, OBJ_TEXT, 0, t2, q2))
            {
               ObjectSetString (0, tl, OBJPROP_TEXT, tt);
               ObjectSetInteger(0, tl, OBJPROP_COLOR, InpLabelColor);
               ObjectSetInteger(0, tl, OBJPROP_FONTSIZE, InpLabelSize);
               ObjectSetInteger(0, tl, OBJPROP_ANCHOR, ANCHOR_LOWER);
               ObjectSetInteger(0, tl, OBJPROP_SELECTABLE, false);
            }
            else
            {
               ObjectMove(0, tl, 0, t2, q2);
               ObjectSetString(0, tl, OBJPROP_TEXT, tt);
            }
         }
         else
            ObjectDelete(0, tl);          // evtl. Alt-Label aus v1.13–1.15 entfernen
         continue;
      }
      // ("Z;"-Zonen-Zeilen schreibt der Bot nicht mehr — die dynamische Handels-
      //  Range zeichnet DrawRange() selbst aus den Chart-Kerzen.)
   }
   ChartRedraw();
}
//+------------------------------------------------------------------+
