MT5-Chart v1.18: Trade-Marker, Liquiditaetszonen, Kurs oben rechts + Pfeil-Bugfix

Pfeil-Bugfix (User: "liquiditaetspfeile werden immer noch nicht angezeigt"):
  (1) ANCHOR_LEFT ist fuer OBJ_ARROW UNGUELTIG (nur TOP/BOTTOM)
  (2) Pfeile sitzen rechts neben der letzten Kerze -> ohne CHART_SHIFT kein
      sichtbarer Bereich dort
Blink-Fix (User: "blinken nur manchmal auf"): hl_walls setzte den Cache bei jedem
Fehlschlag auf None -> LQ-Zeilen fehlten -> Indikator loeschte die Pfeile. Jetzt
letzter guter Stand 120s Grace.

Neu (alles reine Anzeige):
- TR;<O|C>;zeit  -> Kerze der Eroeffnung HELLGRUEN, des Close DUNKELGRUEN umrandet
- EQ;<H|L>;lo;hi;zeit -> Equal Highs/Lows aus M30-Pivots als goldenes Band +
  gestrichelte Kante (wie im User-Referenzbild). Als Signal gemessen verworfen.
- PX;bid -> aktueller Kurs fett oben rechts
- Beschriftung der Liquiditaets-Trendlinie per Default aus (v1.16)

Alles kompiliert und live in sr_levels.csv verifiziert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-07-30 18:36:22 +02:00
co-authored by Claude Opus 4.8
parent b75997d703
commit 03567adf6b
5 changed files with 280 additions and 14 deletions
+24 -1
View File
@@ -1202,7 +1202,7 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
Daten älter als 60 s werden verworfen. **REINE ANZEIGE** — ob HL-Buch-Signale auf den Daten älter als 60 s werden verworfen. **REINE ANZEIGE** — ob HL-Buch-Signale auf den
CFD übertragbar sind, wird gerade gemessen (`analyze_bookflow.py` im HL-Projekt, CFD übertragbar sind, wird gerade gemessen (`analyze_bookflow.py` im HL-Projekt,
Datensammlung seit 2026-07-30, Urteil in ~2 Wochen). Der Indikator Datensammlung seit 2026-07-30, Urteil in ~2 Wochen). Der Indikator
**`mql5/SR_Levels.mq5`** (**v1.15** — `LQ`-Handler zeichnet seit v1.14 **nur noch **`mql5/SR_Levels.mq5`** (**v1.18** — `LQ`-Handler zeichnet seit v1.14 **nur noch
den PFEIL**, rechtsbündig am aktuellen Rand (in `RepositionLabels()` nachgeführt, den PFEIL**, rechtsbündig am aktuellen Rand (in `RepositionLabels()` nachgeführt,
bleibt beim Scrollen rechts; Details im **Tooltip**) — Linie und Text-Label sind bleibt beim Scrollen rechts; Details im **Tooltip**) — Linie und Text-Label sind
auf User-Wunsch raus („Chart zu voll"). **v1.15 = CHART-THEME** (User „Farben im auf User-Wunsch raus („Chart zu voll"). **v1.15 = CHART-THEME** (User „Farben im
@@ -1417,6 +1417,29 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
wie Squeeze vs. beliebige Box); ohne die ist „Muster wirkt" nicht belegt. Muster wie Squeeze vs. beliebige Box); ohne die ist „Muster wirkt" nicht belegt. Muster
bleiben **reine Anzeige**. Ziel%-Spalte = der ehrliche Muster-Check (1338 % = Muster bleiben **reine Anzeige**. Ziel%-Spalte = der ehrliche Muster-Check (1338 % = Muster
sagen den Kursverlauf kaum voraus). sagen den Kursverlauf kaum voraus).
- **MQL5-Chart-Erweiterungen 2026-07-30 (Indikator v1.16v1.18, alles REINE ANZEIGE):**
· **Trade-Marker** `TR;<O|C>;<broker_time>` — die Kerze der **Eröffnung wird
HELLGRÜN**, die des **Close DUNKELGRÜN** umrandet (OBJ_RECTANGLE über Bar-Spanne +
High/Low, `iBarShift` → sitzt auf jeder Chart-TF korrekt). Quelle: offene Position
(`trader.open_time`) + die letzten `_TRADE_MARKS`=6 geschlossenen Trades.
· **Liquiditätszonen** `EQ;<H|L>;lo;hi;<broker_time>`**Equal Highs / Equal Lows**
aus den M30-Pivots (zwei Pivots innerhalb `_EQ_TOL_ATR`=0,15×ATR, 240 Bars
auseinander) als **goldenes Band** + gestrichelte Kante, nach rechts verlängert
(Darstellung nach User-Referenzbild). Berechnung in `wave_rec` (`_liq_zones`,
Snapshot `liq_zones`), Mindesthöhe `_EQ_MIN_H_ATR`=0,10×ATR. ⚠ **Als SIGNAL
gemessen und verworfen** (`backtest_liquidity_sweep.py`) — nur Kontext.
· **Aktueller Kurs** `PX;bid` — fett oben rechts (OBJ_LABEL, CORNER_RIGHT_UPPER,
Arial Black, `InpShowPrice`/`InpPriceSize`/`InpPriceColor`).
· ⚠ **Zwei Fallen beim Pfeil-Bug** („Pfeile werden nicht angezeigt", v1.17):
(1) **`ANCHOR_LEFT` ist für `OBJ_ARROW` UNGÜLTIG** — erlaubt sind nur `ANCHOR_TOP`/
`ANCHOR_BOTTOM`; (2) die Pfeile sitzen rechts NEBEN der letzten Kerze → ohne
**`CHART_SHIFT=true`** gibt es dort keinen sichtbaren Bereich. Beides im Theme/
Handler behoben.
· ⚠ **Blink-Fix in `core/hl_walls.py`** („Pfeile blinken nur manchmal auf"): bei
einem Fehlschlag (HTTP-Timeout, HL-Server kurz busy) wurde der Cache auf `None`
gesetzt → `LQ`-Zeilen fehlten in der CSV → der Indikator löschte die Pfeile beim
Redraw. Jetzt wird der **letzte gute Stand `_STALE_GRACE_S`=120 s weitergereicht**;
erst danach verschwinden die Pfeile (echter Ausfall).
- **Kurslücken-Kachel** (`#card-gaps`): offene D1-Gaps des laufenden Jahres - **Kurslücken-Kachel** (`#card-gaps`): offene D1-Gaps des laufenden Jahres
(`gaps.GapAnalyzer`, Refresh ~10 min im Trend-Loop unter `mt5_lock`). Je Gap: (`gaps.GapAnalyzer`, Refresh ~10 min im Trend-Loop unter `mt5_lock`). Je Gap:
Richtung, Zone, Fill-Level, Abstand zum Kurs. Die **Fill-Level fließen als Richtung, Zone, Fill-Level, Abstand zum Kurs. Die **Fill-Level fließen als
+41
View File
@@ -75,6 +75,8 @@ _SQUEEZE_REENTRY_COOLDOWN_S = 180.0 # nach JEDEM Close eines Trades (Notfall/SL
# gegen die Open→Instant-Close-Kaskade bei zu engem Stop # gegen die Open→Instant-Close-Kaskade bei zu engem Stop
# (real 2026-07-27 15:39: 3-€-Stop killte Entry #1 in 16 s, # (real 2026-07-27 15:39: 3-€-Stop killte Entry #1 in 16 s,
# Squeeze noch aktiv → sofortiger Re-Entry; 2026-07-28) # Squeeze noch aktiv → sofortiger Re-Entry; 2026-07-28)
_TRADE_MARKS = 6 # so viele letzte Trades im MQL5-Chart markieren
# (Kerze umranden: Eröffnung hellgrün, Close dunkelgrün)
_STARTUP_GRACE_S = 60.0 # nach Bot-Start schließt der Bot 60 s lang KEINEN _STARTUP_GRACE_S = 60.0 # nach Bot-Start schließt der Bot 60 s lang KEINEN
# laufenden Trade server-seitig (S/R-Close/Notfall/ # laufenden Trade server-seitig (S/R-Close/Notfall/
# Time-Stop/Reverse) — die Daten (Preis/ATR/P(break)/ # Time-Stop/Reverse) — die Daten (Preis/ATR/P(break)/
@@ -1984,6 +1986,45 @@ class TradingEngine:
f"{'U' if tr.get('dir')=='up' else 'D'}") f"{'U' if tr.get('dir')=='up' else 'D'}")
except Exception: except Exception:
pass pass
# Liquiditätszonen (Equal Highs/Lows, M30): EQ;<H|L>;lo;hi;<broker_time>
# → goldenes Band ab dem ersten Pivot, nach rechts verlängert.
# ⚠ REINE ANZEIGE (als Signal gemessen verworfen, s. backtest_liquidity_sweep).
try:
zs = (self.wave.snapshot() or {}).get("liq_zones") or {}
boff_z = self.trader._broker_offset_s()
for key, tag in (("eqh", "H"), ("eql", "L")):
z = zs.get(key)
if z and z.get("lo") and z.get("hi"):
# `t` ist ROHE Broker-Zeit aus copy_rates → NICHT nochmal
# umrechnen (anders als die HL-Trendlinie, die lokal tickt).
lines.append(f"EQ;{tag};{z['lo']};{z['hi']};{int(z['t'])}")
except Exception:
pass
# Aktueller Kurs für die fette Anzeige oben rechts (User-Wunsch): PX;bid
try:
_bid = (self.data.snapshot() or {}).get("bid")
if _bid:
lines.append(f"PX;{_bid}")
except Exception:
pass
# Trade-Marker: TR;<O|C>;<broker_time> → der Indikator umrandet die Kerze,
# in der eröffnet (hellgrün) bzw. geschlossen (dunkelgrün) wurde
# (User-Vorgabe 2026-07-30). Offene Position + die letzten Trades des Tages.
try:
boff = self.trader._broker_offset_s()
marks: list[tuple[str, int]] = []
ps = self.trader.snapshot()
if ps.get("ticket") and ps.get("open_time"):
marks.append(("O", int(float(ps["open_time"]) + boff)))
for t in (self.history.last_closed_trades(_TRADE_MARKS) or []):
if t.get("entry_time"):
marks.append(("O", int(float(t["entry_time"]) + boff)))
if t.get("exit_time"):
marks.append(("C", int(float(t["exit_time"]) + boff)))
for kind, ts in marks[:2 * _TRADE_MARKS + 1]:
lines.append(f"TR;{kind};{ts}")
except Exception:
pass
# Zonen NICHT mehr aus der (statischen) [zones]-Config — die dynamische # Zonen NICHT mehr aus der (statischen) [zones]-Config — die dynamische
# Handels-Range zeichnet der Indikator selbst aus den Chart-Kerzen (TF-genau). # Handels-Range zeichnet der Indikator selbst aus den Chart-Kerzen (TF-genau).
path = os.path.join(self._mql5_files_dir, "sr_levels.csv") path = os.path.join(self._mql5_files_dir, "sr_levels.csv")
+16
View File
@@ -34,6 +34,8 @@ log = get_logger("hlwalls")
_TIMEOUT_S = 2.0 # der MT5-Loop darf nicht am HTTP hängen _TIMEOUT_S = 2.0 # der MT5-Loop darf nicht am HTTP hängen
_TTL_S = 10.0 # Cache — die Wände wandern langsamer als der Export-Takt (~5 s) _TTL_S = 10.0 # Cache — die Wände wandern langsamer als der Export-Takt (~5 s)
_MAX_AGE_S = 60.0 # älter = HL-Feed steht → nicht mehr zeichnen _MAX_AGE_S = 60.0 # älter = HL-Feed steht → nicht mehr zeichnen
_STALE_GRACE_S = 120.0 # nach einem Fehlschlag den letzten guten Stand so lange
# weiterreichen (Blink-Fix — s. Kommentar in `walls()`)
class HLWalls: class HLWalls:
@@ -85,6 +87,20 @@ class HLWalls:
f"Liquiditätslinien (HL-Dashboard auf 8001 läuft nicht?)") f"Liquiditätslinien (HL-Dashboard auf 8001 läuft nicht?)")
self._fail_logged = True self._fail_logged = True
with self._lock: with self._lock:
if data is not None:
self._last_good = data
self._last_good_ts = now
elif (getattr(self, "_last_good", None) is not None
and now - getattr(self, "_last_good_ts", 0.0) < _STALE_GRACE_S):
# ⚠ BLINK-FIX (2026-07-30, User „die Liquidationspfeile blinken nur
# manchmal auf"): ein einzelner Fehlschlag (HTTP-Timeout, HL-Server
# kurz busy, Snapshot minimal zu alt) darf die Pfeile NICHT löschen.
# Ohne den letzten guten Wert fehlen die LQ-Zeilen in der CSV → der
# Indikator löscht beim Redraw alle Objekte und zeichnet sie nicht neu
# → Pfeil weg, beim nächsten Erfolg wieder da = sichtbares Blinken.
# Innerhalb der Grace wird der letzte gute Stand weitergereicht;
# erst danach verschwinden die Pfeile wirklich (echter Ausfall).
data = self._last_good
self._cache = data self._cache = data
self._cache_ts = now self._cache_ts = now
return data return data
+37
View File
@@ -80,6 +80,8 @@ _SQ_REFRESH_S = 5.0 # eigener, schneller M5-Fetch NUR für den Squeeze (entkoppe
# raus (z. B. Short im M30-Aufwärtstrend). Nur für Basis-TF < M30 aktiv. # raus (z. B. Short im M30-Aufwärtstrend). Nur für Basis-TF < M30 aktiv.
_HTF = mt5.TIMEFRAME_M30 _HTF = mt5.TIMEFRAME_M30
_HTF_LABEL = "M30" _HTF_LABEL = "M30"
_EQ_TOL_ATR = 0.15 # „gleich" für Equal Highs/Lows: beide Pivots innerhalb x×ATR
_EQ_MIN_H_ATR = 0.10 # Mindest-Bandhöhe der Liquiditätszone (sonst unsichtbar dünn)
_HTF_DEADBAND = 0.15 # |EMA12EMA50| < x×ATR(M30) → HTF gilt als neutral _HTF_DEADBAND = 0.15 # |EMA12EMA50| < x×ATR(M30) → HTF gilt als neutral
# Multi-TF-Konfluenz (nur Konfidenz/Anzeige): wenn M30 UND H1 die Richtung # Multi-TF-Konfluenz (nur Konfidenz/Anzeige): wenn M30 UND H1 die Richtung
@@ -296,6 +298,10 @@ class WaveRecommender:
if _lbl == "M5": if _lbl == "M5":
self._m5_hl = (_h, _l) self._m5_hl = (_h, _l)
elif _lbl == "M30": elif _lbl == "M30":
# Bar-ZEITEN mit ablegen — die Liquiditätszonen (Equal
# Highs/Lows) brauchen den Zeitpunkt der beiden Pivots,
# um im Chart als Band gezeichnet werden zu können.
self._m30_t = [int(b["time"]) for b in _tb]
# M30-H/L für die S/R-LEVEL (Chart + P(break)-Auto-Close). # M30-H/L für die S/R-LEVEL (Chart + P(break)-Auto-Close).
# Gemessen 2026-07-30 (`backtest_level_tf.py`): M30-Level # Gemessen 2026-07-30 (`backtest_level_tf.py`): M30-Level
# schlagen M5 in BEIDEN Hälften (R-Ertrag ~2×, Level 1,95 # schlagen M5 in BEIDEN Hälften (R-Ertrag ~2×, Level 1,95
@@ -398,6 +404,35 @@ class WaveRecommender:
pl30 = [round(_ls[j], 3) for j in range(_K, len(_ls) - _K) pl30 = [round(_ls[j], 3) for j in range(_K, len(_ls) - _K)
if _ls[j] == min(_ls[j - _K:j + _K + 1])] if _ls[j] == min(_ls[j - _K:j + _K + 1])]
self._pb_levels30 = {"ph": ph30, "pl": pl30} self._pb_levels30 = {"ph": ph30, "pl": pl30}
# ── Liquiditätszonen: EQUAL HIGHS / EQUAL LOWS (SMC, User-Wunsch
# 2026-07-30 „zeichne mir die Liquiditätszonen wie im Bild ein").
# Zwei ~gleiche M30-Pivots (innerhalb _EQ_TOL×ATR, 240 Bars
# auseinander) = Stop-Cluster. ⚠ REINE ANZEIGE — als SIGNAL gemessen
# und verworfen (`backtest_liquidity_sweep.py`: Equal Highs/Lows
# fallen in fast allen Varianten durch, der SMC-Exit sogar überall).
_a30 = turn_data.get("M30", (None, None))[1] if turn_data else None
_t30 = getattr(self, "_m30_t", None)
if _a30 and _a30 > 0 and _t30 and len(_t30) >= len(_hs):
_tol = _EQ_TOL_ATR * _a30
_toff = len(_t30) - len(_hs) # Index-Versatz Fenster→Voll-Liste
def _equal(vals, want_high):
# jüngstes Paar zuerst suchen (rückwärts)
idx = [j for j in range(_K, len(vals) - _K)
if ((vals[j] == max(vals[j - _K:j + _K + 1])) if want_high
else (vals[j] == min(vals[j - _K:j + _K + 1])))]
for a in range(len(idx) - 1, 0, -1):
for b in range(a - 1, -1, -1):
gap = idx[a] - idx[b]
if gap < 2: continue
if gap > 40: break
if abs(vals[idx[a]] - vals[idx[b]]) <= _tol:
lo = min(vals[idx[a]], vals[idx[b]])
hi = max(vals[idx[a]], vals[idx[b]])
pad = max(0.0, (_EQ_MIN_H_ATR * _a30 - (hi - lo)) / 2)
return {"lo": round(lo - pad, 3), "hi": round(hi + pad, 3),
"t": int(_t30[idx[b] + _toff])}
return None
self._liq_zones = {"eqh": _equal(_hs, True), "eql": _equal(_ls, False)}
# Multi-TF-Bounce: stärksten Status über M1/M5/M15/M30 wählen (H1 ist NUR # Multi-TF-Bounce: stärksten Status über M1/M5/M15/M30 wählen (H1 ist NUR
# für die Ampel im turn_data, NICHT im gemessen-neutralen Bounce → skip). # für die Ampel im turn_data, NICHT im gemessen-neutralen Bounce → skip).
cands = [] cands = []
@@ -420,6 +455,8 @@ class WaveRecommender:
# M30-Level = Quelle für Chart-Linien UND P(break)-Auto-Close (gemessen besser); # M30-Level = Quelle für Chart-Linien UND P(break)-Auto-Close (gemessen besser);
# `pb_levels` (M5) bleibt für das Entry-Raum-Gate. # `pb_levels` (M5) bleibt für das Entry-Raum-Gate.
snap["pb_levels30"] = dict(self._pb_levels30) if getattr(self, "_pb_levels30", None) else None snap["pb_levels30"] = dict(self._pb_levels30) if getattr(self, "_pb_levels30", None) else None
# Liquiditätszonen (Equal Highs/Lows, M30) — REINE ANZEIGE (Signal verworfen)
snap["liq_zones"] = dict(self._liq_zones) if getattr(self, "_liq_zones", None) else None
snap["squeeze"] = dict(self._squeeze) if getattr(self, "_squeeze", None) else \ snap["squeeze"] = dict(self._squeeze) if getattr(self, "_squeeze", None) else \
{"state": None, "dir": None, "level": None, "box_atr": None} {"state": None, "dir": None, "level": None, "box_atr": None}
# ── Entry-Raum-Gate (gemessen `backtest_entryroom.py`, beide Hälften # ── Entry-Raum-Gate (gemessen `backtest_entryroom.py`, beide Hälften
+162 -13
View File
@@ -12,7 +12,21 @@
//| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). | //| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). |
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+
#property copyright "Oil Trading Bot" #property copyright "Oil Trading Bot"
#property version "1.15" #property version "1.18"
// 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 // v1.15 (2026-07-30): CHART-THEME (User: „Farben im MT5 wie im Bild") — setzt beim
// Start Hintergrund/Gitter/Kerzenfarben auf die Dashboard-Palette // Start Hintergrund/Gitter/Kerzenfarben auf die Dashboard-Palette
// (Bull #3B82F6, Bear #EF4444, BG #0D1117, Gitter #10151C dezent). // (Bull #3B82F6, Bear #EF4444, BG #0D1117, Gitter #10151C dezent).
@@ -56,6 +70,15 @@ input color InpWallBidColor = C'245,158,11'; // Liquidität Nachfrage (Bid)
input color InpWallAskColor = C'245,158,11'; // Liquidität Angebot (Ask) — Gold input color InpWallAskColor = C'245,158,11'; // Liquidität Angebot (Ask) — Gold
input bool InpShowLiqTrend = true; // Liquiditäts-Trendlinie zeichnen input bool InpShowLiqTrend = true; // Liquiditäts-Trendlinie zeichnen
input color InpLiqTrendColor = clrMagenta; // Liquiditäts-Trend (pink) input color InpLiqTrendColor = clrMagenta; // Liquiditäts-Trend (pink)
input bool InpLiqTrendLabel = false; // Beschriftung der Trendlinie (aus per User-Wunsch)
input bool InpShowTrades = true; // Trade-Kerzen umranden (Eroeffnung/Close)
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 InpLiqZoneColor = C'146,94,10'; // Zonen-Band (gedaempftes Gold)
input bool InpShowPrice = true; // aktuellen Kurs oben rechts anzeigen
input int InpPriceSize = 20; // Schriftgroesse des Kurses
input color InpPriceColor = C'230,237,243'; // Kurs-Farbe
// ── Chart-THEME (User-Vorgabe 2026-07-30 „Farben im MT5 wie im Bild") ──────── // ── 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 // 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'. // und die PWA gleich aussehen. Farben in MQL5-Notation C'R,G,B'.
@@ -107,6 +130,10 @@ void ApplyTheme()
ChartSetInteger(0, CHART_COLOR_ASK, InpThemeBear); ChartSetInteger(0, CHART_COLOR_ASK, InpThemeBear);
ChartSetInteger(0, CHART_COLOR_STOP_LEVEL, C'239,68,68'); ChartSetInteger(0, CHART_COLOR_STOP_LEVEL, C'239,68,68');
ChartSetInteger(0, CHART_SHOW_PERIOD_SEP, false); 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);
ChartRedraw(); ChartRedraw();
} }
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+
@@ -354,7 +381,10 @@ void Redraw()
ObjectSetInteger(0, an, OBJPROP_ARROWCODE, (dirc == "U" ? 233 : 234)); ObjectSetInteger(0, an, OBJPROP_ARROWCODE, (dirc == "U" ? 233 : 234));
ObjectSetInteger(0, an, OBJPROP_COLOR, lcol); ObjectSetInteger(0, an, OBJPROP_COLOR, lcol);
ObjectSetInteger(0, an, OBJPROP_WIDTH, (isDom ? 4 : 2)); ObjectSetInteger(0, an, OBJPROP_WIDTH, (isDom ? 4 : 2));
ObjectSetInteger(0, an, OBJPROP_ANCHOR, ANCHOR_LEFT); // ⚠ 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); ObjectSetInteger(0, an, OBJPROP_SELECTABLE, false);
} }
else else
@@ -373,6 +403,116 @@ void Redraw()
ObjectDelete(0, PFX + "TLQ_" + side); ObjectDelete(0, PFX + "TLQ_" + side);
continue; 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;
if(ObjectCreate(0, zn, OBJ_RECTANGLE, 0, zt, zlo, zend, zhi))
{
ObjectSetInteger(0, zn, OBJPROP_COLOR, InpLiqZoneColor);
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);
}
// 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, InpLiqZoneColor);
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;
}
// AKTUELLER KURS oben rechts, fett (User-Wunsch): PX;bid
if(typ == "PX")
{
if(k < 2) continue;
if(!InpShowPrice) continue;
double pxv = StringToDouble(p[1]);
if(pxv <= 0) 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);
ObjectSetInteger(0, pn, OBJPROP_YDISTANCE, 14);
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);
}
ObjectSetString (0, pn, OBJPROP_TEXT, DoubleToString(pxv, (int)_Digits));
ObjectSetInteger(0, pn, OBJPROP_COLOR, InpPriceColor);
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). // 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 // Regression über den VOLUMENGEWICHTETEN Schwerpunkt der beiden Wände über die
// letzten ~60 min: zeigt, wohin das Gewicht der RUHENDEN Orders wandert — // letzten ~60 min: zeigt, wohin das Gewicht der RUHENDEN Orders wandert —
@@ -404,21 +544,30 @@ void Redraw()
ObjectMove(0, nm, 0, t1, q1); ObjectMove(0, nm, 0, t1, q1);
ObjectMove(0, nm, 1, t2, q2); ObjectMove(0, nm, 1, t2, q2);
} }
// 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 tl = PFX + "TLIQTREND";
string tt = "Liquiditäts-Trend " + (td == "U" ? CharToString(94) : CharToString(118)); string tt = "Liquiditaets-Trend " + (td == "U" ? CharToString(94) : CharToString(118));
if(ObjectCreate(0, tl, OBJ_TEXT, 0, t2, q2)) ObjectSetString(0, nm, OBJPROP_TOOLTIP, tt);
if(InpLiqTrendLabel)
{ {
ObjectSetString (0, tl, OBJPROP_TEXT, tt); if(ObjectCreate(0, tl, OBJ_TEXT, 0, t2, q2))
ObjectSetInteger(0, tl, OBJPROP_COLOR, InpLiqTrendColor); {
ObjectSetInteger(0, tl, OBJPROP_FONTSIZE, 9); ObjectSetString (0, tl, OBJPROP_TEXT, tt);
ObjectSetInteger(0, tl, OBJPROP_ANCHOR, ANCHOR_LOWER); ObjectSetInteger(0, tl, OBJPROP_COLOR, InpLiqTrendColor);
ObjectSetInteger(0, tl, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, tl, OBJPROP_FONTSIZE, 9);
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 else
{ ObjectDelete(0, tl); // evtl. Alt-Label aus v1.131.15 entfernen
ObjectMove(0, tl, 0, t2, q2);
ObjectSetString(0, tl, OBJPROP_TEXT, tt);
}
continue; continue;
} }
// ("Z;"-Zonen-Zeilen schreibt der Bot nicht mehr — die dynamische Handels- // ("Z;"-Zonen-Zeilen schreibt der Bot nicht mehr — die dynamische Handels-