Entwicklung des Indikators „Market Memory Zones“: Wohin der Kurs wahrscheinlich zurückkehren wird
Inhaltsverzeichnis
- Einführung
- Überblick über die Indikatoren
- Die ersten Schritte
- Demo der „Market Memory Zones“
- Schlussfolgerung
Einführung
Vielen Händlern fällt es schwer, aussagekräftige Kursniveaus zu identifizieren, die für den Markt wirklich wichtig sind. Traditionelle Unterstützungs- und Widerstandslinien, nachlaufende Indikatoren und subjektive Zoneneinteilung führen oft zu unübersichtlichen Charts und widersprüchlichen Signalen. Infolgedessen reagieren Händler häufig zu spät auf Kursbewegungen, gehen Trades in Bereichen mit geringer Wahrscheinlichkeit ein oder interpretieren Marktrauschen fälschlicherweise als gültige Struktur. Dieses Dilemma führt zu Inkonsistenz, übermäßigem Handeln und mangelndem Vertrauen in die Entscheidungsfindung.
Der Ansatz der „Market Memory Zones“ geht dieses Problem an, indem er sich darauf konzentriert, wie sich der Markt tatsächlich bewegt und Spuren hinterlässt. Anstatt sich auf willkürliche Niveaus zu verlassen, identifiziert der Indikator objektiv Zonen, die durch starke Verschiebungen, strukturelle Übergänge und Liquiditätsereignisse gebildet werden, in denen sich der Preis mit Absicht bewegt hat. Diese Zonen stellen Bereiche dar, in denen der Markt statistisch gesehen dazu neigt, wieder zuzuschlagen, sodass Händler Reaktionen vorwegnehmen können, anstatt dem Preis hinterherzulaufen, was zu einer klareren Analyse, einem besseren Timing und einer disziplinierteren Handelsausführung führt.
Überblick über die Indikatoren: Market Memory Zones
Die Verdrängungszonen konzentrieren sich auf die Ermittlung von Gebieten, in denen sich die Preise mit großer Absicht und Dringlichkeit bewegt haben. Diese Zonen werden durch große, impulsive Kerzen gebildet, deren Reichweite deutlich über dem Durchschnitt für diesen Zeitraum liegt und oft ein Vielfaches der ATR übersteigt. Um sicherzustellen, dass es sich bei der Bewegung um eine echte Verschiebung und nicht um normale Volatilität handelt, sollte die Kerze nur eine minimale Überschneidung mit den umliegenden Kerzen aufweisen, d. h., der größte Teil ihres Bereichs liegt außerhalb des Bereichs der vorherigen Kerze. Diese Kombination aus erweiterter Spanne und geringer Überschneidung macht deutlich, in welchen Bereichen sich die Preise zu schnell bewegten und eine nicht ausgefüllte Aktivität hinterließen, die der Markt später wieder aufgreifen könnte.

Strukturübergangszonen werden in Schlüsselmomenten geschaffen, wenn der Markt sein Verhalten ändert, was gemeinhin als „Change of Character“ (CHoCH) bezeichnet wird. Diese Zonen bilden sich an dem Punkt, an dem eine vorherige Marktstruktur durchbrochen wird, z. B. wenn ein Swing-Hoch in einem Abwärtstrend oder ein Swing-Tief in einem Aufwärtstrend durchbrochen wird. Der Bereich um diesen Strukturbruch herum ist oft von Bedeutung, da er einen Übergang in der Kontrolle zwischen Käufern und Verkäufern darstellt, was ihn zu einer potenziellen Zone macht, in die der Preis zurückkehren kann, um diesen Wechsel neu zu bewerten.

Liquidity Sweep Origin Zones basieren auf der Idee, dass der Markt häufig offensichtliche Höchst- und Tiefststände ansteuert, um Liquidität zu sammeln, bevor er sich umkehrt. Diese Zonen werden durch Kerzen identifiziert, die über ein vorheriges Hoch oder unter ein vorheriges Tief ziehen und unmittelbar von einer Umkehr der Kursrichtung gefolgt werden. Der Bereich der Kerze, die den Liquiditäts-Sweep ausgelöst hat, wird als Zone markiert, da er häufig den Ursprung der Bewegung darstellt, an dem die intelligente Beteiligung in den Markt eintrat, was die Wahrscheinlichkeit künftiger Reaktionen erhöht, wenn der Preis diesen Bereich wieder erreicht.

Die ersten Schritte
//+------------------------------------------------------------------+ //| MarketMemoryZones.mq5 | //| GIT under Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/johnhlomohang/ | //+------------------------------------------------------------------+ #property copyright "GIT under Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/johnhlomohang/" #property version "1.00" #property indicator_chart_window #property indicator_chart_window #property indicator_buffers 0 #property indicator_plots 0 //+------------------------------------------------------------------+ //| Input Parameters | //+------------------------------------------------------------------+ input ENUM_TIMEFRAMES AnalysisTF = PERIOD_CURRENT; // Analysis Timeframe input int LookbackBars = 200; // Bars to analyze input double MinCandleSizeATR = 1.5; // Min candle size (ATR multiplier) input bool ShowDisplacementZones = true; // Show displacement zones input bool ShowUnfilledAreas = true; // Show unfilled price areas input bool ShowStructureZones = true; // Show structure transition zones input bool ShowLiquidityZones = true; // Show liquidity sweep origins input bool FilterByVolume = false; // Require volume spike input int MaxActiveZones = 15; // Max zones to show input bool ExtendZonesForward = true; // Extend zones forward input int ZoneOpacity = 20; // Zone opacity (0-100)
In diesem Abschnitt des Codes definieren wir die wichtigsten Einrichtungs- und Konfigurationsoptionen für den Indikator Market Memory Zones. Wir werden den Indikator so gestalten, dass er direkt im Chart-Fenster läuft, ohne Indikatorpuffer oder Plots zu verwenden, da er sich vollständig auf grafische Objekte wie Rechtecke zur Visualisierung von Zonen verlässt. Die Eingabeparameter geben dem Nutzer die volle Kontrolle darüber, wie die Analyse durchgeführt wird, einschließlich des Zeitrahmens, der für die Erkennung verwendet wird, der Anzahl der analysierten historischen Balken und der Mindestkerzengröße, die erforderlich ist, um als sinnvolle Verschiebung unter Verwendung eines ATR-Multiplikators zu gelten.
Zusätzliche Umschalter ermöglichen es Händlern, bestimmte Zonentypen wie Verdrängung, nicht gefüllte Preisbereiche, Strukturübergänge und Liquiditäts-Sweep-Ursprünge zu aktivieren oder zu deaktivieren, während eine optionale Volumenfilterung zur Verbesserung der Signalqualität angewendet werden kann. Die Gefahr der Unübersichtlichkeit des Charts wird durch die Begrenzung der maximalen Anzahl aktiver Zonen, durch Opazitätseinstellungen und durch die Möglichkeit, Zonen zeitlich nach vorne zu verschieben, eingedämmt, sodass der Indikator sowohl flexibel als auch optisch übersichtlich bleibt.
//+------------------------------------------------------------------+ //| Zone Types | //+------------------------------------------------------------------+ enum ZONE_TYPE { ZONE_DISPLACEMENT, // Large impulsive candles ZONE_UNFILLED, // Price inefficiency gaps ZONE_STRUCTURE, // Structure transition ZONE_LIQUIDITY // Liquidity sweep origin }; //+------------------------------------------------------------------+ //| Zone Structure | //+------------------------------------------------------------------+ struct MemoryZone { datetime startTime; datetime endTime; double high; double low; ZONE_TYPE type; color zoneColor; bool isActive; int creationBar; double strength; // 0-1 strength rating void Reset() { startTime = 0; endTime = 0; high = 0.0; low = 0.0; type = ZONE_DISPLACEMENT; zoneColor = clrNONE; isActive = false; creationBar = 0; strength = 0.5; } bool IsPriceInZone(double price) { return price >= low && price <= high; } double GetZoneMiddle() { return (high + low) / 2.0; } bool IsZoneMitigated(double currentHigh, double currentLow) { // Zone is considered mitigated if price has traded through entire zone return (currentLow <= low && currentHigh >= high); } }; //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ MemoryZone zones[]; int zoneCount = 0; double atrBuffer[]; datetime lastProcessedBar = 0; string indicatorName; double currentAtrValue = 0.0; // Colors for different zone types color displacementColor = C'0,100,255'; // Blue color unfilledColor = C'255,128,0'; // Orange color structureColor = C'128,0,255'; // Purple color liquidityColor = C'255,50,50'; // Red
Dieser Abschnitt des Codes definiert die verschiedenen Zonenklassifizierungen, die vom Indikator „Market Memory Zones“ verwendet werden. Die Enumeration ZONE_TYPE bietet eine klare und strukturierte Möglichkeit, jede Zone nach ihrem Ursprung zu kennzeichnen, einschließlich der Verdrängungszonen, die durch impulsive Kerzen, nicht erfüllte Preisineffizienzen, Strukturübergangszonen und Liquiditätssweep-Ursprünge entstehen. Die Verwendung einer Enumeration verbessert die Lesbarkeit des Codes und stellt sicher, dass jede Zone auf der Grundlage ihrer spezifischen Verhaltensbedeutung auf dem Markt konsistent identifiziert, verarbeitet und visualisiert wird.
Die Struktur MemoryZone dient als zentraler Datencontainer für jede erkannte Zone. Sie speichert wichtige Informationen wie die Zeitgrenzen, die Preisspanne, den Zonentyp, die visuelle Farbe, den Erstellungsbalken und eine optionale Stärkebewertung, die zur Einstufung oder Filterung von Zonen verwendet werden kann. Hilfsfunktionen innerhalb der Struktur vereinfachen das Zonenmanagement, indem sie schnelle Überprüfungen ermöglichen, um festzustellen, ob sich der aktuelle Preis innerhalb einer Zone befindet, den Mittelpunkt der Zone zu berechnen oder zu bestätigen, ob die Zone durch den Preishandel durch ihre gesamte Spanne vollständig gemildert wurde. Die Reset-Methode gewährleistet, dass Zonendaten sicher wiederverwendet oder gelöscht werden können, ohne dass Restwerte zurückbleiben.
Die globalen Variablen verwalten den Gesamtzustand des Indikators während der Laufzeit. Das dynamische Zonen-Array speichert alle aktiven und historischen Zonen, während Zähler und Puffer die Anzahl der Zonen, ATR-Werte und den zuletzt verarbeiteten Balken verfolgen, um redundante Berechnungen zu vermeiden. Zusätzliche Variablen behandeln die Benennung der Indikatoren und die aktuellen ATR-Werte, die in der Logik der Zonenvalidierung verwendet werden. Farbdefinitionen am Ende stellen sicher, dass jeder Zonentyp auf dem Chart visuell erkennbar ist, sodass Händler die Art einer Zone auf einen Blick erkennen können.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { indicatorName = "MMZ_" + IntegerToString(GetTickCount()); // Set indicator properties IndicatorSetString(INDICATOR_SHORTNAME, "Market Memory Zones"); IndicatorSetInteger(INDICATOR_DIGITS, _Digits); // Initialize ATR buffer ArrayResize(atrBuffer, LookbackBars); ArrayInitialize(atrBuffer, 0.0); // Initialize zones array ArrayResize(zones, MaxActiveZones * 2); // Double for safety for(int i = 0; i < ArraySize(zones); i++) zones[i].Reset(); // Calculate initial ATR currentAtrValue = iATR(_Symbol, AnalysisTF, 14); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Clean up all zone objects ObjectsDeleteAll(0, "MMZ_"); ChartRedraw(); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { // Check if we have enough bars if(rates_total < LookbackBars) return 0; // Process only on new bar if(prev_calculated > 0 && prev_calculated == rates_total) return rates_total; // Set array as series ArraySetAsSeries(time, true); ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); ArraySetAsSeries(tick_volume, true); // Update ATR value currentAtrValue = iATR(_Symbol, AnalysisTF, 14); // Detect zones starting from the most recent data DetectMemoryZones(rates_total, time, open, high, low, close, tick_volume); // Update and draw zones UpdateZones(rates_total, time, high[0], low[0]); return rates_total; }
Dieser Abschnitt des Codes behandelt die Verwaltung des Lebenszyklus des Indikators Market Memory Zones, beginnend mit der Funktion OnInit(). Während der Initialisierung wird ein eindeutiger Indikatorenname generiert, um Objektkonflikte zu vermeiden, und es werden wichtige Indikatoreigenschaften wie der Anzeigename und die Preisgenauigkeit festgelegt. Der ATR-Puffer und das Zonen-Array werden dann zugewiesen und initialisiert, um eine saubere Speichernutzung zu gewährleisten, bevor die Berechnungen beginnen. Jede Zonenstruktur wird auf einen Standardzustand zurückgesetzt, und es wird ein anfänglicher ATR-Wert auf der Grundlage des ausgewählten Analysezeitraums berechnet, der später zur Validierung der Verschiebung und der Zonenfestigkeit verwendet wird.
Die Funktionen OnDeinit() und OnCalculate() verwalten die Aufräumarbeiten und die Echtzeitausführung. Wenn der Indikator entfernt wird, werden alle grafischen Zonenobjekte, die mit dem Indikator verbunden sind, gelöscht, um das Chart sauber zu halten. Während jedes Berechnungszyklus stellt der Code sicher, dass genügend historische Daten verfügbar sind, und verarbeitet die Logik nur bei der Bildung eines neuen Balkens, um die Leistung zu verbessern. Die Preisfelder werden im Serienmodus konfiguriert, um den Zugriff auf aktuelle Daten zu erleichtern, der ATR-Wert wird aktualisiert, und die Kernerkennungs- und Aktualisierungsroutinen werden ausgeführt, um neue Marktspeicherzonen zu identifizieren und bestehende zu verwalten, damit der Chart mit dem aktuellen Marktverhalten synchronisiert bleibt.
//+------------------------------------------------------------------+ //| Detect All Memory Zones | //+------------------------------------------------------------------+ void DetectMemoryZones(int rates_total, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[]) { // Clear old zones for(int i = 0; i < zoneCount; i++) zones[i].Reset(); zoneCount = 0; // Calculate average volume for volume filter double avgVolume = CalculateAverageVolume(tick_volume, rates_total, 20); // Start from older bars to newer (but leave room for analysis) int startBar = MathMin(LookbackBars, rates_total - 10); for(int i = startBar; i >= 3; i--) // Start from 3 to have previous bars for context { // 1. Check for Displacement Zones if(ShowDisplacementZones && zoneCount < MaxActiveZones) { if(IsDisplacementCandle(i, high, low, open, close, tick_volume, avgVolume)) { AddDisplacementZone(i, time, high, low, open, close); } } // 2. Check for Unfilled Price Areas if(ShowUnfilledAreas && zoneCount < MaxActiveZones) { if(IsUnfilledArea(i, high, low, open, close)) { AddUnfilledZone(i, time, high, low, open, close); } } // 3. Check for Structure Transition Zones if(ShowStructureZones && zoneCount < MaxActiveZones && i > 5) { if(IsStructureTransition(i, high, low)) { AddStructureZone(i, time, high, low); } } // 4. Check for Liquidity Sweep Origins if(ShowLiquidityZones && zoneCount < MaxActiveZones && i > 1) { if(IsLiquiditySweep(i, high, low, close, open)) { AddLiquidityZone(i, time, high, low, close); } } // Break if we reached max zones if(zoneCount >= MaxActiveZones) break; } } //+------------------------------------------------------------------+ //| Check for Displacement Candle | //+------------------------------------------------------------------+ bool IsDisplacementCandle(int bar, const double &high[], const double &low[], const double &open[], const double &close[], const long &tick_volume[], double avgVolume) { // 1. Check minimum candle size (ATR-based) double candleSize = high[bar] - low[bar]; double minSize = currentAtrValue * MinCandleSizeATR; if(candleSize < minSize) return false; // 2. Check for minimal overlap with prior candle double overlapWithPrev = CalculateOverlap(high[bar], low[bar], high[bar+1], low[bar+1]); if(overlapWithPrev > 0.3) return false; // More than 30% overlap // 3. Check for minimal overlap with next candle if(bar > 0) { double overlapWithNext = CalculateOverlap(high[bar], low[bar], high[bar-1], low[bar-1]); if(overlapWithNext > 0.3) return false; } // 4. Volume spike confirmation (optional) if(FilterByVolume) { double volumeRatio = (double)tick_volume[bar] / avgVolume; if(volumeRatio < 1.5) return false; // Less than 150% of average } // 5. Check if it's an impulsive candle (strong directional move) bool isBullishImpulse = (close[bar] > open[bar]) && ((close[bar] - open[bar]) > candleSize * 0.6); bool isBearishImpulse = (close[bar] < open[bar]) && ((open[bar] - close[bar]) > candleSize * 0.6); return (isBullishImpulse || isBearishImpulse); } //+------------------------------------------------------------------+ //| Check for Unfilled Price Area | //+------------------------------------------------------------------+ bool IsUnfilledArea(int bar, const double &high[], const double &low[], const double &open[], const double &close[]) { // Look for Fair Value Gap pattern (3-candle formation) if(bar < 2) return false; // Pattern: Middle candle has range not overlapped by candles before and after // Bullish FVG if(low[bar-1] > high[bar] && low[bar-2] > high[bar]) { return true; } // Bearish FVG if(high[bar-1] < low[bar] && high[bar-2] < low[bar]) { return true; } // Alternative: Large candle followed by small overlapping candles double candle1Size = high[bar] - low[bar]; double candle2Size = high[bar-1] - low[bar-1]; if(candle1Size > currentAtrValue && candle2Size < currentAtrValue * 0.5) { // Check if candle2 didn't fill much of candle1's range double fillRatio = CalculateOverlap(high[bar], low[bar], high[bar-1], low[bar-1]); if(fillRatio < 0.3) return true; } return false; } //+------------------------------------------------------------------+ //| Check for Structure Transition | //+------------------------------------------------------------------+ bool IsStructureTransition(int bar, const double &high[], const double &low[]) { // Need at least 5 bars to determine structure if(bar < 5) return false; // Detect swing points bool isSwingHigh = IsSwingHigh(bar, high, 3); bool isSwingLow = IsSwingLow(bar, low, 3); if(!isSwingHigh && !isSwingLow) return false; // Check for structure break if(isSwingHigh) { // Check if breaking previous structure (Higher High to Lower High) double prevSwingHigh = FindPrevSwingHigh(bar, high, 3); if(prevSwingHigh > 0 && high[bar] < prevSwingHigh) { return true; // Lower High formed } } if(isSwingLow) { // Check if breaking previous structure (Lower Low to Higher Low) double prevSwingLow = FindPrevSwingLow(bar, low, 3); if(prevSwingLow > 0 && low[bar] > prevSwingLow) { return true; // Higher Low formed } } return false; } //+------------------------------------------------------------------+ //| Check for Liquidity Sweep | //+------------------------------------------------------------------+ bool IsLiquiditySweep(int bar, const double &high[], const double &low[], const double &close[], const double &open[]) { if(bar < 2) return false; // Bullish liquidity sweep: Sweeps previous low then closes above if(low[bar] < low[bar+1] && close[bar] > low[bar+1] && close[bar] > close[bar+1]) { // Check for reversal pattern if(close[bar] > open[bar] && (close[bar] - open[bar]) > (high[bar] - low[bar]) * 0.5) { return true; } } // Bearish liquidity sweep: Sweeps previous high then closes below if(high[bar] > high[bar+1] && close[bar] < high[bar+1] && close[bar] < close[bar+1]) { // Check for reversal pattern if(close[bar] < open[bar] && (open[bar] - close[bar]) > (high[bar] - low[bar]) * 0.5) { return true; } } return false; }
Dieser Teil des Codes ist für die Erkennung aller „Market Memory Zones“ verantwortlich, indem er historische Kursdaten scannt und aussagekräftige Bereiche auf der Grundlage unterschiedlichen Marktverhaltens klassifiziert. Die Funktion DetectMemoryZones beginnt mit dem Löschen zuvor gespeicherter Bereiche, um bei jedem Berechnungszyklus eine neue Analyse zu gewährleisten. Anschließend wird das durchschnittliche Volumen berechnet, das später als optionaler Filter verwendet wird, um Bewegungen mit hoher Partizipation zu validieren. Die Funktion iteriert von älteren Balken zu neueren innerhalb des definierten Rückblickbereichs, sodass der Indikator die Kursbewegung im richtigen chronologischen Kontext analysieren kann, während die Leistungsgrenzen durch eine maximale Zonenbegrenzung eingehalten werden.
Verdrängungszonen werden zuerst identifiziert, da sie starke, absichtliche Marktbewegungen darstellen. Die Funktion IsDisplacementCandle wendet mehrere Validierungsebenen an, einschließlich ATR-basierter Kerzengröße, minimaler Überlappung mit benachbarten Kerzen, optionaler Bestätigung von Volumenspikes und starker Dominanz von Richtungskörpern. Diese Bedingungen stellen sicher, dass nur echte impulsive Bewegungen berücksichtigt werden und normale Volatilität und Signale von geringer Qualität herausgefiltert werden. Sind alle Kriterien erfüllt, wird die Kursspanne der Kerze als Verschiebungszone erfasst, die Bereiche widerspiegelt, in denen sich der Kurs zu stark bewegt hat, um vollständig gehandelt zu werden.
Unerfüllte Preisbereiche werden als Nächstes durch die Funktion IsUnfilledArea erkannt, die sich auf Ineffizienzen wie Fair Value Gaps und schnelle Expansionen gefolgt von schwachen Rücksetzern konzentriert. Die Logik sucht nach Drei-Kerzen-Formationen, bei denen sich der Bereich der mittleren Kerze nicht mit den umliegenden Kerzen überschneidet, sowie nach Szenarien, in denen auf eine große Kerze eine viel kleinere Kerze folgt, die die Bewegung nicht deutlich zurückläuft. Diese Muster zeigen Bereiche auf, in denen ein Preisungleichgewicht auftrat, was die Wahrscheinlichkeit erhöht, dass der Markt später wieder ins Gleichgewicht kommt.
Der Code identifiziert dann Strukturübergangszonen und Liquiditätsschwankungen, die Veränderungen im Marktverhalten und manipulationsbedingte Bewegungen erfassen. Die Funktion IsStructureTransition nutzt die Schwingungspunktanalyse, um Verschiebungen wie niedrigere Höhen oder höhere Tiefen zu erkennen, die eine Änderung des Charakters signalisieren. Die Funktion IsLiquiditySweep konzentriert sich auf Kerzen, die frühere Höchst- oder Tiefststände erreichen und dann stark umkehren, was auf Stopp-Läufe und eine intelligente Beteiligung hinweist. Diese Erkennungsmethoden ermöglichen es dem Indikator, Zonen abzubilden, die auf Struktur, Liquidität und Absicht beruhen, und bilden einen umfassenden Rahmen, um zu erkennen, wo der Preis wahrscheinlich zurückkehren wird.
//+------------------------------------------------------------------+ //| Add Displacement Zone | //+------------------------------------------------------------------+ void AddDisplacementZone(int bar, const datetime &time[], const double &high[], const double &low[], const double &open[], const double &close[]) { if(zoneCount >= MaxActiveZones) return; zones[zoneCount].startTime = time[bar]; zones[zoneCount].endTime = ExtendZonesForward ? TimeCurrent() + PeriodSeconds(_Period) * 100 : time[bar]; zones[zoneCount].high = high[bar]; zones[zoneCount].low = low[bar]; zones[zoneCount].type = ZONE_DISPLACEMENT; zones[zoneCount].zoneColor = displacementColor; zones[zoneCount].isActive = true; zones[zoneCount].creationBar = bar; // Calculate strength based on candle size relative to ATR double candleSize = high[bar] - low[bar]; zones[zoneCount].strength = MathMin(candleSize / (currentAtrValue * 2), 1.0); // Adjust zone boundaries for better visualization double range = zones[zoneCount].high - zones[zoneCount].low; zones[zoneCount].high += range * 0.05; // Add 5% padding zones[zoneCount].low -= range * 0.05; zoneCount++; } //+------------------------------------------------------------------+ //| Add Unfilled Zone | //+------------------------------------------------------------------+ void AddUnfilledZone(int bar, const datetime &time[], const double &high[], const double &low[], const double &open[], const double &close[]) { if(zoneCount >= MaxActiveZones) return; // For FVG pattern (3-candle) if(bar >= 2) { // Bullish FVG if(low[bar-1] > high[bar] && low[bar-2] > high[bar]) { zones[zoneCount].startTime = time[bar]; zones[zoneCount].endTime = ExtendZonesForward ? TimeCurrent() + PeriodSeconds(_Period) * 100 : time[bar-1]; zones[zoneCount].high = MathMax(high[bar-1], high[bar-2]); zones[zoneCount].low = low[bar]; } // Bearish FVG else if(high[bar-1] < low[bar] && high[bar-2] < low[bar]) { zones[zoneCount].startTime = time[bar]; zones[zoneCount].endTime = ExtendZonesForward ? TimeCurrent() + PeriodSeconds(_Period) * 100 : time[bar-1]; zones[zoneCount].high = high[bar]; zones[zoneCount].low = MathMin(low[bar-1], low[bar-2]); } zones[zoneCount].type = ZONE_UNFILLED; zones[zoneCount].zoneColor = unfilledColor; zones[zoneCount].isActive = true; zones[zoneCount].creationBar = bar; zones[zoneCount].strength = 0.7; zoneCount++; } } //+------------------------------------------------------------------+ //| Add Structure Zone | //+------------------------------------------------------------------+ void AddStructureZone(int bar, const datetime &time[], const double &high[], const double &low[]) { if(zoneCount >= MaxActiveZones) return; zones[zoneCount].startTime = time[bar]; zones[zoneCount].endTime = ExtendZonesForward ? TimeCurrent() + PeriodSeconds(_Period) * 100 : time[bar]; if(IsSwingHigh(bar, high, 3)) { zones[zoneCount].high = high[bar]; zones[zoneCount].low = high[bar] - (high[bar] - low[bar]) * 0.5; // 50% of candle range } else if(IsSwingLow(bar, low, 3)) { zones[zoneCount].low = low[bar]; zones[zoneCount].high = low[bar] + (high[bar] - low[bar]) * 0.5; // 50% of candle range } zones[zoneCount].type = ZONE_STRUCTURE; zones[zoneCount].zoneColor = structureColor; zones[zoneCount].isActive = true; zones[zoneCount].creationBar = bar; zones[zoneCount].strength = 0.8; zoneCount++; } //+------------------------------------------------------------------+ //| Add Liquidity Zone | //+------------------------------------------------------------------+ void AddLiquidityZone(int bar, const datetime &time[], const double &high[], const double &low[], const double &close[]) { if(zoneCount >= MaxActiveZones) return; zones[zoneCount].startTime = time[bar]; zones[zoneCount].endTime = ExtendZonesForward ? TimeCurrent() + PeriodSeconds(_Period) * 100 : time[bar]; // Determine sweep direction if(low[bar] < low[bar+1]) // Bullish sweep { zones[zoneCount].high = high[bar]; zones[zoneCount].low = low[bar+1]; // The level that was swept } else if(high[bar] > high[bar+1]) // Bearish sweep { zones[zoneCount].high = high[bar+1]; // The level that was swept zones[zoneCount].low = low[bar]; } zones[zoneCount].type = ZONE_LIQUIDITY; zones[zoneCount].zoneColor = liquidityColor; zones[zoneCount].isActive = true; zones[zoneCount].creationBar = bar; zones[zoneCount].strength = 0.9; // Liquidity sweeps are high probability zoneCount++; } //+------------------------------------------------------------------+ //| Update and Draw Zones | //+------------------------------------------------------------------+ void UpdateZones(int rates_total, const datetime &time[], double currentHigh, double currentLow) { // First, remove all old objects ObjectsDeleteAll(0, "MMZ_"); // Draw active zones for(int i = 0; i < zoneCount; i++) { if(!zones[i].isActive) continue; // Check if zone is still valid (not fully mitigated) if(zones[i].IsZoneMitigated(currentHigh, currentLow)) { zones[i].isActive = false; continue; } // Draw zone as rectangle DrawZone(zones[i], i); } // Draw legend DrawLegend(); }
Hier konzentriert sich der Code auf die Erstellung und Verwaltung der verschiedenen Marktspeicherzonen, sobald diese erkannt wurden. Jede Funktion Add*Zone ist für die Initialisierung eines bestimmten Zonentyps und dessen Speicherung im gemeinsamen Zonen-Array verantwortlich, wobei die Höchstgrenze für aktive Zonen beachtet wird, um ein Durcheinander im Chart zu vermeiden. Gemeinsame Eigenschaften wie Start- und Endzeit, Preisgrenzen, Zonentyp, Farbe, Aktivierungsstatus und Erstellungsbalken werden konsistent zugewiesen, sodass alle Zonen der gleichen internen Struktur folgen und später einheitlich verarbeitet werden können.
Verdrängte und nicht ausgefüllte Zonen werden anhand von Preisaktionsmerkmalen konstruiert, die für ihr Verhalten einzigartig sind. Verdrängungszonen erfassen den gesamten Bereich einer impulsiven Kerze und berechnen einen Stärke-Score, der darauf basiert, wie groß die Kerze im Verhältnis zur aktuellen ATR ist, wobei ein leichtes Padding hinzugefügt wird, um die visuelle Klarheit auf dem Chart zu verbessern. Ungefüllte Zonen hingegen ergeben sich aus Fair-Value-Gaps oder schnellen Expansionen, deren Grenzen durch das Ungleichgewicht zwischen mehreren Kerzen definiert werden. Diese Zonen werden als mäßig stark eingestuft, um ihre Tendenz widerzuspiegeln, eher Preise für eine Neuausrichtung als für eine sofortige Reaktion anzuziehen.
Struktur- und Liquiditätszonen betonen Absicht und Wahrscheinlichkeit. Strukturzonen werden um Swing-Hochs oder Swing-Tiefs herum gebildet, bei denen eine Änderung des Marktverhaltens eingetreten ist, wobei ein Teil des Kerzenbereichs zur Definition eines präzisen Reaktionsbereichs verwendet wird. Liquiditätszonen markieren den Ausgangspunkt von Stop-Runs, indem sie den Bereich um das durchlaufene Kursniveau erfassen und ihm aufgrund seiner ausgeprägten historischen Reaktionstendenz einen höheren Stärke-Wert zuweisen. Die Funktion UpdateZones aktualisiert dann das Chart, indem sie veraltete Objekte entfernt, Zonen deaktiviert, die bereits vollständig entschärft wurden, und nur gültige Zonen neu zeichnet, sodass die visuelle Ausgabe sauber, relevant und auf das aktuelle Kursgeschehen abgestimmt bleibt.
//+------------------------------------------------------------------+ //| Draw Zone as Rectangle | //+------------------------------------------------------------------+ void DrawZone(MemoryZone &zone, int index) { string objName = StringFormat("MMZ_%s_%d_%d", EnumToString(zone.type), (int)zone.startTime, index); // Create rectangle if(ObjectCreate(0, objName, OBJ_RECTANGLE, 0, zone.startTime, zone.high, zone.endTime, zone.low)) { // Set properties ObjectSetInteger(0, objName, OBJPROP_COLOR, zone.zoneColor); ObjectSetInteger(0, objName, OBJPROP_BACK, true); ObjectSetInteger(0, objName, OBJPROP_FILL, true); ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1); // Set opacity based on strength and input int alpha = (int)(zone.strength * ZoneOpacity); alpha = MathMax(alpha, 10); alpha = MathMin(alpha, 80); color zoneColorWithAlpha = ColorToARGB(zone.zoneColor, alpha); ObjectSetInteger(0, objName, OBJPROP_COLOR, zoneColorWithAlpha); // Add tooltip string tooltip = StringFormat("MMZ Type: %s\nStrength: %.1f\nRange: %.5f - %.5f", GetZoneTypeName(zone.type), zone.strength, zone.low, zone.high); ObjectSetString(0, objName, OBJPROP_TOOLTIP, tooltip); } } //+------------------------------------------------------------------+ //| Draw Legend | //+------------------------------------------------------------------+ void DrawLegend() { int yPos = 20; int xPos = 20; if(ShowDisplacementZones) { CreateLabel("Legend_Displacement", "• Displacement Zones", xPos, yPos, displacementColor); yPos += 20; } if(ShowUnfilledAreas) { CreateLabel("Legend_Unfilled", "• Unfilled Areas", xPos, yPos, unfilledColor); yPos += 20; } if(ShowStructureZones) { CreateLabel("Legend_Structure", "• Structure Transitions", xPos, yPos, structureColor); yPos += 20; } if(ShowLiquidityZones) { CreateLabel("Legend_Liquidity", "• Liquidity Sweeps", xPos, yPos, liquidityColor); yPos += 20; } } void CreateLabel(string name, string text, int x, int y, color clr) { ObjectCreate(0, "MMZ_" + name, OBJ_LABEL, 0, 0, 0); ObjectSetString(0, "MMZ_" + name, OBJPROP_TEXT, text); ObjectSetInteger(0, "MMZ_" + name, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, "MMZ_" + name, OBJPROP_YDISTANCE, y); ObjectSetInteger(0, "MMZ_" + name, OBJPROP_COLOR, clr); ObjectSetInteger(0, "MMZ_" + name, OBJPROP_FONTSIZE, 10); ObjectSetString(0, "MMZ_" + name, OBJPROP_FONT, "Arial"); ObjectSetInteger(0, "MMZ_" + name, OBJPROP_HIDDEN, true); }
Dieser Abschnitt des Codes ist für die übersichtliche und informative Darstellung der „Market Memory Zones“ im Chart verantwortlich. Die Funktion DrawZone erstellt für jede aktive Zone ein eindeutig benanntes Rechteck unter Verwendung des Typs, der Startzeit und des Index, um Objektkonflikte zu vermeiden. Das Rechteck wird zwischen den Preisgrenzen und der Zeitspanne der Zone gezeichnet, so gestaltet, dass es sich im Hintergrund befindet und zur besseren Sichtbarkeit ausgefüllt ist. Die Deckkraft der Zonen wird dynamisch auf der Grundlage der berechneten Stärke der Zone und der nutzerdefinierten Deckkrafteinstellung angepasst, sodass stärkere Zonen hervorstechen, während schwächere dezent bleiben. Jede Zone ist außerdem mit einem Tooltip versehen, der beim Überfahren mit dem Mauszeiger schnelle Kontextinformationen wie Zonentyp, Stärke und genaue Preisspanne liefert.
Die legendenbezogenen Funktionen verbessern die Nutzerfreundlichkeit, indem sie klar erklären, was die einzelnen Zonenfarben bedeuten. Die Funktion DrawLegend zeigt nur Beschriftungen für die Zonentypen an, die der Nutzer aktiviert hat, sodass das Chart übersichtlich und relevant bleibt. Jede Beschriftung wird mit der Hilfsfunktion CreateLabel erstellt, die den Text ordentlich im Chart positioniert, ein einheitliches Styling anwendet und die Objekte vor versehentlicher Interaktion verbirgt. Diese Funktionen sorgen dafür, dass der Indikator nicht nur analytisch aussagekräftig, sondern auch visuell intuitiv ist, sodass die Händler die angezeigten Zonen leicht interpretieren und ihnen vertrauen können.
Demo der „Market Memory Zones“

Schlussfolgerung
Zusammenfassend lässt sich sagen, dass es bei der Entwicklung des Market Memory Zones-Indikators darum ging, eine systematische Methode zur Identifizierung von Schlüsselbereichen auf dem Chart zu entwickeln, in denen eine Rückkehr der Kurse wahrscheinlich ist. Wir haben mehrere Zonentypen entwickelt – Verdrängungszonen, unbesetzte Preisbereiche, Strukturübergangszonen und Liquiditätssweep-Originale –, die jeweils spezifische Marktverhaltensweisen wie impulsive Bewegungen, Ineffizienzen, Strukturverschiebungen und Liquiditätsziele widerspiegeln. Durch die Implementierung von Erkennungsfunktionen, die Definition einer robusten MemoryZone-Struktur und die Verwaltung von Zonen mit Stärkebewertungen und visuellen Eigenschaften haben wir einen Indikator geschaffen, der diese Zonen nicht nur automatisch erkennt, sondern sie auch mit Rechtecken, Tooltips und einer Legende zur einfachen Interpretation übersichtlich im Chart anzeigt.
Zusammenfassend lässt sich sagen, dass dieser Indikator den Händlern ein leistungsfähiges Instrument an die Hand gibt, um potenzielle Marktreaktionen zu antizipieren, indem er Bereiche hervorhebt, die auf der Grundlage früherer Kursbewegungen statistische Bedeutung haben. Indem sie sich auf Bereiche des Marktgedächtnisses statt auf willkürliche Unterstützungen und Widerstände konzentrieren, können Händler fundiertere Entscheidungen treffen, das Timing für den Handelseinstieg verbessern und das Risiko besser steuern. Die visuelle Darstellung der Zonen und Stärkebewertungen ermöglicht eine schnelle Analyse und erleichtert die Identifizierung von Bereichen mit hoher Wahrscheinlichkeit für einen Wiedereinstieg, eine Umkehrung oder eine Fortsetzung des Trends.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20973
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Statistische Arbitrage durch kointegrierte Aktien (Teil 10): Erkennen von Strukturbrüchen
MQL5-Werkzeuge für den Handel (Teil 12): Erweiterung des Korrelationsmatrix-Dashboards um interaktive Funktionen
Entwicklung eines Toolkits zur Preisaktionsanalyse (Teil 56): Interpretation von Annahme und Ablehnung bei Sitzungen anhand des CPI
Klassische Strategien neu interpretieren (Teil 21): Entdeckung einer Ensemble-Strategie aus Bollinger-Bändern und RSI
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.