From 263ec9f9a54fa95308358b05f0dd62a610e41bb5 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Fri, 21 Aug 2026 13:02:58 +0200 Subject: [PATCH] Bestaetigtes Chartmuster je Zeitebene ins MT5-Chart (Indikator v1.45) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-Wunsch: das bestaetigte Muster in das jeweils passende Chart einzeichnen. CSV: neue Zeile `PT;;;;;` je Zeitebene. INDIKATOR: zeichnet NUR die Zeile, deren zu SEINEM Chart passt (`TFName(PERIOD_CURRENT)`). Ohne diesen Filter laegen alle Zeitebenen uebereinander im selben Fenster und keine waere lesbar - so bekommt jedes Chartfenster (M1/M5/M15/M30/H1) sein eigenes Muster. Gezeichnet werden zwei waagerechte Linien: Trigger (Nackenlinie, durchgezogen, Breite 2) und Ziel (gestrichelt), gruen bei bullisch, rot bei baerisch, dazu ein Label an der Triggerlinie. ⚠⚠ NUR `confirmed`. "Bildet sich" ist genau der Zustand, in dem noch nichts gebrochen ist - ihn zu zeichnen waere eine Vorhersage. Live gegengeprueft: M30 "Tasse+Henkel" und H1 "Aufsteigendes Dreieck" stehen auf confirmed und sind in der CSV; der M5-"Steigende Keil" ist nur forming und wird korrekt NICHT exportiert. ⚠⚠ EHRLICHKEIT IM LABEL: die Muster-Klasse ist als SIGNAL gemessen und VERWORFEN - die Ziel-Trefferquote liegt bei nur 13-38 % (backtest_patterns.py). Deshalb steht "(trifft 13-38 %)" im Chart-Label. Eine gezeichnete Ziellinie ohne diesen Zusatz liest sich wie eine Prognose, und genau das ist sie nicht. Kein Verdict-Gewicht, kein Trade-Trigger. ⚠ Nur ZUSAMMENGESETZT aus `patterns_tf`, das der Snapshot ohnehin baut - kein zweiter Rechenweg (der Konsens-Pfeil-Fehler). ⚠ Der dokumentierte Fallstrick beachtet: kompiliert wurde ins RICHTIGE Terminal (D0E8209F..., das einzige mit sr_levels.csv), mit EIGENEM Logpfad, und geprueft wurde auf "Result: 0 errors, 0 warnings" PLUS frischen .ex5-Zeitstempel - nicht auf den Default-Log. Der erste Versuch lief ins Leere, weil der Editor-Pfad falsch war (Pepperstone-Ordner existiert nicht, es ist "MetaTrader 5"); das leere Log und die unveraenderte .ex5 haben es verraten. Quelle ins Repo gespiegelt (mql5/SR_Levels.mq5), Backup daneben. Abschaltbar: InpShowMuster=false; Farben ueber InpMusterBullColor/-BearColor. Pipeline A-G gruen, 97 Tests, deployt. Co-Authored-By: Claude Opus 5 --- core/engine.py | 27 + mql5/SR_Levels.mq5 | 83 +- mql5/SR_Levels.mq5.bak-2026-08-21 | 1208 +++++++++++++++++++++++++++++ 3 files changed, 1317 insertions(+), 1 deletion(-) create mode 100644 mql5/SR_Levels.mq5.bak-2026-08-21 diff --git a/core/engine.py b/core/engine.py index 8325687..819ccb8 100644 --- a/core/engine.py +++ b/core/engine.py @@ -3601,6 +3601,33 @@ class TradingEngine: lines.append(f"{_tag};{round(_lo2, 3)};{round(_hi2, 3)}") except Exception: pass + # ── BESTAETIGTES CHARTMUSTER je Zeitebene (User-Wunsch 2026-08-21) + # PT;;;;; + # Der Indikator zeichnet NUR die Zeile, deren zu SEINEM Chart + # passt — so bekommt jedes Fenster (M1/M5/M15/H1) sein eigenes + # Muster, ohne dass fuenf Linien uebereinander liegen. + # ⚠⚠ NUR `confirmed`. "Bildet sich" ist genau der Zustand, in dem noch + # nichts gebrochen ist; ihn zu zeichnen waere eine Vorhersage. + # ⚠⚠ EHRLICHKEIT IM LABEL: die Muster-Klasse ist als SIGNAL gemessen + # und VERWORFEN — die Ziel-Trefferquote liegt bei nur 13-38 % + # (`backtest_patterns.py`). Deshalb steht die Zahl im Chart mit + # dran. Eine gezeichnete Ziellinie ohne diesen Hinweis liest sich + # wie eine Prognose, und genau das ist sie nicht. + # ⚠ Nur zusammengesetzt aus `patterns_tf`, das der Snapshot ohnehin + # baut — kein zweiter Rechenweg (der Konsens-Pfeil-Fehler). + try: + for _tf, _m in (self._patterns_tf() or {}).items(): + if not _m or _m.get("status") != "confirmed": + continue + _trg, _zl = _m.get("trigger"), _m.get("target") + if not _trg or not _zl: + continue + _nm = str(_m.get("name") or "").replace(";", ",")[:28] + lines.append(f"PT;{_tf};{_nm};{_m.get('dir') or ''};" + f"{float(_trg):.3f};{float(_zl):.3f}") + except Exception as _pe: + log.debug(f"PT-Export: {_pe}") + # ── MELDUNGEN oben links (MS;) — User-Wunsch 2026-08-17 ──── # Dieselben Zeilen wie die Dashboard-Karte „Meldungen" (`#card-pos`). # ⚠⚠ NUR ZUSAMMENSETZEN, NICHT NEU RECHNEN. Der Konsens-Pfeil hat im diff --git a/mql5/SR_Levels.mq5 b/mql5/SR_Levels.mq5 index 1ac54de..9196026 100644 --- a/mql5/SR_Levels.mq5 +++ b/mql5/SR_Levels.mq5 @@ -12,7 +12,7 @@ //| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). | //+------------------------------------------------------------------+ #property copyright "Oil Trading Bot" -#property version "1.44" +#property version "1.45" // 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 @@ -271,6 +271,12 @@ 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) +// Bestaetigtes Chartmuster je Zeitebene (User-Wunsch 2026-08-21). +// ⚠ Gezeichnet wird NUR das Muster, dessen Zeitebene zum EIGENEN Chart passt. +input bool InpShowMuster = true; // bestaetigtes Chartmuster zeichnen +input color InpMusterBullColor = C'70,180,100';// bullisch +input color InpMusterBearColor = C'220,90,80'; // baerisch + 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) @@ -317,6 +323,25 @@ int OnInit() //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Farbe mit dem Chart-Hintergrund mischen — simuliert TRANSPARENZ. | +//+------------------------------------------------------------------+ +//| Zeitebene dieses Charts als Text ("M1","M5","M15","M30","H1" ...) | +//| ⚠ Muss zu den Schluesseln passen, die der Bot in `PT;` schreibt | +//| (`engine.patterns_tf`). Weicht die Schreibweise ab, wird still | +//| NICHTS gezeichnet — deshalb die Fallback-Zeile am Ende. | +//+------------------------------------------------------------------+ +string TFName(ENUM_TIMEFRAMES tf) +{ + switch(tf) + { + case PERIOD_M1: return "M1"; + case PERIOD_M5: return "M5"; + case PERIOD_M15: return "M15"; + case PERIOD_M30: return "M30"; + case PERIOD_H1: return "H1"; + } + return EnumToString(tf); +} + //| ⚠ 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. | @@ -981,6 +1006,62 @@ void Redraw() // 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. + // BESTAETIGTES CHARTMUSTER je Zeitebene (PT;;;;;) + // — User-Wunsch 2026-08-21. + // ⚠⚠ Es wird NUR die Zeile gezeichnet, deren zum EIGENEN Chart passt. + // Der Bot liefert alle Zeitebenen in derselben CSV; ohne diesen Filter + // laegen fuenf Musterpaare uebereinander und keines waere lesbar. + // So bekommt jedes Chartfenster (M1/M5/M15/H1) sein eigenes Muster. + // ⚠⚠ EHRLICHKEIT: die Ziel-Trefferquote dieser Muster-Klasse liegt gemessen + // bei nur 13-38 % (backtest_patterns.py). Sie steht im Label, weil eine + // gezeichnete Ziellinie sonst wie eine Prognose aussieht — und genau das + // ist sie nicht. + // ⚠ Nur `confirmed` kommt ueberhaupt an; "bildet sich" waere der Zustand, in + // dem noch nichts gebrochen ist. + if(typ == "PT") + { + if(!InpShowMuster || k < 6) continue; + if(p[1] != TFName(PERIOD_CURRENT)) continue; // fremde Zeitebene + double trg = StringToDouble(p[4]), zl = StringToDouble(p[5]); + if(trg <= 0 || zl <= 0) continue; + bool bull = (p[3] == "bull"); + color pc = bull ? InpMusterBullColor : InpMusterBearColor; + datetime pt0 = iTime(_Symbol, PERIOD_CURRENT, + MathMin(80, Bars(_Symbol, PERIOD_CURRENT) - 1)); + string ln1 = PFX + "MUST_TRG", ln2 = PFX + "MUST_ZIEL"; + if(ObjectFind(0, ln1) < 0) + { + ObjectCreate(0, ln1, OBJ_TREND, 0, pt0, trg, RightTime(), trg); + ObjectSetInteger(0, ln1, OBJPROP_RAY_RIGHT, false); + ObjectSetInteger(0, ln1, OBJPROP_WIDTH, 2); + ObjectSetInteger(0, ln1, OBJPROP_SELECTABLE, false); + } + if(ObjectFind(0, ln2) < 0) + { + ObjectCreate(0, ln2, OBJ_TREND, 0, pt0, zl, RightTime(), zl); + ObjectSetInteger(0, ln2, OBJPROP_RAY_RIGHT, false); + ObjectSetInteger(0, ln2, OBJPROP_STYLE, STYLE_DOT); + ObjectSetInteger(0, ln2, OBJPROP_SELECTABLE, false); + } + ObjectSetInteger(0, ln1, OBJPROP_COLOR, pc); + ObjectMove(0, ln1, 0, pt0, trg); ObjectMove(0, ln1, 1, RightTime(), trg); + ObjectSetInteger(0, ln2, OBJPROP_COLOR, pc); + ObjectMove(0, ln2, 0, pt0, zl); ObjectMove(0, ln2, 1, RightTime(), zl); + string plb = PFX + "MUST_LBL"; + if(ObjectFind(0, plb) < 0) + { + ObjectCreate(0, plb, OBJ_TEXT, 0, RightTime(), trg); + ObjectSetInteger(0, plb, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER); + ObjectSetInteger(0, plb, OBJPROP_FONTSIZE, InpLabelSize); + ObjectSetInteger(0, plb, OBJPROP_SELECTABLE, false); + } + ObjectSetString(0, plb, OBJPROP_TEXT, + (bull ? "^ " : "v ") + p[2] + " bestaetigt - Ziel " + + DoubleToString(zl, (int)_Digits) + " (trifft 13-38 %)"); + ObjectSetInteger(0, plb, OBJPROP_COLOR, pc); + ObjectMove(0, plb, 0, RightTime(), trg); + continue; + } if(typ == "RZ" || typ == "SZ") { if(!InpShowZonen || k < 3) continue; diff --git a/mql5/SR_Levels.mq5.bak-2026-08-21 b/mql5/SR_Levels.mq5.bak-2026-08-21 new file mode 100644 index 0000000..1ac54de --- /dev/null +++ b/mql5/SR_Levels.mq5.bak-2026-08-21 @@ -0,0 +1,1208 @@ +//+------------------------------------------------------------------+ +//| 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 \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;) — 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;;;;) 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;;;) — 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;;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;;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; — 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;;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;;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;;lo;hi; → 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;;; + // 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 = 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;;;;; + // 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;) — 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;; → 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; (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(); +} +//+------------------------------------------------------------------+