Files
AH-Oil-Trader/mql5/SR_Levels.mq5
T
Axel Hocks eba3feb46a MQL5 v1.46: PT-Muster wurden NIE gezeichnet - PERIOD_CURRENT ist 0
Das bestaetigte M30-Muster "Tasse+Henkel" blieb unsichtbar, obwohl die
CSV-Zeile korrekt vorlag (PT;M30;Tasse+Henkel;bull;87.810;88.746) und der
Snapshot status=confirmed meldete.

URSACHE: der TF-Filter verglich gegen TFName(PERIOD_CURRENT).
PERIOD_CURRENT hat den WERT 0. MT5-Funktionen wie iTime() deuten 0 als
"dieser Chart" - ein switch trifft damit aber KEINEN case, und TFName()
fiel auf EnumToString() zurueck und lieferte den String "PERIOD_CURRENT".
Der Vergleich p[1] != "PERIOD_CURRENT" war deshalb IMMER wahr, jede
PT-Zeile wurde uebersprungen. Auf JEDER Zeitebene, nicht nur M30.

FIX: TFName((ENUM_TIMEFRAMES)_Period) - _Period liefert die tatsaechliche
Chart-Periode als Wert.

Die uebrigen PERIOD_CURRENT-Verwendungen im Indikator sind korrekt: sie
stehen als Argument in iTime()/Bars(), wo MT5 die 0 selbst aufloest.

Kompiliert ins richtige Terminal (D0E8209F...), 0 errors 0 warnings,
Spiegel mql5/SR_Levels.mq5 bitgleich.
2026-08-21 13:09:19 +02:00

1296 lines
72 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| 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.46"
// 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)
// 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)
// 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. |
//+------------------------------------------------------------------+
//| 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. |
//| 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 4555 % „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.
// BESTAETIGTES CHARTMUSTER je Zeitebene (PT;<TF>;<name>;<dir>;<trigger>;<ziel>)
// — User-Wunsch 2026-08-21.
// ⚠⚠ Es wird NUR die Zeile gezeichnet, deren <TF> 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;
// ⚠⚠ `_Period`, NICHT `PERIOD_CURRENT`. PERIOD_CURRENT ist der Wert 0 —
// MT5-Funktionen wie iTime() deuten das als "dieser Chart", ein
// `switch` trifft damit aber KEINEN case und TFName() gab
// "PERIOD_CURRENT" zurueck. Der Vergleich schlug also IMMER fehl und
// es wurde NIE etwas gezeichnet (real am 21.08.: das bestaetigte
// M30-Muster blieb unsichtbar, obwohl die CSV-Zeile korrekt da war).
if(p[1] != TFName((ENUM_TIMEFRAMES)_Period)) 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;
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.131.15 entfernen
continue;
}
// ("Z;"-Zonen-Zeilen schreibt der Bot nicht mehr — die dynamische Handels-
// Range zeichnet DrawRange() selbst aus den Chart-Kerzen.)
}
ChartRedraw();
}
//+------------------------------------------------------------------+