Vom Neuling zum Experten: Forex Markt Perioden
Inhalt
Einführung
In meiner Stadt habe ich mir einmal die Zeit genommen, zu beobachten, wie der örtliche Gemüsemarkt täglich funktioniert – und was ich dabei entdeckte, war ziemlich aufschlussreich. Jeden Morgen kommen die Landwirte früh mit ihren frischen Produkten, und die Einzelhändler strömen ebenso früh herbei, um sich die besten Großhandelsangebote zu sichern. In den ersten Stunden nach der Markteröffnung erreicht die Aktivität ihren Höhepunkt – die Transaktionen gehen schnell vonstatten, und die Preise ändern sich oft dynamisch. Gegen Mittag und in der Mittagspause verlangsamt sich das Tempo allmählich, es kommen weniger Einzelhändler, und viele Landwirte beginnen zu packen. Doch wenn der Abend naht und der Markt sich dem Ladenschluss nähert, nimmt die Aktivität noch einmal zu, und späte Käufer und Verkäufer machen ihre letzten Schritte vor Sonnenuntergang.
Dieses einfache Marktverhalten spiegelt die Funktionsweise des Devisenmarktes eindrucksvoll wider. Genau wie die Landwirte und Einzelhändler agieren auch die Händler weltweit innerhalb bestimmter Zeiträume – Perioden mit hoher und niedriger Aktivität, die den Rhythmus des Marktes bestimmen. Es gibt Spitzenzeiten, in denen große Transaktionen stattfinden, die den Charakter und die Dynamik des Handelstages prägen.
Die heutige Herausforderung besteht darin, MQL5 zu nutzen, um diese Marktsitzungsperioden zu visualisieren und sie nahtlos an Strukturen mit höherem und niedrigerem Zeitrahmen anzugleichen. Durch die Synchronisierung der Eröffnungs- und Schlusszeiten dieser Sitzungen wollen wir besser verstehen, wie eine Sitzung die nächste beeinflusst – und wie das Timing die Kursbewegung wirklich beeinflusst. Ein weiteres faszinierendes Geheimnis, das es zu lüften gilt, liegt in der Erkenntnis, dass Finanzkerzen selbst zeitbasierte Balken darstellen, die in der Regel stündliche, tägliche oder wöchentliche Zeiträume bezeichnen. Wenn wir dasselbe Konzept auf Marktsitzungen anwenden, können wir sitzungsbasierte Kerzen erstellen, die jeweils die einzigartigen Merkmale des jeweiligen Handelsfensters widerspiegeln. Genau wie bei den traditionellen Zeitrahmen besitzt jede Sitzung ihre eigene Open-, High-, Low- und Close-Struktur (OHLC), die eine eindeutige Aussage über die Aktivität, die Stimmung und die Volatilität innerhalb dieses Zeitraums macht.
Der auffälligste Unterschied besteht jedoch darin, dass sich die Sitzungsbalken häufig überschneiden, insbesondere bei globalen Märkten mit gleichen Handelszeiten. So überschneiden sich beispielsweise die Londoner und New Yorker Sitzungen (Abb. 1) für mehrere Stunden, was zu Zeiten erhöhter Volatilität und Liquidität führt. Im Gegensatz zu den Standardzeitrahmen, die der Reihe nach aufeinander folgen, liegen die Sitzungsleisten zeitlich nebeneinander und spiegeln die gleichzeitige Beteiligung mehrerer Regionen wider. Diese Überschneidung ist genau das, was zu dynamischen Übergängen in der Marktstimmung führt und die sitzungsbasierte Analyse zu einer wesentlichen Ergänzung der traditionellen zeitbasierten Strukturen macht.

Abb. 1. Konzeptionelle Überschneidungen zwischen den Londoner und New Yorker Handelssitzungen
Durch diese Entwicklung wollen wir Sitzungsbalken in der gleichen analytischen Sprache wie Standardkerzenstöcke visualisieren. Dadurch können Händler sitzungsspezifisches Verhalten erkennen, Liquiditätsspitzen identifizieren und vergleichen, wie die Stimmung einer Sitzung in die nächste übergeht. Letztlich schlägt diese Funktion eine Brücke zwischen der konventionellen Zeitrahmenanalyse und dem verborgenen Rhythmus der Marktsitzungen und fördert eine tiefere Verbindung zwischen Zeit, Struktur und Preisdynamik.
Um diese Idee zum Leben zu erwecken, werde ich im Folgenden die vollständige Implementierung skizzieren – das zugrundeliegende Konzept im Detail erläutern und dann den vollständigen MQL5-Code im folgenden Abschnitt dieser Diskussion vorstellen.
Umsetzung
Unsere Erforschung der Marktperioden entwickelt sich weiter, da wir eine weitere leistungsstarke Funktion einführen – eine, die die herkömmliche Zeitrahmenanalyse mit der Dynamik globaler Forex-Handelssitzungen vereint. Diese Verbesserung überbrückt die Lücke zwischen strukturierten Zeiträumen und dem realen Sitzungsverhalten und bietet einen realistischeren Überblick über die Entwicklung der Märkte im Laufe des Tages. Um diese neue Funktion zu implementieren, werden wir sie zunächst isoliert entwickeln und testen, um ihre Genauigkeit und Leistung sicherzustellen, bevor wir sie in den bestehenden Market Periods Synchronizer integrieren. Das Ergebnis wird eine fortschrittlichere und aufschlussreichere Version des Tools sein – eine, die sowohl die zeitliche Struktur als auch das sitzungsbedingte Marktverhalten in perfekter Harmonie erfasst.
Ein weiterer Grund für die isolierte Implementierung dieses Merkmals ist, dass sichergestellt werden soll, dass jeder Leser das Konzept vor der Integration vollständig begreifen kann. Es dient auch als gute Entwicklungspraxis, insbesondere da das Programm mit jeder neuen Funktion komplexer wird. Durch die Isolierung der Komponenten wird das System einfacher zu testen, zu debuggen und zu verstehen – sowohl für die Lernenden, die mitmachen, als auch für die Entwickler, die das Tool in zukünftigen Versionen erweitern.
Um dieses Konzept in die Praxis umzusetzen, folgen wir einem zweistufigen Arbeitsablauf: Zuerst entwickeln wir die Funktion isoliert, und dann integrieren wir die validierte Komponente in den Market Periods Synchronizer EA. Dieser Ansatz stellt sicher, dass die Logik modular bleibt, einfach zu testen ist und für zukünftige Erweiterungen angepasst werden kann.
- Schritt 1 – Entwicklung in Isolation: Wir beginnen mit der Erstellung der eigenständigen Klasse CSessionVisualizer, die für die Definition von Handelssitzungen, die Berechnung ihrer OHLC-Werte und ihre Darstellung als gefüllte Rechtecke, die Kerzen nachahmen (optional mit Dochten und Beschriftungen), verantwortlich ist. Diese eigenständige Version sollte unabhängig getestet werden, z. B. durch Aufzeichnen von Sitzungen in einem EURUSD-H1-Chart, um die Genauigkeit der Sitzungsgrenzen und der Datenvisualisierung zu überprüfen.
- Schritt 2 – Integrationsphase: Nach der Überprüfung kann der Sitzungsvisualisierer in den Haupt-EA integriert werden. Dies beinhaltet das Hinzufügen neuer UI-Steuerelemente, wie z. B. einen Schalter „Show Sessions: ON/OFF“, eine Farbauswahl für die Sitzungen und eine Rückblick-Einstellung, um festzulegen, wie viele historische Sitzungen angezeigt werden sollen. Die Funktion RefreshLines() wird erweitert, um den Sitzungsvisualisierer nach dem Zeichnen von Haupt- und Nebenlinien aufzurufen, damit alle Elemente synchronisiert bleiben. Gemeinsame globale Variablen (wie g_Lookback) sorgen für Konsistenz, während visuelle Überschneidungen zwischen Sitzungen durch halbtransparente Überlagerungen verwaltet werden, um die Übersichtlichkeit zu erhöhen.
Schritt 1 – Entwicklung in Isolation
In diesem Abschnitt werden wir eine modulare Klasse namens CSessionVisualizer erstellen, die entwickelt wurde, um Forex-Markt-Sessions in einem Chart zu erkennen, zu berechnen und zu visualisieren – komplett mit Kerzen-ähnlichen Körpern, optionalen Dochten und Labels. Der Ansatz legt Wert auf Modularität, Lesbarkeit und Wiederverwendbarkeit, sodass die Funktion später in größere Frameworks wie den Market Periods Synchronizer EA integriert werden kann.
1. Einrichtung und Zweck des Headers
Jedes gute MQL5-Modul beginnt mit einem ordentlichen Header, der klar definiert, worum es sich bei dem Skript handelt, wer es geschrieben hat und wofür es gedacht ist. Das hilft anderen, Ihren Code zu verstehen, und sorgt für Entwicklungshygiene bei der Versionskontrolle.
Außerdem definieren wir ein kleines, aber leistungsstarkes Makro ARGB, mit dem wir auf einfache Weise Farben mit Transparenz erstellen können, die für das Überblenden mehrerer visueller Ebenen (z. B. sich überlappende Sitzungen in einem Chart) unerlässlich sind.
//+------------------------------------------------------------------+ //| SessionVisualizerTest.mqh | //| Modular class for Forex session OHLC visualization | //| Author: Clemence Benjamin | //| Version: 1.00 | //+------------------------------------------------------------------+ #property strict // ARGB Macro: Creates a color with alpha (transparency) #define ARGB(a,r,g,b) ((color)(((uchar)(a))<<24)|(((uchar)(r))<<16)|(((uchar)(g))<<8)|((uchar)(b))) #include <Object.mqh> // Provides access to chart object manipulation
Hier verwenden wir Object.mqh, weil der Visualizer mehrere Chart-Objekte (Rechtecke, Beschriftungen, Linien) erstellt und verwaltet. Das Makro ARGB() ermöglicht es uns, halbtransparente Farben wie ARGB(120,255,0,0) für eine durchsichtige rote Füllung zu erzeugen – wichtig für visuelle Überlagerungen. Aber später werden wir ARGB entfernen, um eine von CCanvas zu verwenden.
2. Sitzungsdefinitionen und Strukturaufbau
Bevor wir etwas zeichnen, müssen wir definieren, was eine „Sitzung“ in unserem Zusammenhang bedeutet. Jede Devisensitzung – Sydney, Tokio, London und New York – hat ihre eigenen Öffnungs- und Schließungszeiten. Wir verwenden eine Enumeration (enum), um sie symbolisch zu definieren, und eine Struktur (struct), um die Eigenschaften jeder Sitzung zu speichern, z. B. den Namen, die Zeit, die Farbe und ob sie gerade aktiv ist.
enum SESSION_TYPE { SESSION_SYDNEY = 0, // 22:00-07:00 GMT SESSION_TOKYO = 1, // 00:00-09:00 GMT SESSION_LONDON = 2, // 08:00-17:00 GMT SESSION_NEWYORK = 3 // 13:00-22:00 GMT }; struct SessionInfo { SESSION_TYPE type; string name; // Short code e.g., "LON" int open_hour; // GMT open hour int close_hour; // GMT close hour color sess_color; // Visual color on chart bool enabled; // Whether to render this session };
Die Verwendung von Enums stellt sicher, dass unser Code leicht zu lesen und später zu erweitern ist. Zum Beispiel würde das Hinzufügen einer „Custom Session“ nur einen zusätzlichen Eintrag und eine Strukturinitialisierung erfordern.
3. Eingabeparameter und Initialisierung der Klasse
Als Nächstes definieren wir Eingabeparameter und bereiten unseren Klassenkonstruktor vor. Dieses Setup ermöglicht es Händlern oder Entwicklern, Lookback-Perioden, Farben und Dochtverhalten direkt im Eingabedialog des EAs anzupassen.
input int InpSessionLookback = 10; // Days of historical sessions input bool InpShowSessionWicks = false; input int InpWickAlpha = 120; input color InpFillBull = clrLime; input color InpFillBear = clrPink; class CSessionVisualizer : public CObject { private: SessionInfo m_sessions[4]; int m_gmt_offset; string m_prefix; public: CSessionVisualizer(string prefix = "SESS_") : m_prefix(prefix) { // Initialize all session parameters (GMT-based) m_sessions[SESSION_SYDNEY] = {SESSION_SYDNEY, "SYD", 22, 7, clrAqua, true}; m_sessions[SESSION_TOKYO] = {SESSION_TOKYO, "TOK", 0, 9, clrYellow, true}; m_sessions[SESSION_LONDON] = {SESSION_LONDON, "LON", 8, 17, clrRed, true}; m_sessions[SESSION_NEWYORK]= {SESSION_NEWYORK,"NY", 13, 22, clrBlue, true}; // Basic GMT offset estimation MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); m_gmt_offset = -dt.hour % 24; } ~CSessionVisualizer() { DeleteAllSessionObjects(); }
Hier hilft m_prefix, Konflikte bei der Benennung von Objekten im Chart zu vermeiden, insbesondere wenn mehrere Werkzeuge gleichzeitig verwendet werden. Zum Beispiel kann jedes Objekt mit „SESS_LON_O_1730764800“ beschriftet sein.
4. Erfrischung und Zeichensitzungen
Die Methode RefreshSessions() ist das Herzstück des Visualizers. Es löscht alle früheren Sitzungszeichnungen und generiert sie über einen vom Nutzer definierten Rückblickzeitraum neu. Dieser modulare Aufbau ermöglicht es, die Darstellung bei jedem neuen Balken oder Timer-Tick zu aktualisieren, ohne das Chart zu überladen.
void RefreshSessions(int lookback_days = 10) { DeleteAllSessionObjects(); // Clear previously drawn sessions datetime end_time = TimeCurrent(); datetime start_time = end_time - (lookback_days * 86400); for (int day = 0; day < lookback_days; day++) { datetime day_start = start_time + (day * 86400); for (int s = 0; s < 4; s++) { if (!m_sessions[s].enabled) continue; DrawSessionForDay(m_sessions[s], day_start); } } ChartRedraw(); }
Diese Struktur ist aus Gründen der Effizienz und Modularität schleifenförmig aufgebaut. Jeder Tag und jede Sitzung wird einzeln verarbeitet – eine Technik, die überlappende Sitzungsartefakte oder fehlende Datenbereiche vermeidet.
5. Zeichnen von Einzelsitzungen
Die Funktion DrawSessionForDay() sorgt dafür, dass jede Sitzung tatsächlich gerendert wird. Es bestimmt die Eröffnungs- und Schließzeiten (angepasst an den GMT-Offset), ruft die Kursdaten für dieses Fenster ab und stellt die Sitzung als kerzenähnliches Rechteck mit optionalen Dochten dar.
void DrawSessionForDay(const SessionInfo &sess, datetime day_start) { MqlDateTime dt_open, dt_close; TimeToStruct(day_start, dt_open); dt_open.hour = sess.open_hour + m_gmt_offset; datetime t_open = StructToTime(dt_open); TimeToStruct(day_start, dt_close); dt_close.hour = sess.close_hour + m_gmt_offset; datetime t_close = StructToTime(dt_close); if (sess.close_hour < sess.open_hour) t_close += 86400; // Overnight fix double o,h,l,c; if (!GetOHLCInTimeRange(t_open, t_close, o,h,l,c)) return; color body_col = (c > o) ? InpFillBull : InpFillBear; string rect = m_prefix + sess.name + "_B_" + IntegerToString((int)t_open); ObjectCreate(0, rect, OBJ_RECTANGLE, 0, t_open, MathMin(o,c), t_close, MathMax(o,c)); ObjectSetInteger(0, rect, OBJPROP_BGCOLOR, body_col); ObjectSetInteger(0, rect, OBJPROP_FILL, true); ObjectSetInteger(0, rect, OBJPROP_BACK, true); }
Mit diesem Ansatz erhalten wir Sitzungsbalken, die sich wie Kerzen verhalten – eine neue Möglichkeit, Sitzungen als eigene Mikro-Zeitrahmen zu analysieren.
6. Abrufen von Sitzungs-OHLC-Daten
Die Funktion GetOHLCInTimeRange() extrahiert genaue Eröffnungs-, Höchst-, Tiefst- und Schlussdaten aus dem ausgewählten Zeitrahmen innerhalb der Dauer der jeweiligen Sitzung. Die Verwendung von iBarShift() gewährleistet eine exakte Ausrichtung auf vorhandene Balken des Charts.
bool GetOHLCInTimeRange(datetime start, datetime end, double &open, double &high, double &low, double &close) { int shift_start = iBarShift(_Symbol, _Period, start, false); int shift_end = iBarShift(_Symbol, _Period, end, false); if (shift_start < 0 || shift_end < 0) return false; int count = shift_start - shift_end + 1; if (count <= 0) return false; double o[],h[],l[],c[]; ArraySetAsSeries(o,true); ArraySetAsSeries(h,true); ArraySetAsSeries(l,true); ArraySetAsSeries(c,true); CopyOpen(_Symbol,_Period,shift_end,count,o); CopyHigh(_Symbol,_Period,shift_end,count,h); CopyLow(_Symbol,_Period,shift_end,count,l); CopyClose(_Symbol,_Period,shift_end,count,c); open = o[count-1]; high = h[ArrayMaximum(h,0,count)]; low = l[ArrayMinimum(l,0,count)]; close = c[0]; return true; }
Es liefert die OHLC-Daten der „Kerzen-Sitzung“ aus Standardkursreihen – ohne erneute Abfrage externer Quellen.
7. Bereinigung und Objektverwaltung
Guter Code räumt immer hinter sich selbst auf. Die Funktion DeleteAllSessionObjects() stellt sicher, dass alte Objekte beim Aktualisieren oder Deinitialisieren entfernt werden. Ohne diese Funktion hätten Sie nach langen Testsitzungen Tausende von übrig gebliebenen Rechtecken.
void DeleteAllSessionObjects() { int total = ObjectsTotal(0); for (int i = total - 1; i >= 0; i--) { string name = ObjectName(0, i); if (StringFind(name, m_prefix) == 0) ObjectDelete(0, name); } }
Entwicklung des Test-EAs:
Wir beginnen mit einem kompakten Header und dem Include. Dieser EA ist absichtlich winzig: Er startet nur den Visualizer, startet einen Timer für regelmäßige Aktualisierungen (damit Sie sehen können, wie sich die Sitzungen aktualisieren, während der Markt läuft) und räumt beim Beenden auf. Die Beibehaltung kleiner Test-Strukturen hilft, Probleme zu isolieren und das Verhalten von Klassen vor der Integration in größere Systeme zu validieren.
//+------------------------------------------------------------------+ //| SessionTest.mq5 - Standalone test for CSessionVisualizer | //| Purpose: lightweight EA to exercise the SessionVisualizer class | //+------------------------------------------------------------------+ #property strict #include <SessionVisualizerTest.mqh>
Als Nächstes deklarieren wir die Laufzeitinstanz. Die Verwendung eines Zeigers und „new“ ermöglicht eine explizite Kontrolle über die Lebensdauer: Wir erstellen den Visualizer in OnInit() und löschen ihn in OnDeinit(), sodass der Destruktor sofort ausgeführt wird und die von der Klasse erstellten Objekte entfernt. Dieses Muster ist praktisch für Tests und spiegelt wider, wie Sie Objekte in größeren EAs verwalten würden, die Subsysteme dynamisch erstellen/zerstören.
// runtime pointer to the session visualizer CSessionVisualizer *g_session_vis = NULL;
Mit OnInit() wird der Visualizer aufgebaut, ein Aktualisierungs-Timer gesetzt und die erste Zeichnung durchgeführt. Das Beispiel verwendet einen 30-Sekunden-Demo-Timer, der kurz genug ist, um häufig Aktualisierungen zu sehen, aber lang genug, um eine übermäßige CPU-Auslastung zu vermeiden. Wir übergeben dem Visualizer ein eindeutiges Präfix, damit seine Objekte eindeutig benannt werden können (praktisch, wenn mehrere Tools mit demselben Chart getestet werden). Durch den sofortigen Aufruf von RefreshSessions(5) werden die Sitzungen von fünf Tagen wiedergegeben, sodass Sie die historische Aufzeichnung sofort überprüfen können.
int OnInit() { // allocate the session visualizer with a test-specific prefix to avoid name clashes g_session_vis = new CSessionVisualizer("TEST_SESS_"); // Refresh every 30 seconds during testing (demo). Adjust for production usage. EventSetTimer(30); // initial rendering of the last 5 days (quick sanity check) if(g_session_vis != NULL) g_session_vis.RefreshSessions(5); return(INIT_SUCCEEDED); }
Hinweis: Vor dem Aufruf der Methode wird eine Null-Prüfung durchgeführt. Wenn new aus irgendeinem Grund ausfällt (was auf normalen Desktops sehr selten vorkommt), verhindert dies einen Absturz und macht es einfacher, den Fehler in den Protokollen zu erkennen.
OnTimer() sorgt für periodische Aktualisierungen. Für die visuelle Darstellung von Sitzungen ist dies nützlich, da sich die OHLCs von Sitzungen auf Live-Balken leicht ändern können (je nach dem Zeitrahmen, den Sie für die OHLC-Berechnung verwenden), und häufiges Neuzeichnen ermöglichen es Ihnen, die Entwicklung von Dochten und Körpern von Sitzungen zu beobachten. In einem produktiven EA ziehen Sie es vielleicht vor, nur bei neuen Balken oder in einem längeren Intervall zu aktualisieren, aber für Tests sind 30 Sekunden ausreichend.
void OnTimer() { // periodic refresh of the last 5 days if(g_session_vis != NULL) g_session_vis.RefreshSessions(5); }
OnDeinit() räumt auf: Wir beenden den Timer und löschen das Visualizer-Objekt. Das Löschen des Objekts ruft seinen Destruktor auf, der (wie implementiert) alle Sitzungsobjekte entfernt, die die Klasse auf dem Chart erstellt hat, um sicherzustellen, dass das Chart nach dem Test sauber bleibt. Eine explizite Bereinigung ist in Umgebungen mit wiederholten Ladevorgängen (Laden/Entladen vieler EAs) unerlässlich, um eine Aufblähung des Charts und verweilende Objekte zu vermeiden.
void OnDeinit(const int reason) { EventKillTimer(); if(g_session_vis != NULL) { // destructor will call DeleteAllSessionObjects() delete g_session_vis; g_session_vis = NULL; } }
Erstes Testergebnis:
Unsere anfängliche Testphase war unkompliziert – nachdem wir den Session Test EA erfolgreich kompiliert hatten, haben wir ihn einfach an ein Live-Chart des MetaTrader 5 angehängt. Das Ergebnis war eine sofortige und saubere Visualisierung der Devisensitzungen. In der folgenden Abbildung können Sie deutlich sehen, wie sich die Sitzungen überschneiden, wobei jede durch eine eigene Farbe gekennzeichnet ist, um sie leicht zu identifizieren. Die ausgefüllten Sitzungsrechtecke zeigen auch die Stimmung an: Lindgrün zeigt einen gestiegenen Sitzungsschluss an, während Rosa einen fallenden markiert. Diese farbkodierte Visualisierung macht es mühelos möglich, die vorherrschende Richtung jeder Handelssitzung auf einen Blick zu erkennen.

Abb. 2. Sitzung Test
Schritt 2-Integrationsphase
Jedes großartige Handelsinstrument durchläuft eine Phase der Transformation – von einem experimentellen Skript zu einem voll integrierten Produktionsmodul. Unser Session Visualizer ist da keine Ausnahme. Ursprünglich konzentrierte sich der Header des Tests (SessionVisualizerTest.mqh) auf die Schaffung der Grundlagen: Definition von Handelssitzungen, Abrufen von OHLC-Daten und Zeichnen von farbigen Rechtecken, die jede Forex-Marktperiode darstellen. Während sie in der Testphase ihren Zweck gut erfüllte, musste die Produktionsintegration in unserem Market Periods Synchronizer EA im Hinblick auf bessere visuelle Klarheit, Erweiterbarkeit und Kompatibilität verfeinert werden.
Warum wir uns von den gefüllten Sitzungskörpern abgewandt haben
In der Testversion wurden die Sitzungen durch einfarbige Rechtecke dargestellt, wobei die Farben der Sitzungen verwendet wurden, um jeden aktiven Handelsblock hervorzuheben. Dieser Ansatz sah zwar lebendig aus, wurde aber schnell unübersichtlich – vor allem, wenn sich überlappende Sitzungen oder historische Ebenen zusammen angezeigt wurden. Der Füllstil verdeckte auch die darunter liegenden Chart-Details, was einem unserer Ziele zuwiderlief: Kontext ohne Unübersichtlichkeit zu bieten.
Daher führen wir in der neuen Version ungefüllte Körperrechtecke mit gestrichelten Rändern ein. Sie wirken wie hohle Kerzen, die den Eröffnungs- und Schlussbereich jeder Sitzung markieren, ohne das Kurs-Chart abzudecken. Das gewählte Design macht das Chart atmungsaktiv und sorgt dafür, dass der Händler das Kursgeschehen unter den Sitzungsmarkierungen immer noch deutlich erkennen kann.
Für Entwickler ist dies eine wichtige Lektion: Manchmal ist weniger Farbe gleich mehr Information. Indem wir die undurchsichtigen Füllungen entfernten, gewannen wir an Sichtbarkeit und Professionalität in der Präsentation.
Integration von CCanvas und ARGB: Eine visuelle Maschine
Eine subtile, aber wichtige technische Änderung in der neuen Kopfzeile ist die Verwendung von #include <Canvas/Canvas.mqh> für ARGB-basierte Grafiken. Dadurch wird die Kompatibilität mit der bestehenden CCanvas-Funktion unseres EA sichergestellt, die auch das Hintergrund-Rendering und die Transparenz für andere Module verwaltet. Anstatt ARGB separat neu zu definieren, wird dies in der neuen Version an die Canvas-Bibliothek delegiert, wodurch Konsistenz für alle vom EA verwendeten Grafiken geschaffen wird.
Diese Entscheidung ist sowohl architektonisch als auch praktisch begründet. Beim Aufbau modularer EAs, die Komponenten wiederverwenden, können doppelte ARGB-Makros zu Konflikten oder unvorhersehbarem Verhalten führen, insbesondere wenn eine Komponente Transparenz anders definiert. Die Verwendung einer einzigen, gemeinsam genutzten Engine – Canvas – hilft bei der Aufrechterhaltung einer einheitlichen Rendering-Logik und reduziert den Wartungsaufwand.
Vereinfacht ausgedrückt, deklarieren wir die Canvas-Einbindung global, sodass jede visuelle Komponente – ob Sitzungsboxen, Dochte oder Overlays – dieselbe grafische „Sprache“ spricht.
Sitzungen neu definieren: Von Farbblöcken zu Marktrahmen
Die Struktur der Sitzungsdarstellung bleibt ähnlich: Wir verwenden weiterhin eine SessionInfo-Struktur, die Name, Typ, Öffnungs-/Schließungszeiten und Farbe enthält. Wir haben jedoch die Art und Weise, wie diese Attribute visualisiert werden, neu gestaltet.
Jede Sitzung wird nun gerendert:
- Ein gestricheltes Rechteck, das die Preisspanne zwischen Eröffnung und Schließung markiert
- Optionale grau gefüllte Rechtecke der „Dochte“, die die gesamte Hoch-Tief-Auslenkung der Sitzung anzeigen
- Texte und vertikale Linien zur Kennzeichnung der Öffnungs- und Schließzeiten
- Vorder-/Hintergrundüberlagerung, sodass Rechtecke hinter den Preiskerzen bleiben, während Linien und Beschriftungen für den Nutzer sichtbar bleiben
So entsteht ein analytisches, aber elegantes Erscheinungsbild, wie eine transparente Blaupause des Marktrhythmus. Händler können sehen, welche Sitzungen expandieren oder schrumpfen, ohne dabei Details im Chart zu verlieren. Das neue Konzept des „Unausgefüllten“ ist eher ein Rahmen für den Markt als eine Abdeckung.
Laufzeit-Konfiguration: Den Visualizer dynamisch machen
Um Nutzern und EAs mehr Flexibilität zu bieten, haben wir zuvor statische Einstellungen in Laufzeitvariablen umgewandelt. Zum Beispiel sind m_show_wicks, m_wick_alpha und m_gmt_offset jetzt global konfigurierbar. Ein Händler oder Entwickler kann die Dochtsichtbarkeit umschalten, die Transparenz ändern oder den Zeitversatz ohne Neukompilierung anpassen.
Sie werden feststellen, dass Funktionen wie
void SetShowWicks(bool show) { m_show_wicks = show; } void SetWickAlpha(int alpha) { m_wick_alpha = MathMax(0, MathMin(255, alpha)); } void SetGMTOffset(int offset){ m_gmt_offset = offset; }
Diese kleinen, aber sinnvollen Ergänzungen ermöglichen eine Echtzeit-Interaktion zwischen der EA-Schnittstelle und der Visualisierungsschicht – ein grundlegender Aspekt des modernen modularen Designs in MQL5.
Kontextuelle Einblicke: Aktuelle und vorherige Sitzungen anzeigen
Im Gegensatz zur Testversion, die nur statische historische Sitzungen anzeigte, werden in der neuen Kopfzeile die aktuellen und jüngsten Sitzungen angezeigt. Während des Live-Handels erkennt das System, welche Sitzung aktiv ist, und blendet die Kennzeichnung „Live“ ein. Außerdem bleibt die gerade beendete Sitzung noch eine Stunde lang sichtbar, sodass man sich schnell einen Überblick über die Ergebnisse der letzten Sitzung verschaffen kann.
Nachdem wir nun die Anpassungen für eine effektive Integration verstanden haben, werde ich den neuen SessionVisualizer-Header im Detail aufschlüsseln und erklären. Danach werde ich die Integration in den Expert Advisor (EA) erörtern und die neuen EA-Funktionen hervorheben, die Händlern dabei helfen sollen, die Marktsitzungsperioden effizienter zu nutzen, um bessere Handelsentscheidungen zu treffen.
1) Modulbeschreibung und Strenge der Kompilierung
Wir beginnen mit einer klaren Beschreibung, denn veröffentlichter Code ist auch eine Dokumentation. Die Versionierung teilt den Lesern mit, was neu ist (z.B. „unfilled rectangles, dashed borders, and grey wicks“, unausgefüllte Rechtecke, gestrichelte Ränder und graue Dochte), und #property strict erzwingt moderne MQL5-Typsicherheit und fängt stille Fehler ab, die oft in experimentellen Headern durchrutschen.
//+------------------------------------------------------------------+ //| SessionVisualizer.mqh | //| Modular class for Forex session OHLC visualization | //| Copyright 2025: Clemence Benjamin | //| Version: 1.02 | //+------------------------------------------------------------------+ #property strict
2) Gemeinsame Abhängigkeiten (Objekt-API + einheitliches ARGB über Canvas)
In der Testphase ist es üblich, ein eigenes ARGB-Makro zu erstellen; in der Produktion orientieren wir uns an der Single Visual Engine des EA, um Konflikte zu vermeiden. Durch die Einbindung von Canvas.mqh erhalten wir das kanonische ARGB() und eine nahtlose Kompatibilität mit anderen Zeichenfunktionen im Market Periods Synchronizer EA.
#include <Object.mqh> #include <Canvas/Canvas.mqh> // for ARGB()
3) Sitzungsdomänenmodell (enum + struct)
Wir formalisieren den Problemraum: vier Hauptsitzungen, menschenfreundliche Namen und ein einfacher SessionInfo-Container. Selbst wenn eine Eigenschaft nicht mehr überall verwendet wird (z. B. färbt sess_color nicht mehr die Füllung des Körpers), behalten wir sie bei – die Farben steuern weiterhin die Beschriftungen/Zeilen und erhalten die Abwärtskompatibilität mit früheren Werkzeugen und Nutzergewohnheiten.
//---------------------------------------------- // Session enum and info //---------------------------------------------- enum SESSION_TYPE { SESSION_SYDNEY = 0, // 22:00-07:00 GMT SESSION_TOKYO = 1, // 00:00-09:00 GMT SESSION_LONDON = 2, // 08:00-17:00 GMT SESSION_NEWYORK= 3 // 13:00-22:00 GMT }; struct SessionInfo { SESSION_TYPE type; string name; // "SYD","TOK","LON","NY" int open_hour; // GMT open hour int close_hour; // GMT close hour color sess_color; // base tint (not used for border now) bool enabled; };
4) Laufzeitparameter (EA-gesteuerte „Knöpfe“)
Wir deklarieren globale, von EA abstimmbare Variablen, damit Funktionen ohne Neukompilierung umgeschaltet werden können. Lookback regelt die Verlaufsdichte, m_show_wicks/m_wick_alpha steuert die Sichtbarkeit und Transparenz von Dochtbereichen, und m_gmt_offset ermöglicht dem EA die Harmonisierung von Serverzeit und GMT – ein klassischer Schmerzpunkt in der Praxis.
//---------------------------------------------- // Runtime params (configured by EA) //---------------------------------------------- int m_session_lookback = 10; bool m_show_wicks = false; int m_wick_alpha = 120; int m_gmt_offset = 0;
5) Klassenmantel und Konstruktorvorgaben
Wir halten die öffentliche API kompakt und den privaten Status explizit. Im Konstruktor initialisieren wir kanonische GMT-Zeitpläne und lesbare Drei-Buchstaben-Tags. Sie werden feststellen, dass wir ein m_prefix speichern: Dies ist das Geheimnis für eine sichere Objektbenennung und späteres sauberes Löschen – keine versehentlichen Kollisionen mit anderen Chart-Objekten.
class CSessionVisualizer : public CObject { private: SessionInfo m_sessions[4]; string m_prefix; public: CSessionVisualizer(string prefix="SESS_") : m_prefix(prefix) { // Defaults (GMT schedule) m_sessions[SESSION_SYDNEY] .type=SESSION_SYDNEY; m_sessions[SESSION_SYDNEY] .name="SYD"; m_sessions[SESSION_SYDNEY] .open_hour=22; m_sessions[SESSION_SYDNEY] .close_hour=7; m_sessions[SESSION_SYDNEY] .sess_color=clrAqua; m_sessions[SESSION_SYDNEY] .enabled=true; m_sessions[SESSION_TOKYO] .type=SESSION_TOKYO; m_sessions[SESSION_TOKYO] .name="TOK"; m_sessions[SESSION_TOKYO] .open_hour=0; m_sessions[SESSION_TOKYO] .close_hour=9; m_sessions[SESSION_TOKYO] .sess_color=clrYellow; m_sessions[SESSION_TOKYO] .enabled=true; m_sessions[SESSION_LONDON] .type=SESSION_LONDON; m_sessions[SESSION_LONDON] .name="LON"; m_sessions[SESSION_LONDON] .open_hour=8; m_sessions[SESSION_LONDON] .close_hour=17; m_sessions[SESSION_LONDON] .sess_color=clrRed; m_sessions[SESSION_LONDON] .enabled=true; m_sessions[SESSION_NEWYORK].type=SESSION_NEWYORK; m_sessions[SESSION_NEWYORK].name="NY"; m_sessions[SESSION_NEWYORK].open_hour=13; m_sessions[SESSION_NEWYORK].close_hour=22; m_sessions[SESSION_NEWYORK].sess_color=clrBlue; m_sessions[SESSION_NEWYORK].enabled=true; } ~CSessionVisualizer() { DeleteAllSessionObjects(); }
6) Öffentliche API: Aktualisieren + Umschalten (für Live-Charts)
RefreshSessions() ist der Ein-Knopf-Neuaufbau – löschen, Verlauf neu zeichnen, dann das Live-Fenster zeichnen. Wir stellen winzige, ausdrucksstarke Setter zur Verfügung, sodass ein EA-Panel Sitzungen umschalten, Themen neu einfärben oder GMT im laufenden Betrieb synchronisieren kann. Eine bemerkenswerte Änderung gegenüber dem Test-Header: keine Körperfüllungen; SetFillColors() ist jetzt ein No-op, das nur aus Gründen der Abwärtskompatibilität beibehalten wurde.
//--------------------------------------------------------------- // PUBLIC API //--------------------------------------------------------------- void RefreshSessions(int lookback_days=10) { m_session_lookback = lookback_days; DeleteAllSessionObjects(); const datetime now = TimeCurrent(); const datetime start = now - (m_session_lookback*86400); // Historical completed sessions for(int d=0; d<m_session_lookback; ++d) { datetime day_start = start + d*86400; for(int s=0; s<4; ++s) { if(!m_sessions[s].enabled) continue; DrawHistoricalSessionForDay(m_sessions[s], day_start); } } // Active session windows DrawCurrentSessions(); ChartRedraw(); } void SetSessionEnabled(SESSION_TYPE type, bool enabled) { for(int i=0;i<4;i++) if(m_sessions[i].type==type){ m_sessions[i].enabled=enabled; break; } } void SetSessionColor(SESSION_TYPE type, color col) { for(int i=0;i<4;i++) if(m_sessions[i].type==type){ m_sessions[i].sess_color=col; break; } } // Kept for compatibility with EA; ignored (we no longer fill bodies). void SetFillColors(color /*bull*/, color /*bear*/) { /* no-op */ } void SetShowWicks(bool show) { m_show_wicks = show; } void SetWickAlpha(int alpha) { m_wick_alpha = MathMax(0, MathMin(255, alpha)); } void SetGMTOffset(int offset){ m_gmt_offset = offset; } // public cleanup void ClearAll() { DeleteAllSessionObjects(); }
7) Historische Zeichnung (nur abgeschlossene Sitzungen)
Ein häufiges Fußangeln in Sitzungswerkzeugen ist das Zeichnen von Teildaten in den „Verlauf“. Hier berechnen wir open/close mit m_gmt_offset, behandeln den Fall „wrap past midnight“ und lassen dann alles aus, was nicht vollständig in der Vergangenheit liegt. Erst nachdem wir ein vollständiges Fenster bestätigt haben, berechnen wir die OHLC und rendern.
private: //--------------------------------------------------------------- // CORE DRAWING //--------------------------------------------------------------- void DrawHistoricalSessionForDay(const SessionInfo &sess, datetime day_start) { // build open/close datetimes adjusted by GMT offset MqlDateTime dto, dtc; TimeToStruct(day_start, dto); dtc = dto; dto.hour = sess.open_hour + m_gmt_offset; dto.min=0; dto.sec=0; dtc.hour = sess.close_hour + m_gmt_offset; dtc.min=0; dtc.sec=0; datetime t_open = StructToTime(dto); datetime t_close = StructToTime(dtc); if(sess.close_hour < sess.open_hour) t_close += 86400; // wraps midnight // Only draw if fully in the past if(t_close >= TimeCurrent()) return; if(t_close < TimeCurrent() - (m_session_lookback*86400)) return; double o,h,l,c; if(!GetOHLCInTimeRange(t_open, t_close, o,h,l,c)) return; DrawSessionVisuals(sess, t_open, t_close, o,h,l,c, /*is_current=*/false); }
8) Bearbeitung von Live-Sitzungen (und 1 Stunde Nachfrist für die letzte Sitzung)
Wir ermitteln die aktive Sitzung in Echtzeit, indem wir das heutige Fenster erstellen und um die Nachtsitzungen bereinigen. Wenn wir drinnen sind, streamen wir OHLC bis jetzt, schieben h/l mit dem aktuellen Gebot, sodass die Box das Live-Band widerspiegelt, und kennzeichnen es mit „(Live)“. Außerdem behalten wir die soeben beendete Sitzung für eine weitere Stunde auf dem Bildschirm – sehr praktisch bei Rollovers, wenn Händler einen Überblick haben wollen.
void DrawCurrentSessions() { const datetime now = TimeCurrent(); MqlDateTime cur; TimeToStruct(now, cur); for(int i=0;i<4;i++) { if(!m_sessions[i].enabled) continue; const SessionInfo sess = m_sessions[i]; // compute session window for "today" (adjusted for wrap) MqlDateTime ds, de; TimeToStruct(now, ds); de = ds; ds.hour = sess.open_hour + m_gmt_offset; ds.min=0; ds.sec=0; de.hour = sess.close_hour + m_gmt_offset; de.min=0; de.sec=0; datetime session_start = StructToTime(ds); datetime session_end = StructToTime(de); if(sess.close_hour < sess.open_hour) { if(cur.hour >= sess.open_hour + m_gmt_offset) session_end += 86400; // ends tomorrow else session_start -= 86400; // started yesterday } if(now >= session_start && now <= session_end) { // pull OHLC until now (close = current) double o,h,l,c; if(!GetOHLCInTimeRange(session_start, now, o,h,l,c)) o = h = l = c = SymbolInfoDouble(_Symbol, SYMBOL_BID); const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); h = MathMax(h, bid); l = MathMin(l, bid); c = bid; DrawSessionVisuals(sess, session_start, session_end, o,h,l,c, /*is_current=*/true); } else if(now > session_end && now <= session_end + 3600) { // Keep just-closed session visible for 1 hour double o,h,l,c; if(!GetOHLCInTimeRange(session_start, session_end, o,h,l,c)) continue; DrawSessionVisuals(sess, session_start, session_end, o,h,l,c, /*is_current=*/false); } } }
9) Visuelle Grammatik: ungefüllte Körper, gestrichelte Ränder, optionaler grauer Docht
Dies ist die Signaturänderung aus dem Testkopf. Die Körper werden nicht ausgefüllt und hinter den Preis gesetzt (OBJPROP_BACK=true), damit nichts die Kerzen verdeckt. Die Grenzen kodieren die Stimmung (dunkelgrün für aufwärts, rot für abwärts; ansonsten die Sitzungsfarbe). Dochte sind, wenn sie aktiviert sind, hellgraue ARGB-Rechtecke, die im Hintergrund gezeichnet werden, um den vollen Hoch-Tief-Ausschlag ohne Unordnung zu zeigen. Beschriftungen und Zeitleisten bleiben aus Gründen der Übersichtlichkeit im Vordergrund.
// Renders: // - unfilled body rectangle (Open..Close) with dashed border // * border = clrDarkGreen if bullish, clrRed if bearish (Neutral uses session color) // - optional GREY filled wick rectangles in background for visibility // - open/close vlines + labels in foreground (thin) void DrawSessionVisuals(const SessionInfo &sess, datetime t_open, datetime t_close, double o,double h,double l,double c, bool is_current) { const bool bullish = (c > o); const bool bearish = (c < o); // ---------- vertical lines ---------- string vopen = m_prefix + sess.name + "_O" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, vopen) == -1) ObjectCreate(0, vopen, OBJ_VLINE, 0, t_open, 0); ObjectSetInteger(0, vopen, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, vopen, OBJPROP_WIDTH, is_current?2:1); ObjectSetInteger(0, vopen, OBJPROP_STYLE, is_current?STYLE_SOLID:STYLE_DASH); ObjectSetInteger(0, vopen, OBJPROP_BACK, false); ObjectSetInteger(0, vopen, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, vopen, OBJPROP_HIDDEN, false); string vclose = m_prefix + sess.name + "_C" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_close); if(ObjectFind(0, vclose) == -1) ObjectCreate(0, vclose, OBJ_VLINE, 0, t_close, 0); ObjectSetInteger(0, vclose, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, vclose, OBJPROP_WIDTH, is_current?2:1); ObjectSetInteger(0, vclose, OBJPROP_STYLE, is_current?STYLE_SOLID:STYLE_DASH); ObjectSetInteger(0, vclose, OBJPROP_BACK, false); ObjectSetInteger(0, vclose, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, vclose, OBJPROP_HIDDEN, false); // ---------- body rectangle (UNFILLED, dashed) ---------- const double body_top = MathMax(o,c); const double body_bot = MathMin(o,c); color border_col = sess.sess_color; // neutral fallback if(bullish) border_col = clrDarkGreen; else if(bearish) border_col = clrRed; string rect = m_prefix + sess.name + "_R" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, rect) == -1) { if(!ObjectCreate(0, rect, OBJ_RECTANGLE, 0, t_open, body_bot, t_close, body_top)) PrintFormat("Failed to create session rectangle %s err=%d", rect, GetLastError()); } // unfilled rectangle with dashed edge, kept in background ObjectSetInteger(0, rect, OBJPROP_FILL, false); ObjectSetInteger(0, rect, OBJPROP_COLOR, border_col); ObjectSetInteger(0, rect, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, rect, OBJPROP_WIDTH, 1); ObjectSetInteger(0, rect, OBJPROP_BACK, true); ObjectSetInteger(0, rect, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, rect, OBJPROP_HIDDEN, false); // keep coordinates fresh ObjectMove(0, rect, 0, t_open, body_bot); ObjectMove(0, rect, 1, t_close, body_top); // ---------- WICKS (optional) as GREY filled rectangles in background ---------- if(m_show_wicks) { uint wick_col = ARGB(m_wick_alpha, 128,128,128); // semi-transparent grey // Upper wick: [body_top .. high] string wu = m_prefix + sess.name + "_WU" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(h > body_top) { if(ObjectFind(0, wu) == -1) { if(!ObjectCreate(0, wu, OBJ_RECTANGLE, 0, t_open, body_top, t_close, h)) PrintFormat("Failed to create upper wick %s err=%d", wu, GetLastError()); } ObjectSetInteger(0, wu, OBJPROP_BGCOLOR, wick_col); ObjectSetInteger(0, wu, OBJPROP_COLOR, wick_col); ObjectSetInteger(0, wu, OBJPROP_FILL, true); ObjectSetInteger(0, wu, OBJPROP_BACK, true); ObjectSetInteger(0, wu, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, wu, OBJPROP_HIDDEN, false); ObjectMove(0, wu, 0, t_open, body_top); ObjectMove(0, wu, 1, t_close, h); } else ObjectDelete(0, wu); // Lower wick: [low .. body_bot] string wl = m_prefix + sess.name + "_WL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(l < body_bot) { if(ObjectFind(0, wl) == -1) { if(!ObjectCreate(0, wl, OBJ_RECTANGLE, 0, t_open, l, t_close, body_bot)) PrintFormat("Failed to create lower wick %s err=%d", wl, GetLastError()); } ObjectSetInteger(0, wl, OBJPROP_BGCOLOR, wick_col); ObjectSetInteger(0, wl, OBJPROP_COLOR, wick_col); ObjectSetInteger(0, wl, OBJPROP_FILL, true); ObjectSetInteger(0, wl, OBJPROP_BACK, true); ObjectSetInteger(0, wl, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, wl, OBJPROP_HIDDEN, false); ObjectMove(0, wl, 0, t_open, l); ObjectMove(0, wl, 1, t_close, body_bot); } else ObjectDelete(0, wl); } else { // Ensure wicks are removed if disabled ObjectDelete(0, m_prefix + sess.name + "_WU" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open)); ObjectDelete(0, m_prefix + sess.name + "_WL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open)); } // ---------- Labels in foreground ---------- string session_tag = sess.name + (is_current ? " (Live)" : ""); string lbl_o = m_prefix + sess.name + "_OL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_open); if(ObjectFind(0, lbl_o) == -1) ObjectCreate(0, lbl_o, OBJ_TEXT, 0, t_open, o); ObjectSetString (0, lbl_o, OBJPROP_TEXT, session_tag + " Open"); ObjectSetInteger(0, lbl_o, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, lbl_o, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, lbl_o, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, lbl_o, OBJPROP_HIDDEN, false); ObjectSetInteger(0, lbl_o, OBJPROP_BACK, false); string lbl_c = m_prefix + sess.name + "_CL" + (is_current?"_CUR_":"_HIS_") + IntegerToString((int)t_close); if(ObjectFind(0, lbl_c) == -1) ObjectCreate(0, lbl_c, OBJ_TEXT, 0, t_close, c); ObjectSetString (0, lbl_c, OBJPROP_TEXT, session_tag + " Close"); ObjectSetInteger(0, lbl_c, OBJPROP_COLOR, sess.sess_color); ObjectSetInteger(0, lbl_c, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, lbl_c, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, lbl_c, OBJPROP_HIDDEN, false); ObjectSetInteger(0, lbl_c, OBJPROP_BACK, false); }
10) Robuste OHLC-Aggregation (auf Basis von Balkenverschiebung)
Um die Korrektheit über mehrere Zeitrahmen hinweg zu gewährleisten, berechnen wir die Sitzungs-OHLC mit Hilfe von iBarShift-Grenzen und holen eine kompakte Scheibe von Arrays, die dann mit ArrayMaximum/Minimum aggregiert werden. Dadurch werden Fehler vermieden, die sich aus dem Zusammenhang ergeben, und es funktioniert auch, wenn die Granularität des Zeitrahmens nicht perfekt mit den Sitzungsrändern übereinstimmt.
//--------------------------------------------------------------- // Data helpers //--------------------------------------------------------------- bool GetOHLCInTimeRange(datetime start, datetime end, double &open, double &high, double &low, double &close) { int shift_start = iBarShift(_Symbol, _Period, start, false); int shift_end = iBarShift(_Symbol, _Period, end, false); if(shift_start < 0 || shift_end < 0) return false; int bars = shift_start - shift_end + 1; if(bars <= 0) return false; double opens[], highs[], lows[], closes[]; ArraySetAsSeries(opens, true); ArraySetAsSeries(highs, true); ArraySetAsSeries(lows, true); ArraySetAsSeries(closes, true); if(CopyOpen (_Symbol,_Period, shift_end, bars, opens) != bars) return false; if(CopyHigh (_Symbol,_Period, shift_end, bars, highs) != bars) return false; if(CopyLow (_Symbol,_Period, shift_end, bars, lows) != bars) return false; if(CopyClose(_Symbol,_Period, shift_end, bars, closes) != bars) return false; open = opens[bars-1]; high = highs[ArrayMaximum(highs,0,bars)]; low = lows [ArrayMinimum(lows ,0,bars)]; close = closes[0]; return true; }
11) Deterministische Bereinigung (prefix-scoped GC)
Professionelle Chart-Werkzeuge müssen die Bühne sauber hinterlassen. Beim Aktualisieren oder Entfernen von EA durchlaufen wir die Objekte in umgekehrter Reihenfolge und löschen nur diejenigen, die unser m_prefix tragen. Dies ist eine höfliche Koexistenz mit anderen Indikatoren/EAs und verhindert „Geisterzeichnungen“, die die Nutzer frustrieren.
//--------------------------------------------------------------- // Cleanup //--------------------------------------------------------------- void DeleteAllSessionObjects() { int total = ObjectsTotal(0); for(int i = total-1; i >= 0; --i) { string name = ObjectName(0, i); if(StringFind(name, m_prefix) == 0) ObjectDelete(0, name); } } };
Integration von CSessionVisualizer und der Sitzungsinformationen in MarketPeriodsSynchronizerEA
Vor diesem Upgrade konzentrierte sich unsere vorherige EA-Version auf die Haupt-/Nebenperiodenstruktur, Füllungen der Körper, Dochte und UI-Steuerungen – ohne Vorstellung von Marktsitzungen oder Sitzungszusammenfassungen. In dieser Version fügen wir die neue Klasse CSessionVisualizer ein und fügen ein kompaktes „Sitzungsinformationen“-Panel hinzu, sowie ordentliche Steuerungskontrollen, sodass Sitzungen und ihre Dochthighlights unabhängig von den Hauptperiodenbildern gesteuert werden können. Im Folgenden werde ich Sie durch die Integration führen, als ob wir den früheren EA Schritt für Schritt weiterentwickeln würden, wobei ich die Gründe für jede Änderung nenne und den genauen Ausschnitt einfüge, der sie implementiert. (Zur Information: Die frühere EA-Basisversion ist v1.01 ohne Sitzungen und ohne das Info-Panel.
1) Bringen Sie den Session Visualizer in den EA
Wir fügen den Header für unseren neuen Sitzungsrenderer zusammen mit dem Canvas-Helper ein. Die Testkopfzeile verwendet bereits CSessionVisualizer mit ungefüllten, gestrichelten Körperrechtecken und optionalen grauen Dochtfüllungen im Hintergrund, sodass sie optisch nicht mit unseren HTF-Overlays kollidiert. Wir halten Includes minimal und explizit, um ODR/Namenskollisionen zu vermeiden.
#include <Canvas/Canvas.mqh> // Canvas helper library (expects Canvas.mqh to be present) #include <SessionVisualizer.mqh> // Integrated Session Visualizer class
2) Neue Eingaben für Sitzungen und die Infotafel
Inputs sind die Verträge mit den Nutzern. Wir fügen spezielle Schalter für die Anzeige von Sitzungen, ihr Rückblickfenster, den GMT-Offset des Brokers (damit die Sitzungen übereinstimmen), Dochtsichtbarkeit/Alpha nur für Sitzungen und das Flag für das Panel „Sitzungsinformationen“ hinzu. Beachten Sie, dass wir absichtlich die Dochteinstellungen für Majors und Sessions trennen – Händler wollen oft Dochte für das eine, aber nicht für das andere.
// Session inputs input bool InpShowSessions = true; // Show Forex sessions input int InpSessionLookback = 10; // Session lookback (days) input int InpGMTOffset = 0; // GMT offset (broker hours) // Wick visualization (separated for majors and sessions) input bool InpShowSessionWicks = false; // Show wick regions for sessions input int InpSessionWickAlpha = 120; // Alpha transparency for session wicks 0..255 // Session information panel input bool InpShowSessionInfo = true; // Show session info panel
Frühere Versionen hatten keine sitzungsspezifischen Eingaben; alles drehte sich um Majors/Minors und einen Docht-Knopf. Jetzt entkoppeln wir diese Bereiche, damit das Layout auch dann sauber bleibt, wenn beide Funktionen aktiv sind.
3) Globale Laufzeitdaten spiegeln die neuen Eingaben wider
Das Dashboard des EA ändert den Zustand zur Laufzeit, daher behalten wir veränderbare Kopien der Eingaben. Hier fügen wir g_ShowSessions, Session-Wick-Laufzeit (g_ShowSessionWicks, g_SessionWickAlpha) und ein Flag für das Info-Panel hinzu. Durch die Nähe zu den anderen Globals bleibt das mentale Modell erhalten: Eingaben → Laufzeit → Nutzeroberfläche.
// Wick runtime (separated for majors and sessions) bool g_ShowWicks; int g_WickAlpha; bool g_ShowSessionWicks; int g_SessionWickAlpha; // Session runtime bool g_ShowSessions; // Session information panel bool g_ShowSessionInfo;
4) Eine einzige, gemeinsam genutzte Instanz von CSessionVisualizer
Alle Sitzungszeichnungen sollten ein einheitliches Namenspräfix und einen einheitlichen Lebenszyklus haben. Wir instanziieren einen Visualizer mit einem kurzen Präfix, sodass die Objektnamen eindeutig bleiben und leicht zu bereinigen sind, ohne andere Chart-Objekte zu berühren.
// Session visualizer instance CSessionVisualizer g_sessions("SESS_");
5) Sitzungsinformationen – kompakter Zustand, den wir bei jedem Tick aktualisieren können
Händler lieben den Kontext auf einen Blick: „In welcher Sitzung bin ich? Wie wurde die vorherige geschlossen?“ Wir speichern die minimalen Daten, die wir zum Zeichnen des Panels benötigen: Namen, O/C, Art (Bulle/Bär/Neutral) und die letzte Aktualisierungszeit, um redundante Arbeit zu vermeiden.
// Session data storage string g_CurrentSessionName = ""; double g_CurrentSessionOpen = 0; double g_CurrentSessionCurrent = 0; string g_CurrentSessionNature = ""; datetime g_CurrentSessionStart = 0; string g_PrevSessionName = ""; double g_PrevSessionOpen = 0; double g_PrevSessionClose = 0; string g_PrevSessionNature = ""; datetime g_LastSessionUpdate = 0;
6) Breiterer Leinwandhintergrund zur Anpassung an das neue Panel
Der Sitzungsinfoblock fügt drei prägnante Zeilen für Zusammenfassungen der aktuellen und vorherigen Sitzungen hinzu. Wir passen die Breite und Höhe der Panels an, damit das Layout atmet und der Text nicht unschön umbrochen wird.
// Canvas background - INCREASED WIDTH CCanvas g_bgCanvas; string g_bg_name = ""; int g_bg_x = 6; int g_bg_y = 44; // uses Y_OFFSET in OnInit int g_bg_w = 450; // increased from 430 int g_bg_h = 340; // increased from 300 for session info
7) Initialisieren der neuen Knöpfe in OnInit()
OnInit() ist der Zeitpunkt, an dem die Eingaben in den Live-Zustand übergehen. Wir bereiten auch den Visualizer vor, damit er weiß, welche Seitenkanaleinstellungen für Sitzungen (nicht für Majors) gelten: GMT-Offset, Ein-/Ausschalter des Dochts und Docht-Alpha. Obwohl die Sitzungsklasse SetFillColors() zur Verfügung stellt, verwenden unsere aktuellen Sitzungsbilder unausgefüllte Rechtecke, aber wir behalten den Aufruf aus Gründen der Vorwärtskompatibilität bei.
// Initialize session visualizer with separate session wick params g_sessions.SetGMTOffset(InpGMTOffset); g_sessions.SetFillColors(g_FillBull, g_FillBear); // kept for compatibility (no body fill in visualizer) g_sessions.SetShowWicks(g_ShowSessionWicks); // session-specific wick setting g_sessions.SetWickAlpha(g_SessionWickAlpha); // session-specific wick alpha // copy panel flag g_ShowSessionInfo = InpShowSessionInfo;
8) Fügen Sie die UI „Sitzungsinformationen“ hinzu
Wir zeichnen kleine Kennzeichnungen in das Panel, um die aktuelle Sitzung (Name, offen, Art) und die vorherige Sitzung (Name, O/C, Art) anzuzeigen. Die Farben verstärken sanft das Auf- und Abwärts. Das Panel wird einmal erstellt, der Inhalt wird bei jedem Tick aktualisiert.
void UpdateSessionInfo() { if(!g_ShowSessionInfo) return; datetime now = TimeCurrent(); // Always refresh current session nature UpdateCurrentSessionInfo(now); // Update previous session only at boundaries if(g_LastSessionUpdate == 0 || IsNewSessionStart(now)) { UpdatePreviousSessionInfo(now); g_LastSessionUpdate = now; } UpdateSessionDisplay(); }
9) Intelligente Sitzungsbuchhaltung (aktuell + vorher)
Wir teilen die Anliegen in drei Helfer auf:
- UpdateCurrentSessionInfo: Ermitteln, in welcher Sitzung wir uns befinden (GMT-basiert), Zwischenspeichern des Eröffnungskurses der Sitzung und Klassifizierung der Natur anhand von Live-Gebot und Eröffnungskurs der Sitzung.
- UpdatePreviousSessionInfo: Wenn eine neue Sitzung beginnt, wird ein Snapshot der O/C und Art der vorherigen Sitzung erstellt.
- UpdateSessionDisplay: Übertragen der neuesten Zeichenfolgen/Farben in die Objekte der Kennzeichnung.
Dies ist absichtlich leichtgewichtig – keine schweren Schleifen – sodass es sich gut in den regelmäßigen HTF-Aktualisierungszyklus des EA einfügt. Im Gegensatz dazu hatte der frühere EA kein derartiges Konzept, weshalb die Logik des Panels und der Begrenzung in dieser Version völlig neu ist.
void UpdateSessionInfo() { if(!g_ShowSessionInfo) return; datetime now = TimeCurrent(); // Always refresh current session nature UpdateCurrentSessionInfo(now); // Update previous session only at boundaries if(g_LastSessionUpdate == 0 || IsNewSessionStart(now)) { UpdatePreviousSessionInfo(now); g_LastSessionUpdate = now; } UpdateSessionDisplay(); }
10) Sitzungssteuerung im Dashboard
Die Macht sollte in den Händen des Händlers liegen. Wir fügen zwei neue Schaltflächen hinzu: eine zum Ein- und Ausschalten von Sitzungen, eine weitere zum Umschalten von Sitzungsdochten. Wir halten die Kontrollen der Majors unabhängig. Die Tasten befinden sich neben den vorhandenen Umschaltern, um das „Muskelgedächtnis“ zu erhalten.
// NEW ROW: Session wicks, Sessions, Major VLines (swapped positions for clarity) CreateButton(btn_toggle_session_wicks, 12, 86 + Y_OFFSET, 130, 22, g_ShowSessionWicks ? "Sess Wicks: ON" : "Sess Wicks: OFF"); CreateButton(btn_toggle_sessions, 152, 86 + Y_OFFSET, 130, 22, g_ShowSessions ? "Sessions: ON" : "Sessions: OFF"); CreateButton(btn_toggle_maj_vlines, 292, 86 + Y_OFFSET, 130, 22, g_ShowMajorVLines ? "Maj VLines: ON" : "Maj VLines: OFF");
Und die Click-Handler drehen einfach das Bit um und aktualisieren:
if(obj == btn_toggle_session_wicks) { g_ShowSessionWicks = !g_ShowSessionWicks; ObjectSetString(main_chart_id, btn_toggle_session_wicks, OBJPROP_TEXT, g_ShowSessionWicks ? "Sess Wicks: ON" : "Sess Wicks: OFF"); RefreshLines(); return; } if(obj == btn_toggle_sessions) { g_ShowSessions = !g_ShowSessions; ObjectSetString(main_chart_id, btn_toggle_sessions, OBJPROP_TEXT, g_ShowSessions ? "Sessions: ON" : "Sessions: OFF"); RefreshLines(); return; }
11) Zeichnen von Sitzungen innerhalb von RefreshLines() (ein Ort für alle Fälle)
Das zentrale Neuzeichnen des EA ist RefreshLines(). Nachdem wir HTF-Objekte verwaltet haben, bitten wir den Visualizer entweder, Sitzungen zu rendern (mit den neuesten Einstellungen pro Sitzung) oder sie zu löschen. Dadurch wird ein einziger Aktualisierungszyklus und ein einziger Bereinigungsdurchgang sichern gestellt.
// Draw sessions if toggled - NOW WITH SEPARATE WICK SETTINGS if(g_ShowSessions) { g_sessions.SetFillColors(g_FillBull, g_FillBear); // no-op for bodies today; future-proof g_sessions.SetShowWicks(g_ShowSessionWicks); g_sessions.SetWickAlpha(g_SessionWickAlpha); g_sessions.RefreshSessions(InpSessionLookback); } else { g_sessions.ClearAll(); }
Im älteren EA wurden mit RefreshLines() nur HTF/Minor Visuals behandelt. Da die Sitzungen hier zentralisiert sind, steuert ein einziger Timer/Tick alle visuellen Elemente, wodurch Flimmern und Wettlaufbedingungen vermieden werden.
12) Aktualisierung und Bereinigung bei Tick & Deinit
Bei jedem Tick berechnen wir den High-Level-Status neu: neue Balken und jetzt den Sitzungsinfoblock. Während des Deinitierens werden auch die Sitzungszeichnungen gelöscht, sodass ein erneutes Anhängen von neuem beginnt.
void OnTick() { bool need_refresh = false; // ... (bar-change checks for majors/minors) // Always update session info on tick for real-time current session nature UpdateSessionInfo(); if(need_refresh) RefreshLines(); } void DeleteAllHTFLines() { // remove HTF objects // ... // Also clear sessions g_sessions.ClearAll(); }
13) Höheres Panel nach der Wiederherstellung, um den neuen Abschnitt anzupassen
Beim Minimieren/Wiederherstellen sollte die Höhe des Hintergrunds mit dem Inhalt übereinstimmen. Wir erhöhen die wiederhergestellte Höhe, damit die Sitzungsinformationen vollständig sichtbar bleiben, ohne andere Chart-Elemente zu überlagern.
void RestoreUI() { UpdateBackgroundHeight(340); // was 250; space for the Session Info CreateAllOtherUIObjects(); ObjectSetString(main_chart_id, btn_minimize, OBJPROP_TEXT, "Minimize"); }
Tests
Für diesen Test binden wir den EA direkt an einen Live-Chart an (nicht an den Strategietester), da wir uns ausschließlich auf die visuelle Darstellung im Chart konzentrieren. So können wir überprüfen, ob das Rendering und die Kontrollen mit unseren Zielen übereinstimmen. Nachfolgend sehen Sie ein Bild, das die zusammengefasste Funktion in Aktion zeigt.

Abb. 3. Endergebnis mit Forex-Sitzungen und Informationsanzeige
Schlussfolgerung
Devisen- und Börsensitzungen können Händlern und Finanzanalysten tief greifende Einblicke bieten, wenn sie richtig verstanden und visualisiert werden. Die hier umgesetzte Idee hilft uns, Handelssitzungen als Kerzenperioden darzustellen, die wertvolle Details über den Höchst-, Tiefst-, Eröffnungs- und Schlussstand jeder Sitzung enthüllen. Mit diesem Ansatz können wir unser traditionelles Kerzen-Wissen auf die Analyse auf Sitzungsebene anwenden und so potenzielle Bewegungen in der nächsten Sitzung auf der Grundlage der Art der vorangegangenen Sitzungen vorhersagen.
Mit dieser Entwicklung haben wir visuelle Marker und ein dynamisches Kontrollprogramm (Market Periods Synchronizer) eingeführt, mit dem wir die Sitzungsanzeigen direkt im Chart umschalten, anpassen und synchronisieren können. Darüber hinaus fasst das Sitzungsinformationsfeld die Eröffnungs- und Schlusskurse sowie die Richtung (auf- oder abwärts) der aktuellen und der vorangegangenen Sitzungen zusammen und bietet Händlern einen schnellen, intuitiven Überblick über die zugrunde liegenden Marktkräfte.
Da der Auf- oder Abwärtscharakter einer Börsensitzung über längere Zeiträume gemessen wird, spiegelt er häufig die allgemeine Marktstimmung und Trendtendenzen wider. Das Verständnis dieser Verhaltensweisen schärft nicht nur die technische Perspektive, sondern stärkt auch die Handelspsychologie durch direkte, visuelle Beobachtung der Marktdynamik.
Sie sind eingeladen, zu experimentieren, zu verändern und Ihre Erkenntnisse in den Kommentaren unten mitzuteilen. Die entsprechenden Quelldateien sind zu Ihrer Information und zur weiteren Erforschung beigefügt.
Wichtige Lektionen
| Wichtigste Lektion: | Beschreibung: |
|---|---|
| Klassenintegration und modularer Aufbau | Der Aufbau wiederverwendbarer Module wie CSessionVisualizer fördert eine saubere Architektur und erleichtert die Wartung. Durch die Ausgliederung der Visualisierungslogik in eine Klasse gewinnt der EA an Flexibilität – Entwickler können die Sitzungsbilder ändern oder die Komponente in anderen Projekten wiederverwenden, ohne den Kerncode neu schreiben zu müssen. |
| Laufzeitvariablen und UI-Synchronisation | Durch das Spiegeln von Eingabeparametern in Laufzeit-Globals wird sichergestellt, dass Echtzeit-Nutzeroberflächen-Steuerelemente (wie Schieberegler, Schaltflächen und Kippschalter) das visuelle Verhalten dynamisch aktualisieren können, ohne den EA neu zu initialisieren. Dieser Ansatz lehrt, wie man mit Hilfe von objektbasierten Nutzeroberflächen reaktionsfähige Charts entwickelt. |
| Sitzungsmanagement und Datenzuordnung | Die Verwendung strukturierter Sitzungsdaten (z. B. GMT-basierte Start-/Endzeiten, Open/Close-Berechnungen) zeigt, wie Live-Chart-Daten mit zeitbasierter Handelslogik verbunden werden können. Es unterstreicht, wie Entwickler Analysen und visuelle Darstellungen kombinieren können, um den Marktkontext intuitiver zu gestalten. |
Anlagen
| Name der Quelldatei | Version | Beschreibung |
|---|---|---|
| SessionVisualizerTest.mqh | 1.0 | Ein eigenständiges Testskript, mit dem die Darstellung und das Verhalten der Klasse CSessionVisualizer überprüft werden können. Es ermöglicht Entwicklern, die visuelle Logik zu isolieren und sicherzustellen, dass Sitzungsrechtecke, Farben und Zeitzuordnungen vor der Integration in größere Systeme korrekt angezeigt werden. |
| SessionTest.mq5 | 1.0 | Ein vereinfachter Expert Advisor, der speziell zum Testen und Demonstrieren der Funktionalität des SessionVisualizerTest-Headers entwickelt wurde. |
| SessionVisualizer.mqh | 1.0 | Die Implementierung der Hauptklasse, die die Visualisierung der Forex-Sitzung verwaltet. Es übernimmt Zeitzonenanpassungen, Farbzuordnung und grafische Darstellung von Sitzungsgrenzen auf dem Chart und dient als wiederverwendbares visuelles Modul für jeden EA oder Indikator, der sitzungsbasierten Kontext benötigt. |
| MarketPeriodsSychronizer_EA.mq5 | 1.02 | Der aktualisierte Expert Advisor integriert den SessionVisualizer und ein neues Panel für Sitzungsinformationen. Es synchronisiert mehrere Zeitrahmen, Session-Fills und Echtzeit-Analysen in einem einheitlichen Kontroll-Dashboard, das Händlern ein interaktives und lehrreiches Tool zur Visualisierung von Marktperioden bietet. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20005
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.
Aufbau eines Remote-Forex-Risikomanagementsystems in Python
Der Algorithmus Central Force Optimization (CFO)
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Vom Neuling zum Experten: Die Schatten der Kerzen enthüllen (Dochte)
- 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.