MQL5-Handelswerkzeuge (Teil 6): Dynamisches holografisches Dashboard mit Impulsanimationen und Steuerelementen
Einführung
In unserem vorigen Artikel (Teil 5) haben wir ein Laufband mit Tickermeldungen in MetaQuotes Language 5 (MQL5) für die Überwachung von Symbolen in Echtzeit mit scrollenden Preisen, Spreads und Änderungen erstellt, um Händler effizient zu informieren. In Teil 6 entwickeln wir ein dynamisches holografisches Dashboard, das Multi-Symbol- und Zeitrahmen-Indikatoren wie den Relative Strength Index (RSI) und die Volatilität (basierend auf dem Average True Range (ATR)) mit Impulsanimationen, Sortierung und interaktiven Steuerelementen anzeigt und so ein ansprechendes Analysetool schafft. Wir werden die folgenden Themen behandeln:
- Verständnis der holografischen Dashboard-Architektur
- Implementation in MQL5
- Backtests
- Schlussfolgerung
Am Ende haben Sie ein anpassbares holografisches Dashboard für Ihr Trading-Setup – legen wir los!
Verständnis der holografischen Dashboard-Architektur
Das holografische Dashboard, das wir entwickeln, ist ein visuelles Tool, das mehrere Symbole und Zeitrahmen überwacht und Indikatoren wie RSI und Volatilität sowie Sortier- und Warnhinweise anzeigt, um uns dabei zu helfen, Chancen schnell zu erkennen. Diese Architektur ist wichtig, weil sie Echtzeitdaten mit interaktiven Steuerelementen und Animationen kombiniert und so die Analyse in einer unübersichtlichen Chart-Umgebung interessanter und effizienter macht.
Dazu verwenden wir Arrays für die Datenverwaltung, Handles für Indikatoren wie ATR und RSI sowie Funktionen für Sortier- und Pulseffekte mit Schaltflächen zum Umschalten der Sichtbarkeit und zum Umschalten der Ansichten. Wir planen, die Aktualisierungen in einer Schleife zu zentralisieren, die die Nutzeroberfläche (UI) dynamisch aktualisiert, um sicherzustellen, dass das Dashboard für den strategischen Handel anpassungsfähig und reaktionsschnell bleibt. Sehen Sie sich die nachstehende Visualisierung an, um zu verstehen, was wir erreichen wollen, bevor wir mit der Implementierung beginnen.

Implementation in MQL5
Um das Programm in MQL5 zu erstellen, müssen wir die Metadaten des Programms definieren und dann einige Eingaben festlegen, die es uns ermöglichen, die Funktionsweise des Programms leicht zu ändern, ohne direkt in den Code einzugreifen.
//+------------------------------------------------------------------+ //| Holographic Dashboard EA.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //| https://t.me/Forex_Algo_Trader | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" #include <Arrays\ArrayString.mqh> //--- Include ArrayString library for string array operations #include <Files\FileTxt.mqh> //--- Include FileTxt library for text file operations // Input Parameters input int BaseFontSize = 9; // Base Font Size input string FontType = "Calibri"; // Font Type (Professional) input int X_Offset = 30; // X Offset input int Y_Offset = 30; // Y Offset input color PanelColor = clrDarkSlateGray; // Panel Background Color input color TitleColor = clrWhite; // Title/Symbol Color input color DataColor = clrLightGray; // Bid/Neutral Color input color ActiveColor = clrLime; // Active Timeframe/Symbol Color input color UpColor = clrDeepSkyBlue; // Uptrend Color input color DownColor = clrCrimson; // Downtrend Color input color LineColor = clrSilver; // Grid Line Color input bool EnableAnimations = true; // Enable Pulse Animations input int PanelWidth = 730; // Panel Width (px) input int ATR_Period = 14; // ATR Period for Volatility input int RSI_Period = 14; // RSI Period input double Vol_Alert_Threshold = 2.0; // Volatility Alert Threshold (%) input color GlowColor = clrDodgerBlue; // Glow Color for holographic effect input int AnimationSpeed = 30; // Animation delay in ms for pulse input int PulseCycles = 3; // Number of pulse cycles for animations
Wir beginnen mit der Einbindung von Bibliotheken für String-Arrays und Textdateiprotokollierung sowie der Einrichtung von Eingaben zur Anpassung der Nutzeroberfläche und der Indikatoren. Wir fügen „<Arrays\ArrayString.mqh>“ für die Behandlung von Symbollisten und „<Files\FileTxt.mqh>“ für die Protokollierung von Fehlern in einer Datei ein. Die Eingaben ermöglichen es uns, die Grundschriftgröße auf 9 einzustellen, eine professionelle Schriftart wie Calibri zu wählen, Offsets für die x- und y-Positionierung auf jeweils 30 Pixel festzulegen und Farben wie dunkles Schiefergrau für den Panelhintergrund, Weiß für Titel, Hellgrau für Daten, Limette für aktive Elemente, tiefes Himmelblau für Aufwärtstrends, Karmesinrot für Abwärtstrends und Silber für Rasterlinien auszuwählen.
Wir aktivieren die Impulsanimationen standardmäßig, legen die Breite des Panels auf 730 Pixel fest, setzen die ATR- und RSI-Perioden für die Volatilitäts- und Momentum-Berechnungen auf 14, legen einen Schwellenwert von 2,0 % für Volatilitätswarnungen fest, wählen Dodger Blue für das holografische Glühen und konfigurieren die Animationsgeschwindigkeit auf 30 ms mit 3 Impulszyklen. Diese Einstellungen machen das Dashboard in hohem Maße anpassungsfähig an visuelle und funktionale Präferenzen. Dann müssen wir einige globale Variablen definieren, die wir im gesamten Programm verwenden werden.
// Global Variables double prices_PrevArray[]; //--- Array for previous prices double volatility_Array[]; //--- Array for volatility values double bid_array[]; //--- Array for bid prices long spread_array[]; //--- Array for spreads double change_array[]; //--- Array for percentage changes double vol_array[]; //--- Array for volumes double rsi_array[]; //--- Array for RSI values int indices[]; //--- Array for sorted indices ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H2, PERIOD_H4, PERIOD_D1, PERIOD_W1}; //--- Array of timeframes string logFileName = "Holographic_Dashboard_Log.txt"; //--- Log file name int sortMode = 0; //--- Current sort mode string sortNames[] = {"Name ASC", "Vol DESC", "Change ABS DESC", "RSI DESC"}; //--- Sort mode names int atr_handles_sym[]; //--- ATR handles for symbols int rsi_handles_sym[]; //--- RSI handles for symbols int atr_handles_tf[]; //--- ATR handles for timeframes int rsi_handles_tf[]; //--- RSI handles for timeframes int totalSymbols; //--- Total number of symbols bool dashboardVisible = true; //--- Dashboard visibility flag
Hier definieren wir globale Variablen, um Daten und Indikatoren in unserem Programm zu verwalten und Echtzeitüberwachung, Sortierung und Animationen zu unterstützen. Wir erstellen Arrays wie „prices_PrevArray“ für frühere Preise zur Berechnung von Änderungen, „volatility_array“ für Volatilitätswerte, „bid_array“ für aktuelle Gebote, „spread_array“ für Spreads als Long-Positionen, „change_array“ für prozentuale Veränderungen, „vol_array“ für Volumina, „rsi_array“ für RSI-Werte und „indices“ für die Sortierung von Indizes. Wir setzen „periods“ als ein Array von Zeitrahmen von PERIOD_M1 bis PERIOD_W1, „logFileName“ auf „Holographic_Dashboard_Log.txt“ für die Fehlerprotokollierung, „sortMode“ auf 0 für die erste Sortierung, „sortNames“ als Strings für Sortieroptionen wie „Name ASC“ oder „Vol DESC“und Arrays für ATR- und RSI-Handles („atr_handles_sym“, „rsi_handles_sym“ für Symbole, „atr_handles_tf“, „rsi_handles_tf“ für Timeframes).
Der Integerwert „totalSymbols“ gibt die Anzahl der Symbole an, und „dashboardVisible“ ist true, was den Zustand des Dashboards steuert. Um Objekte besser zu verwalten, werden wir eine Klasse erstellen.
// Object Manager Class class CObjectManager : public CArrayString { public: void AddObject(string name) { //--- Add object name to manager if (!Add(name)) { //--- Check if add failed LogError(__FUNCTION__ + ": Failed to add object name: " + name); //--- Log error } } void DeleteAllObjects() { //--- Delete all managed objects for (int i = Total() - 1; i >= 0; i--) { //--- Iterate through objects string name = At(i); //--- Get object name if (ObjectFind(0, name) >= 0) { //--- Check if object exists if (!ObjectDelete(0, name)) { //--- Delete object LogError(__FUNCTION__ + ": Failed to delete object: " + name + ", Error: " + IntegerToString(GetLastError())); //--- Log deletion failure } } Delete(i); //--- Remove from array } ChartRedraw(0); //--- Redraw chart } };
Um die Dashboard-Objekte effizient zu verwalten, erstellen wir die Klasse „CObjectManager“, die die Klasse CArrayString erweitert. In der Methode „AddObject“ fügen wir das Objekt „name“ mit „Add“ zum Array hinzu und protokollieren Fehler über „LogError“, wenn sie nicht erfolgreich waren. Wir verwenden die Methode „deleteallobjects“, indem wir das Array mit „Total“ rückwärts durchlaufen, jeden „name“ mit „At“ abrufen, das Vorhandensein mit der Funktion ObjectFind prüfen, mit ObjectDelete löschen und bei einem Fehlschlag den Fehler protokollieren, es mit „Delete“ aus dem Array entfernen und das Chart mit der Funktion ChartRedraw neu zeichnen. Mit der Klassenerweiterung können wir einige Hilfsfunktionen erstellen, die wir im gesamten Programm zur Wiederverwendung aufrufen werden.
CObjectManager objManager; //--- Object manager instance //+------------------------------------------------------------------+ //| Utility Functions | //+------------------------------------------------------------------+ void LogError(string message) { CFileTxt file; //--- Create file object if (file.Open(logFileName, FILE_WRITE | FILE_TXT | FILE_COMMON, true) >= 0) { //--- Open log file file.WriteString(message + "\n"); //--- Write message file.Close(); //--- Close file } Print(message); //--- Print message } string Ask(string symbol) { double value; //--- Variable for ask price if (SymbolInfoDouble(symbol, SYMBOL_ASK, value)) { //--- Get ask price return DoubleToString(value, (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS)); //--- Return formatted ask } LogError(__FUNCTION__ + ": Failed to get ask price for " + symbol + ", Error: " + IntegerToString(GetLastError())); //--- Log error return "N/A"; //--- Return N/A on failure } string Bid(string symbol) { double value; //--- Variable for bid price if (SymbolInfoDouble(symbol, SYMBOL_BID, value)) { //--- Get bid price return DoubleToString(value, (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS)); //--- Return formatted bid } LogError(__FUNCTION__ + ": Failed to get bid price for " + symbol + ", Error: " + IntegerToString(GetLastError())); //--- Log error return "N/A"; //--- Return N/A on failure } string Spread(string symbol) { long value; //--- Variable for spread if (SymbolInfoInteger(symbol, SYMBOL_SPREAD, value)) { //--- Get spread return IntegerToString(value); //--- Return spread as string } LogError(__FUNCTION__ + ": Failed to get spread for " + symbol + ", Error: " + IntegerToString(GetLastError())); //--- Log error return "N/A"; //--- Return N/A on failure } string PercentChange(double current, double previous) { if (previous == 0) return "0.00%"; //--- Handle zero previous value return StringFormat("%.2f%%", ((current - previous) / previous) * 100); //--- Calculate and format percentage } string TruncPeriod(ENUM_TIMEFRAMES period) { return StringSubstr(EnumToString(period), 7); //--- Truncate timeframe string }
Um Objekte und Daten zu verwalten, instanziieren wir „objManager“ als „CObjectManager“, um UI-Elemente zu verfolgen. Wir erstellen die Funktion „LogError“, um Fehler zu protokollieren, öffnen „logFileName“ mit „CFileTxt“ unter Verwendung von „FILE_WRITE | FILE_TXT | FILE_COMMON“, schreiben die „Nachricht“ mit „WriteString“, schließen die Datei und drucken sie aus. Die Funktion „Ask“ ruft den Briefkurs für ein „Symbol“ mit SymbolInfoDouble ab, formatiert ihn mit DoubleToString unter Verwendung von „SymbolInfoInteger“ für die Dezimalstellen, protokolliert Fehler mit „LogError“, wenn sie fehlgeschlagen sind, und gibt bei Fehlschlag „N/A“ zurück. In ähnlicher Weise holt die Funktion „Bid“ den Geldkurs, formatiert ihn und behandelt eventuelle Fehler.
Die Funktion „Spread“ ermittelt die Spanne mit „SymbolInfoInteger“ und gibt sie als String mit IntegerToString oder „N/A“ bei Fehlschlag zurück. Die Funktion „PercentChange“ berechnet die prozentuale Veränderung zwischen „aktuellem“ und „vorherigem“ Preis unter Verwendung des StringFormats und gibt „0.00%“ zurück, wenn „vorheriger“ gleich Null ist. Die Funktion „TruncPeriod“ schneidet die Zeichenkette ENUM_TIMEFRAMES mit StringSubstr ab, um eine übersichtliche Anzeige des Zeitrahmens zu gewährleisten und saubere Ausgaben zu erhalten. Jetzt können wir die Funktion für die holografischen Impulse erstellen.
//+------------------------------------------------------------------+ //| Holographic Animation Function | //+------------------------------------------------------------------+ void HolographicPulse(string objName, color mainClr, color glowClr) { if (!EnableAnimations) return; //--- Exit if animations disabled int cycles = PulseCycles; //--- Set pulse cycles int delay = AnimationSpeed; //--- Set animation delay for (int i = 0; i < cycles; i++) { //--- Iterate through cycles ObjectSetInteger(0, objName, OBJPROP_COLOR, glowClr); //--- Set glow color ChartRedraw(0); //--- Redraw chart Sleep(delay); //--- Delay ObjectSetInteger(0, objName, OBJPROP_COLOR, mainClr); //--- Set main color ChartRedraw(0); //--- Redraw chart Sleep(delay / 2); //--- Shorter delay } }
Hier implementieren wir die Funktion „HolographicPulse“, um einen Puls-Animationseffekt für Dashboard-Elemente zu erzeugen. Wir beenden das Programm vorzeitig, wenn „EnableAnimations“ falsch ist, um Animationen zu überspringen. Wir setzen „cycles“ auf „PulseCycles“ und „delay“ auf „AnimationSpeed“ und durchlaufen dann die „cycles“ mit einer for-Schleife. Bei jeder Iteration wird die Farbe des Objekts mit ObjectSetInteger für OBJPROP_COLOR auf „glowClr“ gesetzt, das Chart mit ChartRedraw neu gezeichnet, mit „Sleep“ für „delay“ pausiert, wieder auf „mainClr“ umgeschaltet, erneut neu gezeichnet und für einen kürzeren Effekt für „delay / 2“ pausiert. Dadurch wird der holografische Impuls hinzugefügt, um aktive oder warnende Elemente visuell hervorzuheben. Mit diesen Funktionen bewaffnet, können wir mit der Erstellung des Kerninitialisierungs-Dashboards fortfahren. Dazu benötigen wir einige Hilfsfunktionen, um das Programm modular zu halten.
//+------------------------------------------------------------------+ //| Create Text Label Function | //+------------------------------------------------------------------+ bool createText(string objName, string text, int x, int y, color clrTxt, int fontsize, string font, bool animate = false, double opacity = 1.0) { ResetLastError(); //--- Reset error code if (ObjectFind(0, objName) < 0) { //--- Check if object exists if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) { //--- Create label LogError(__FUNCTION__ + ": Failed to create label: " + objName + ", Error: " + IntegerToString(GetLastError())); //--- Log error return false; //--- Return failure } objManager.AddObject(objName); //--- Add to manager } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, x); //--- Set x-coordinate ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, y); //--- Set y-coordinate ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); //--- Set corner ObjectSetString(0, objName, OBJPROP_TEXT, text); //--- Set text ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); //--- Set color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontsize); //--- Set font size ObjectSetString(0, objName, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, objName, OBJPROP_BACK, false); //--- Set foreground ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Disable selection ObjectSetInteger(0, objName, OBJPROP_ZORDER, StringFind(objName, "Glow") >= 0 ? -1 : 0); //--- Set z-order if (animate && EnableAnimations) { //--- Check for animation ObjectSetInteger(0, objName, OBJPROP_COLOR, DataColor); //--- Set temporary color ChartRedraw(0); //--- Redraw chart Sleep(50); //--- Delay ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); //--- Set final color } ChartRedraw(0); //--- Redraw chart return true; //--- Return success } //+------------------------------------------------------------------+ //| Create Button Function | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int x, int y, int width, int height, color textColor, color bgColor, color borderColor, bool animate = false) { ResetLastError(); //--- Reset error code if (ObjectFind(0, objName) < 0) { //--- Check if object exists if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { //--- Create button LogError(__FUNCTION__ + ": Failed to create button: " + objName + ", Error: " + IntegerToString(GetLastError())); //--- Log error return false; //--- Return failure } objManager.AddObject(objName); //--- Add to manager } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, x); //--- Set x-coordinate ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, y); //--- Set y-coordinate ObjectSetInteger(0, objName, OBJPROP_XSIZE, width); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, height); //--- Set height ObjectSetString(0, objName, OBJPROP_TEXT, text); //--- Set text ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); //--- Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, bgColor); //--- Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor); //--- Set border color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, BaseFontSize + (StringFind(objName, "SwitchTFBtn") >= 0 ? 3 : 0)); //--- Set font size ObjectSetString(0, objName, OBJPROP_FONT, FontType); //--- Set font ObjectSetInteger(0, objName, OBJPROP_ZORDER, 1); //--- Set z-order ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Reset state if (animate && EnableAnimations) { //--- Check for animation ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrLightGray); //--- Set temporary background ChartRedraw(0); //--- Redraw chart Sleep(50); //--- Delay ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, bgColor); //--- Set final background } ChartRedraw(0); //--- Redraw chart return true; //--- Return success } //+------------------------------------------------------------------+ //| Create Panel Function | //+------------------------------------------------------------------+ bool createPanel(string objName, int x, int y, int width, int height, color clr, double opacity = 1.0) { ResetLastError(); //--- Reset error code if (ObjectFind(0, objName) < 0) { //--- Check if object exists if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { //--- Create panel LogError(__FUNCTION__ + ": Failed to create panel: " + objName + ", Error: " + IntegerToString(GetLastError())); //--- Log error return false; //--- Return failure } objManager.AddObject(objName); //--- Add to manager } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, x); //--- Set x-coordinate ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, y); //--- Set y-coordinate ObjectSetInteger(0, objName, OBJPROP_XSIZE, width); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, height); //--- Set height ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clr); //--- Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, BORDER_FLAT); //--- Set border type ObjectSetInteger(0, objName, OBJPROP_ZORDER, -1); //--- Set z-order ChartRedraw(0); //--- Redraw chart return true; //--- Return success } //+------------------------------------------------------------------+ //| Create Line Function | //+------------------------------------------------------------------+ bool createLine(string objName, int x1, int y1, int x2, int y2, color clrLine, double opacity = 1.0) { ResetLastError(); //--- Reset error code if (ObjectFind(0, objName) < 0) { //--- Check if object exists if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { //--- Create line as rectangle LogError(__FUNCTION__ + ": Failed to create line: " + objName + ", Error: " + IntegerToString(GetLastError())); //--- Log error return false; //--- Return failure } objManager.AddObject(objName); //--- Add to manager } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, x1); //--- Set x1 ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, y1); //--- Set y1 ObjectSetInteger(0, objName, OBJPROP_XSIZE, x2 - x1); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, StringFind(objName, "Glow") >= 0 ? 3 : 1); //--- Set height ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrLine); //--- Set color ObjectSetInteger(0, objName, OBJPROP_ZORDER, StringFind(objName, "Glow") >= 0 ? -1 : 0); //--- Set z-order ChartRedraw(0); //--- Redraw chart return true; //--- Return success }
Hier definieren wir die Funktion „createText“, um eine Textbeschriftung zu erzeugen. Wir beginnen mit dem Aufruf von ResetLastError, um alle vorherigen Fehler zu löschen. Wenn das Objekt nicht existiert (geprüft über „ObjectFind(0, objName) < 0“), wird es mit ObjectCreate vom Typ OBJ_LABEL erstellt. Bei einem Fehlschlag wird der Fehler protokolliert und false zurückgegeben. Wir fügen es dem „objManager“ über „AddObject“ hinzu. Wir legen Eigenschaften fest: OBJPROP_XDISTANCE auf „x“, „OBJPROP_YDISTANCE“ auf „y“, und das Gleiche für die anderen. Wenn „animate“ und „EnableAnimations“ wahr sind, setzen wir „OBJPROP_COLOR“ vorübergehend auf „DataColor“, zeichnen neu, verzögern mit „Sleep(50)“ und setzen dann auf die Farbe des Textes. Zum Schluss wird neu gezeichnet und true zurückgegeben.
Als Nächstes definieren wir „createButton“ auf ähnliche Weise: Fehler zurücksetzen, Existenz prüfen, bei Bedarf mit OBJ_BUTTON erstellen, bei Fehlschlag protokollieren und zum Manager hinzufügen. Dann werden die Objekteigenschaften eingestellt und, falls Animationen aktiviert sind, OBJPROP_BGCOLOR vorübergehend auf „clrLightGray“ gesetzt, neu gezeichnet, 50ms schlafen, dann auf „bgColor“ gesetzt. Neu zeichnen und true zurückgeben. Für „createPanel“ verwenden wir einen ähnlichen Ansatz.
Die Funktion „createLine“ schließlich verwendet ein ähnliches Muster: zurücksetzen, prüfen, als OBJ_RECTANGLE_LABEL (der eine Linie simuliert) erstellen, bei Fehlschlag protokollieren und zum Manager hinzufügen. Setze „OBJPROP_XDISTANCE“ auf „x1“, „OBJPROP_YDISTANCE“ auf „y1“, „OBJPROP_XSIZE“ auf „x2-x1“, „OBJPROP_YSIZE“ auf 3 wenn „Glow“ im Namen sonst 1, „OBJPROP_BGCOLOR“ auf „clrLine“, OBJPROP_ZORDER auf -1 wenn „Glow“ sonst 0. Neu zeichnen und true zurückgeben. Wir verwenden diese Funktionen nun, um die Kernfunktion zu erstellen, mit der wir das Haupt-Dashboard wie folgt gestalten können.
//+------------------------------------------------------------------+ //| Dashboard Creation Function with Holographic Effects | //+------------------------------------------------------------------+ void InitDashboard() { // Get chart dimensions long chartWidth, chartHeight; //--- Variables for chart dimensions if (!ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0, chartWidth) || !ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0, chartHeight)) { //--- Get chart size LogError(__FUNCTION__ + ": Failed to get chart dimensions, Error: " + IntegerToString(GetLastError())); //--- Log error return; //--- Exit on failure } int fontSize = (int)(BaseFontSize * (chartWidth / 800.0)); //--- Calculate font size int cellWidth = PanelWidth / 8; //--- Calculate cell width int cellHeight = 18; //--- Set cell height int panelHeight = 70 + (ArraySize(periods) + 1) * cellHeight + 40 + (totalSymbols + 1) * cellHeight + 50; //--- Calculate panel height // Create Dark Panel createPanel("DashboardPanel", X_Offset, Y_Offset, PanelWidth, panelHeight, PanelColor); //--- Create dashboard panel // Create Header with Glow createText("Header", "HOLOGRAPHIC DASHBOARD", X_Offset + 10, Y_Offset + 10, TitleColor, fontSize + 4, FontType); //--- Create header text createText("HeaderGlow", "HOLOGRAPHIC DASHBOARD", X_Offset + 11, Y_Offset + 11, GlowColor, fontSize + 4, FontType, true); //--- Create header glow createText("SubHeader", StringFormat("%s | TF: %s", _Symbol, TruncPeriod(_Period)), X_Offset + 10, Y_Offset + 30, DataColor, fontSize, FontType); //--- Create subheader // Timeframe Grid int y = Y_Offset + 50; //--- Set y-coordinate for timeframe grid createText("TF_Label", "Timeframe", X_Offset + 10, y, TitleColor, fontSize, FontType); //--- Create timeframe label createText("Trend_Label", "Trend", X_Offset + 10 + cellWidth, y, TitleColor, fontSize, FontType); //--- Create trend label createText("Vol_Label", "Vol", X_Offset + 10 + cellWidth * 2, y, TitleColor, fontSize, FontType); //--- Create vol label createText("RSI_Label", "RSI", X_Offset + 10 + cellWidth * 3, y, TitleColor, fontSize, FontType); //--- Create RSI label createLine("TF_Separator", X_Offset + 5, y + cellHeight + 2, X_Offset + PanelWidth - 5, y + cellHeight + 2, LineColor, 0.6); //--- Create separator line createLine("TF_Separator_Glow", X_Offset + 4, y + cellHeight + 1, X_Offset + PanelWidth - 4, y + cellHeight + 3, GlowColor, 0.3); //--- Create glow separator if (EnableAnimations) HolographicPulse("TF_Separator", LineColor, GlowColor); //--- Animate separator if enabled y += cellHeight + 5; //--- Update y-coordinate for (int i = 0; i < ArraySize(periods); i++) { //--- Iterate through timeframes color periodColor = (periods[i] == _Period) ? ActiveColor : DataColor; //--- Set period color createText("Period_" + IntegerToString(i), TruncPeriod(periods[i]), X_Offset + 10, y, periodColor, fontSize, FontType); //--- Create period text createText("Trend_" + IntegerToString(i), "-", X_Offset + 10 + cellWidth, y, DataColor, fontSize, FontType); //--- Create trend text createText("Vol_" + IntegerToString(i), "0.00%", X_Offset + 10 + cellWidth * 2, y, DataColor, fontSize, FontType); //--- Create vol text createText("RSI_" + IntegerToString(i), "0.0", X_Offset + 10 + cellWidth * 3, y, DataColor, fontSize, FontType); //--- Create RSI text y += cellHeight; //--- Update y-coordinate } // Symbol Grid y += 30; //--- Update y-coordinate for symbol grid createText("Symbol_Label", "Symbol", X_Offset + 10, y, TitleColor, fontSize, FontType); //--- Create symbol label createText("Bid_Label", "Bid", X_Offset + 10 + cellWidth, y, TitleColor, fontSize, FontType); //--- Create bid label createText("Spread_Label", "Spread", X_Offset + 10 + cellWidth * 2, y, TitleColor, fontSize, FontType); //--- Create spread label createText("Change_Label", "% Change", X_Offset + 10 + cellWidth * 3, y, TitleColor, fontSize, FontType); //--- Create change label createText("Vol_Label_Symbol", "Vol", X_Offset + 10 + cellWidth * 4, y, TitleColor, fontSize, FontType); //--- Create vol label createText("RSI_Label_Symbol", "RSI", X_Offset + 10 + cellWidth * 5, y, TitleColor, fontSize, FontType); //--- Create RSI label createText("UpArrow_Label", CharToString(236), X_Offset + 10 + cellWidth * 6, y, TitleColor, fontSize, "Wingdings"); //--- Create up arrow label createText("DownArrow_Label", CharToString(238), X_Offset + 10 + cellWidth * 7, y, TitleColor, fontSize, "Wingdings"); //--- Create down arrow label createLine("Symbol_Separator", X_Offset + 5, y + cellHeight + 2, X_Offset + PanelWidth - 5, y + cellHeight + 2, LineColor, 0.6); //--- Create separator line createLine("Symbol_Separator_Glow", X_Offset + 4, y + cellHeight + 1, X_Offset + PanelWidth - 4, y + cellHeight + 3, GlowColor, 0.3); //--- Create glow separator if (EnableAnimations) HolographicPulse("Symbol_Separator", LineColor, GlowColor); //--- Animate separator if enabled y += cellHeight + 5; //--- Update y-coordinate for (int i = 0; i < totalSymbols; i++) { //--- Iterate through symbols string symbol = SymbolName(i, true); //--- Get symbol name string displaySymbol = (symbol == _Symbol) ? "*" + symbol : symbol; //--- Format display symbol color symbolColor = (symbol == _Symbol) ? ActiveColor : DataColor; //--- Set symbol color createText("Symbol_" + IntegerToString(i), displaySymbol, X_Offset + 10, y, symbolColor, fontSize, FontType); //--- Create symbol text createText("Bid_" + IntegerToString(i), Bid(symbol), X_Offset + 10 + cellWidth, y, DataColor, fontSize, FontType); //--- Create bid text createText("Spread_" + IntegerToString(i), Spread(symbol), X_Offset + 10 + cellWidth * 2, y, DataColor, fontSize, FontType); //--- Create spread text createText("Change_" + IntegerToString(i), "0.00%", X_Offset + 10 + cellWidth * 3, y, DataColor, fontSize, FontType); //--- Create change text createText("Vol_" + IntegerToString(i), "0.00%", X_Offset + 10 + cellWidth * 4, y, DataColor, fontSize, FontType); //--- Create vol text createText("RSI_" + IntegerToString(i), "0.0", X_Offset + 10 + cellWidth * 5, y, DataColor, fontSize, FontType); //--- Create RSI text createText("ArrowUp_" + IntegerToString(i), CharToString(236), X_Offset + 10 + cellWidth * 6, y, UpColor, fontSize, "Wingdings"); //--- Create up arrow createText("ArrowDown_" + IntegerToString(i), CharToString(238), X_Offset + 10 + cellWidth * 7, y, DownColor, fontSize, "Wingdings"); //--- Create down arrow y += cellHeight; //--- Update y-coordinate } // Interactive Buttons with Pulse Animation createButton("ToggleBtn", "TOGGLE DASHBOARD", X_Offset + 10, y + 20, 150, 25, TitleColor, PanelColor, UpColor); //--- Create toggle button createButton("SwitchTFBtn", "NEXT TF", X_Offset + 170, y + 20, 120, 25, UpColor, PanelColor, UpColor); //--- Create switch TF button createButton("SortBtn", "SORT: " + sortNames[sortMode], X_Offset + 300, y + 20, 150, 25, TitleColor, PanelColor, UpColor); //--- Create sort button ChartRedraw(0); //--- Redraw chart }
Hier initialisieren wir das Dashboard, indem wir zunächst die Abmessungen des Charts mithilfe der Variablen „chartWidth“ und „chartHeight“ abrufen. Wir erreichen dies, indem wir die Funktion ChartGetInteger zweimal aufrufen: einmal mit CHART_WIDTH_IN_PIXELS, um die Breite zu erhalten, und einmal mit „CHART_HEIGHT_IN_PIXELS“, um die Höhe zu erhalten. Als Nächstes berechnen wir die „fontSize“, indem wir die „BaseFontSize“ auf der Grundlage der Breite des Charts relativ zu 800 Pixel skalieren und das Ergebnis in eine Ganzzahl umwandeln. Wir bestimmen dann die „cellWidth“, indem wir „PanelWidth“ durch 8 teilen und „cellHeight“ auf einen festen Wert von 18 setzen. „panelHeight“ wird berechnet, indem 70, das Produkt aus („ArraySize(periods)“ + 1) und „cellHeight“, 40, das Produkt aus („totalSymbols“ + 1) und „cellHeight“, und 50 addiert werden – dies berücksichtigt das Gesamtlayout, einschließlich Zeitrahmen und Symbole.
Um den dunklen Hintergrund des Panels zu erstellen, rufen wir die Funktion „createPanel“ mit dem Namen „DashboardPanel“ auf, positionieren es an den Positionen „X_Offset“ und „Y_Offset“, mit den Abmessungen „PanelWidth“ und „panelHeight“ und färben es mit „PanelColor“. Für die Kopfzeile erstellen wir die Haupttextbeschriftung „Header“ mit der Zeichenfolge „HOLOGRAPHIC DASHBOARD“ mit der Funktion „createText“ an den Koordinaten „X_Offset“ + 10“ und „Y_Offset“ + 10“, gestylt mit „TitleColor“, einer Schriftgröße von „fontSize“ + 4“ und „FontType“. Um einen Glüheffekt hinzuzufügen, erstellen wir eine weitere Textbeschriftung „HeaderGlow“ mit der gleichen Zeichenfolge, aber um 1 Pixel in x- und y-Richtung versetzt, unter Verwendung von „GlowColor“, der gleichen Schriftgröße, „FontType“, und setzen das Deckkraft-Flag auf true.
Dann fügen wir eine Zwischenüberschrift „SubHeader“ hinzu, formatiert mit dem aktuellen Symbol _Symbol und einer abgeschnittenen Periode aus „TruncPeriod(_Period)“ mit „StringFormat“formatiert, bei „X_Offset“ + 10“ und „Y_Offset“ + 30“ positioniert und mit „DataColor“, „fontSize“ und „FontType“ eingefärbt.
Im Abschnitt für das Zeitraster setzen wir „y“ auf „Y_Offset“ + 50“. Wir erstellen Beschriftungen für „Timeframe“, „Trend“, „Vol“ und „RSI“ mit „createText“ für jede, horizontal positioniert mit Offsets basierend auf „cellWidth“, alle mit „TitleColor“, „fontSize“ und „FontType“. Darunter zeichnen wir eine Trennlinie „TF_Separator“ mit der Funktion „createLine“ von „X_Offset + 5“ bis „X_Offset + PanelWidth – 5“, in der Höhe „y + cellHeight + 2“, in der Farbe „LineColor“ mit der Deckkraft 0.6“. Für das Glühen fügen wir „TF_Separator_Glow“ als weitere, leicht versetzte und breitere Zeile mit „GlowColor“ und Deckkraft 0,3“ hinzu. Wenn „EnableAnimations“ wahr ist, wenden wir die Animation über „HolographicPulse“ mit „LineColor“ und „GlowColor“ an. Wir verwenden eine ähnliche Logik für alle anderen Etikettenobjekte.
Schließlich erstellen wir interaktive Schaltflächen: „ToggleBtn“ als „TOGGLE DASHBOARD“ bei „X_Offset“ + 10“, „y + 20“, Größe 150x25, mit „TitleColor“, „PanelColor“, „UpColor“; „SwitchTFBtn“ als „NEXT TF“ bei „X_Offset“ + 170“, gleiches y, Größe 120x25, mit „UpColor“, „PanelColor“, „UpColor“; und „SortBtn“ als „SORT: „ + sortNames[sortMode]“ bei „X_Offset“ + 300“, gleiches y, Größe 150x25, mit „TitleColor“, „PanelColor“, „UpColor“. Abschließend wird das Chart mit der Funktion „ChartRedraw(0)“ neu gezeichnet. Mit der Funktion können wir sie im Initialisierungs-Ereignishandler aufrufen, und wir können die schwere Arbeit erledigen.
//+------------------------------------------------------------------+ //| Expert Initialization Function | //+------------------------------------------------------------------+ int OnInit() { // Clear existing objects if (ObjectsDeleteAll(0, -1, -1) < 0) { //--- Delete all objects LogError(__FUNCTION__ + ": Failed to delete objects, Error: " + IntegerToString(GetLastError())); //--- Log error } objManager.DeleteAllObjects(); //--- Delete managed objects // Initialize arrays totalSymbols = SymbolsTotal(true); //--- Get total symbols if (totalSymbols == 0) { //--- Check for symbols LogError(__FUNCTION__ + ": No symbols available"); //--- Log error return INIT_FAILED; //--- Return failure } ArrayResize(prices_PrevArray, totalSymbols); //--- Resize previous prices array ArrayResize(volatility_Array, totalSymbols); //--- Resize volatility array ArrayResize(bid_array, totalSymbols); //--- Resize bid array ArrayResize(spread_array, totalSymbols); //--- Resize spread array ArrayResize(change_array, totalSymbols); //--- Resize change array ArrayResize(vol_array, totalSymbols); //--- Resize vol array ArrayResize(rsi_array, totalSymbols); //--- Resize RSI array ArrayResize(indices, totalSymbols); //--- Resize indices array ArrayResize(atr_handles_sym, totalSymbols); //--- Resize ATR symbol handles ArrayResize(rsi_handles_sym, totalSymbols); //--- Resize RSI symbol handles ArrayResize(atr_handles_tf, ArraySize(periods)); //--- Resize ATR timeframe handles ArrayResize(rsi_handles_tf, ArraySize(periods)); //--- Resize RSI timeframe handles ArrayInitialize(prices_PrevArray, 0); //--- Initialize previous prices ArrayInitialize(volatility_Array, 0); //--- Initialize volatility // Create indicator handles for timeframes for (int i = 0; i < ArraySize(periods); i++) { //--- Iterate through timeframes atr_handles_tf[i] = iATR(_Symbol, periods[i], ATR_Period); //--- Create ATR handle if (atr_handles_tf[i] == INVALID_HANDLE) { //--- Check for invalid handle LogError(__FUNCTION__ + ": Failed to create ATR handle for TF " + EnumToString(periods[i])); //--- Log error return INIT_FAILED; //--- Return failure } rsi_handles_tf[i] = iRSI(_Symbol, periods[i], RSI_Period, PRICE_CLOSE); //--- Create RSI handle if (rsi_handles_tf[i] == INVALID_HANDLE) { //--- Check for invalid handle LogError(__FUNCTION__ + ": Failed to create RSI handle for TF " + EnumToString(periods[i])); //--- Log error return INIT_FAILED; //--- Return failure } } // Create indicator handles for symbols on H1 for (int i = 0; i < totalSymbols; i++) { //--- Iterate through symbols string symbol = SymbolName(i, true); //--- Get symbol name atr_handles_sym[i] = iATR(symbol, PERIOD_H1, ATR_Period); //--- Create ATR handle if (atr_handles_sym[i] == INVALID_HANDLE) { //--- Check for invalid handle LogError(__FUNCTION__ + ": Failed to create ATR handle for symbol " + symbol); //--- Log error return INIT_FAILED; //--- Return failure } rsi_handles_sym[i] = iRSI(symbol, PERIOD_H1, RSI_Period, PRICE_CLOSE); //--- Create RSI handle if (rsi_handles_sym[i] == INVALID_HANDLE) { //--- Check for invalid handle LogError(__FUNCTION__ + ": Failed to create RSI handle for symbol " + symbol); //--- Log error return INIT_FAILED; //--- Return failure } } InitDashboard(); //--- Initialize dashboard dashboardVisible = true; //--- Set dashboard visible return INIT_SUCCEEDED; //--- Return success }
In der Funktion OnInit werden die vorhandenen Objekte mit der Funktion ObjectsDeleteAll für alle Charts und Typen gelöscht, wobei Fehler mit „LogError“ protokolliert werden, wenn sie nicht erfolgreich waren, und „objManager.DeleteAllObjects“ aufgerufen, um verwaltete Elemente zu entfernen. Wir erhalten „totalSymbols“ von SymbolsTotal mit true für Marktbeobachtungssymbole, wobei INIT_FAILED zurückgegeben wird, wenn es null ist, und mit „LogError“ protokolliert wird. Wir ändern die Größe von Arrays wie „prices_PrevArray“, „volatility_array“, „bid_array“, „spread_array“, „change_array“, „vol_array“, „rsi_array“, „indices“, „atr_handles_sym“, „rsi_handles_sym“, „atr_handles_tf“ und „rsi_handles_tf“ mit „totalSymbols“ oder „ArraySize(periods)“ unter Verwendung von ArrayResize angleichen und „prices_PrevArray“ und „volatility_Array“ mit der Funktion ArrayInitialize auf Null initialisieren.
Für Zeitrahmen durchlaufen wir „periods“ und erstellen „atr_handles_tf[i]“ mit iATR auf „_Symbol“, „periods[i]“ und „ATR_Period“ und „rsi_handles_tf[i]“ mit „iRSI“ auf „ _Symbol“, „periods[i]“, „RSI_Period“ und „PRICE_CLOSE“, wobei bei „INVALID_HANDLE“ INIT_FAILED protokolliert und zurückgegeben wird. Ähnlich verhält es sich bei den Symbolen: Wir durchlaufen „totalSymbols“, holen uns „symbol“ mit „SymbolName“ und true, erstellen „atr_handles_sym[i]“ mit „iATR“ auf „symbol“, „PERIOD_H1“und „ATR_Period“, und „rsi_handles_sym[i]“ mit iRSI auf „symbol“, „PERIOD_H1“, „RSI_Period“ und „PRICE_CLOSE“, Protokollierung und Rückgabe von „INIT_FAILED“, wenn ungültig. Wir rufen „InitDashboard“ auf, um die Nutzeroberfläche zu erstellen, setzen „dashboardVisible“ auf true und geben den Erfolg zurück. Wenn wir das Programm ausführen, erhalten wir das folgende Ergebnis.

Aus dem Bild geht hervor, dass wir das Programm erfolgreich initialisiert haben. Wir können uns um die Deinitialisierung des Programms kümmern, bei der wir die erstellten Objekte löschen und die Indikator-Handles freigeben müssen.
//+------------------------------------------------------------------+ //| Expert Deinitialization Function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if (ObjectsDeleteAll(0, -1, -1) < 0) { //--- Delete all objects LogError(__FUNCTION__ + ": Failed to delete objects, Error: " + IntegerToString(GetLastError())); //--- Log error } objManager.DeleteAllObjects(); //--- Delete managed objects // Release indicator handles for (int i = 0; i < ArraySize(atr_handles_tf); i++) { //--- Iterate through timeframe ATR handles if (atr_handles_tf[i] != INVALID_HANDLE) IndicatorRelease(atr_handles_tf[i]); //--- Release handle if (rsi_handles_tf[i] != INVALID_HANDLE) IndicatorRelease(rsi_handles_tf[i]); //--- Release handle } for (int i = 0; i < ArraySize(atr_handles_sym); i++) { //--- Iterate through symbol ATR handles if (atr_handles_sym[i] != INVALID_HANDLE) IndicatorRelease(atr_handles_sym[i]); //--- Release handle if (rsi_handles_sym[i] != INVALID_HANDLE) IndicatorRelease(rsi_handles_sym[i]); //--- Release handle } }
In OnDeinit werden die Ressourcen aufgeräumt, wenn der EA entfernt wird. Wir löschen alle Chart-Objekte mit ObjectsDeleteAll mit -1 für alle Charts und Typen und protokollieren Fehler mit „LogError“, wenn das Ergebnis negativ ist. Wir rufen „objManager.DeleteAllObjects“ auf, um verwaltete Elemente zu entfernen. Für Zeitrahmen-Handles werden die „atr_handles_tf“ und „rsi_handles_tf“ mit ArraySize durchlaufen und gültige Handles mit IndicatorRelease freigegeben, wenn sie nicht „INVALID_HANDLE“ sind. Ähnlich verhält es sich mit den Symbol-Handles in „atr_handles_sym“ und „rsi_handles_sym“. Dies gewährleistet eine vollständige Bereinigung der Objekte und Indikatoren. Hier ist eine Illustration.

Nachdem die erstellten Objekte vollständig erfasst wurden, können wir uns nun den Aktualisierungen zuwenden. Wir beabsichtigen, die Aktualisierungen in OnTick durchzuführen, um alles einfach zu halten, aber Sie könnten sie auch in der Ereignisbehandlung von OnTimer durchführen. Beginnen wir zunächst mit dem Abschnitt über den Zeitrahmen.
//+------------------------------------------------------------------+ //| Expert Tick Function with Holographic Updates | //+------------------------------------------------------------------+ void OnTick() { if (!dashboardVisible) return; //--- Exit if dashboard hidden long chartWidth; //--- Variable for chart width ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0, chartWidth); //--- Get chart width int fontSize = (int)(BaseFontSize * (chartWidth / 800.0)); //--- Calculate font size int cellWidth = PanelWidth / 8; //--- Calculate cell width int cellHeight = 18; //--- Set cell height int y = Y_Offset + 75; //--- Set y-coordinate for timeframe data // Update Timeframe Data with Pulse for (int i = 0; i < ArraySize(periods); i++) { //--- Iterate through timeframes double open = iOpen(_Symbol, periods[i], 0); //--- Get open price double close = iClose(_Symbol, periods[i], 0); //--- Get close price double atr_buf[1]; //--- Buffer for ATR if (CopyBuffer(atr_handles_tf[i], 0, 0, 1, atr_buf) != 1) { //--- Copy ATR data LogError(__FUNCTION__ + ": Failed to copy ATR buffer for TF " + EnumToString(periods[i])); //--- Log error continue; //--- Skip on failure } double vol = (close > 0) ? (atr_buf[0] / close) * 100 : 0.0; //--- Calculate volatility double rsi_buf[1]; //--- Buffer for RSI if (CopyBuffer(rsi_handles_tf[i], 0, 0, 1, rsi_buf) != 1) { //--- Copy RSI data LogError(__FUNCTION__ + ": Failed to copy RSI buffer for TF " + EnumToString(periods[i])); //--- Log error continue; //--- Skip on failure } double rsi = rsi_buf[0]; //--- Get RSI value color clr = DataColor; //--- Set default color string trend = "-"; //--- Set default trend if (rsi > 50) { clr = UpColor; trend = "↑"; } //--- Set up trend else if (rsi < 50) { clr = DownColor; trend = "↓"; } //--- Set down trend createText("Trend_" + IntegerToString(i), trend, X_Offset + 10 + cellWidth, y, clr, fontSize, FontType, EnableAnimations); //--- Update trend text createText("Vol_" + IntegerToString(i), StringFormat("%.2f%%", vol), X_Offset + 10 + cellWidth * 2, y, vol > Vol_Alert_Threshold ? UpColor : DataColor, fontSize, FontType, vol > Vol_Alert_Threshold && EnableAnimations); //--- Update vol text color rsi_clr = (rsi > 70 ? DownColor : (rsi < 30 ? UpColor : DataColor)); //--- Set RSI color createText("RSI_" + IntegerToString(i), StringFormat("%.1f", rsi), X_Offset + 10 + cellWidth * 3, y, rsi_clr, fontSize, FontType, (rsi > 70 || rsi < 30) && EnableAnimations); //--- Update RSI text HolographicPulse("Period_" + IntegerToString(i), (periods[i] == _Period) ? ActiveColor : DataColor, GlowColor); //--- Pulse period text y += cellHeight; //--- Update y-coordinate } // Update Symbol Data with Advanced Glow y += 50; //--- Update y-coordinate for symbol data for (int i = 0; i < totalSymbols; i++) { //--- Iterate through symbols string symbol = SymbolName(i, true); //--- Get symbol name double bidPrice; //--- Variable for bid price if (!SymbolInfoDouble(symbol, SYMBOL_BID, bidPrice)) { //--- Get bid price LogError(__FUNCTION__ + ": Failed to get bid for " + symbol + ", Error: " + IntegerToString(GetLastError())); //--- Log error continue; //--- Skip on failure } long spread; //--- Variable for spread if (!SymbolInfoInteger(symbol, SYMBOL_SPREAD, spread)) { //--- Get spread LogError(__FUNCTION__ + ": Failed to get spread for " + symbol + ", Error: " + IntegerToString(GetLastError())); //--- Log error continue; //--- Skip on failure } double change = (prices_PrevArray[i] == 0 ? 0 : (bidPrice - prices_PrevArray[i]) / prices_PrevArray[i] * 100); //--- Calculate change double close = iClose(symbol, PERIOD_H1, 0); //--- Get close price double atr_buf[1]; //--- Buffer for ATR if (CopyBuffer(atr_handles_sym[i], 0, 0, 1, atr_buf) != 1) { //--- Copy ATR data LogError(__FUNCTION__ + ": Failed to copy ATR buffer for symbol " + symbol); //--- Log error continue; //--- Skip on failure } double vol = (close > 0) ? (atr_buf[0] / close) * 100 : 0.0; //--- Calculate volatility double rsi_buf[1]; //--- Buffer for RSI if (CopyBuffer(rsi_handles_sym[i], 0, 0, 1, rsi_buf) != 1) { //--- Copy RSI data LogError(__FUNCTION__ + ": Failed to copy RSI buffer for symbol " + symbol); //--- Log error continue; //--- Skip on failure } double rsi = rsi_buf[0]; //--- Get RSI value bid_array[i] = bidPrice; //--- Store bid spread_array[i] = spread; //--- Store spread change_array[i] = change; //--- Store change vol_array[i] = vol; //--- Store vol rsi_array[i] = rsi; //--- Store RSI volatility_Array[i] = vol; //--- Store volatility prices_PrevArray[i] = bidPrice; //--- Update previous price } }
In der Funktion OnTick werden Aktualisierungen bei jedem Markttick durchgeführt, sodass die Daten für Zeitrahmen und Symbole in Echtzeit aktualisiert werden. Wir brechen vorzeitig ab, wenn „dashboardVisible“ falsch ist, um unnötige Verarbeitung zu vermeiden. Wir rufen „chartWidth“ mit ChartGetInteger unter Verwendung von CHART_WIDTH_IN_PIXELS ab, berechnen „fontSize“ skaliert mit „chartWidth / 800.0“, „cellWidth“ als „PanelWidth / 8“, und „cellHeight“ als 18. Wir setzen „y“ auf „Y_Offset + 75“ für das Zeitraster und durchlaufen die „Perioden“ mit der Funktion ArraySize. Für jeden Zeitrahmen werden „open“ mit iOpen und „close“ mit „iClose“ bei Shift 0 ermittelt, „atr_buf“ mit CopyBuffer aus „atr_handles_tf[i]“ kopiert und „vol“ als prozentualer ATR über „close“ berechnet, falls positiv.
Wir kopieren „rsi_buf“ aus „rsi_handles_tf[i]“ und erhalten „rsi“, setzen „clr“ und „trend“ basierend auf RSI > 50 für Aufwärtsbewegungen („↑“ in „UpColor“) oder < 50 für Abwärtsbewegungen („↓“ in „DownColor“). Wir hätten auch die Pfeile aus Schriftarten verwenden können, aber diese handcodierten Pfeile fügen sich nahtlos ein und erhöhen den holografischen Reiz. Wir aktualisieren Texte mit „createText“ für Trend, Vol (gefärbt „UpColor“, wenn > „Vol_Alert_Threshold“ mit Animation) und RSI (gefärbt basierend auf überkauft/überverkauft mit Animation), und rufen „HolographicPulse“ auf den Perioden-Text mit „ActiveColor“ auf, wenn er mit _Period übereinstimmt. Wir erhöhen „y“ um „cellHeight“.
Wir aktualisieren „y“ um 50 für das Symbolgitter und durchlaufen die Schleife „totalSymbols“. Für jedes Symbol aus SymbolName mit true, holen wir „bidPrice“ mit „SymbolInfoDouble“ unter Verwendung von „SYMBOL_BID“ und „spread“ mit SymbolInfoInteger unter Verwendung von SYMBOL_SPREAD, protokollieren und überspringen bei Misserfolg. Wir berechnen „change“ als Prozentsatz über „prices_PrevArray[i]“, erhalten „close“ mit iClose auf „PERIOD_H1“ bei Shift 0, kopieren „atr_buf“ aus „atr_handles_sym[i]“, um „vol“ zu berechnen, und „rsi_buf“ aus „rsi_handles_sym[i]“, um „rsi“ zu erhalten. Wir speichern Werte in „bid_array“, „spread_array“, „change_array“, „vol_array“, „rsi_array“ und „volatility_array“ und aktualisieren „prices_PrevArray[i]“ auf „bidPrice“. Wir können nun zum Bereich der Symbole übergehen, wo wir sie sortieren und mit Effekten anzeigen müssen.
// Sort indices for (int i = 0; i < totalSymbols; i++) indices[i] = i; //--- Initialize indices bool swapped = true; //--- Swap flag while (swapped) { //--- Loop until no swaps swapped = false; //--- Reset flag for (int j = 0; j < totalSymbols - 1; j++) { //--- Iterate through indices bool do_swap = false; //--- Swap decision int a = indices[j], b = indices[j + 1]; //--- Get indices if (sortMode == 0) { //--- Sort by name ASC string na = SymbolName(a, true), nb = SymbolName(b, true); //--- Get names if (na > nb) do_swap = true; //--- Swap if needed } else if (sortMode == 1) { //--- Sort by vol DESC if (vol_array[a] < vol_array[b]) do_swap = true; //--- Swap if needed } else if (sortMode == 2) { //--- Sort by change ABS DESC if (MathAbs(change_array[a]) < MathAbs(change_array[b])) do_swap = true; //--- Swap if needed } else if (sortMode == 3) { //--- Sort by RSI DESC if (rsi_array[a] < rsi_array[b]) do_swap = true; //--- Swap if needed } if (do_swap) { //--- Perform swap int temp = indices[j]; //--- Temporary store indices[j] = indices[j + 1]; //--- Swap indices[j + 1] = temp; //--- Complete swap swapped = true; //--- Set flag } } } // Display sorted symbols with pulse on high vol for (int j = 0; j < totalSymbols; j++) { //--- Iterate through sorted indices int i = indices[j]; //--- Get index string symbol = SymbolName(i, true); //--- Get symbol double bidPrice = bid_array[i]; //--- Get bid long spread = spread_array[i]; //--- Get spread double change = change_array[i]; //--- Get change double vol = vol_array[i]; //--- Get vol double rsi = rsi_array[i]; //--- Get RSI color clr_s = (symbol == _Symbol) ? ActiveColor : DataColor; //--- Set symbol color color clr_p = DataColor, clr_sp = DataColor, clr_ch = DataColor, clr_vol = DataColor, clr_rsi = DataColor; //--- Set default colors color clr_a1 = DataColor, clr_a2 = DataColor; //--- Set arrow colors // Price Change if (change > 0) { //--- Check positive change clr_p = UpColor; clr_ch = UpColor; clr_a1 = UpColor; clr_a2 = DataColor; //--- Set up colors } else if (change < 0) { //--- Check negative change clr_p = DownColor; clr_ch = DownColor; clr_a1 = DataColor; clr_a2 = DownColor; //--- Set down colors } // Volatility Alert if (vol > Vol_Alert_Threshold) { //--- Check high volatility clr_vol = UpColor; //--- Set vol color clr_s = (symbol == _Symbol) ? ActiveColor : UpColor; //--- Set symbol color } // RSI Color clr_rsi = (rsi > 70 ? DownColor : (rsi < 30 ? UpColor : DataColor)); //--- Set RSI color // Update Texts string displaySymbol = (symbol == _Symbol) ? "*" + symbol : symbol; //--- Format display symbol createText("Symbol_" + IntegerToString(j), displaySymbol, X_Offset + 10, y, clr_s, fontSize, FontType, vol > Vol_Alert_Threshold && EnableAnimations); //--- Update symbol text createText("Bid_" + IntegerToString(j), Bid(symbol), X_Offset + 10 + cellWidth, y, clr_p, fontSize, FontType, EnableAnimations); //--- Update bid text createText("Spread_" + IntegerToString(j), Spread(symbol), X_Offset + 10 + cellWidth * 2, y, clr_sp, fontSize, FontType); //--- Update spread text createText("Change_" + IntegerToString(j), StringFormat("%.2f%%", change), X_Offset + 10 + cellWidth * 3, y, clr_ch, fontSize, FontType); //--- Update change text createText("Vol_" + IntegerToString(j), StringFormat("%.2f%%", vol), X_Offset + 10 + cellWidth * 4, y, clr_vol, fontSize, FontType, vol > Vol_Alert_Threshold && EnableAnimations); //--- Update vol text createText("RSI_" + IntegerToString(j), StringFormat("%.1f", rsi), X_Offset + 10 + cellWidth * 5, y, clr_rsi, fontSize, FontType, (rsi > 70 || rsi < 30) && EnableAnimations); //--- Update RSI text createText("ArrowUp_" + IntegerToString(j), CharToString(236), X_Offset + 10 + cellWidth * 6, y, clr_a1, fontSize, "Wingdings"); //--- Update up arrow createText("ArrowDown_" + IntegerToString(j), CharToString(238), X_Offset + 10 + cellWidth * 7, y, clr_a2, fontSize, "Wingdings"); //--- Update down arrow // Pulse on high volatility if (vol > Vol_Alert_Threshold) { //--- Check high volatility HolographicPulse("Symbol_" + IntegerToString(j), clr_s, GlowColor); //--- Pulse symbol text } y += cellHeight; //--- Update y-coordinate } ChartRedraw(0); //--- Redraw chart }
Hier sortieren wir die Indizes, indem wir zunächst das Array „indices“ in einer Schleife von 0 bis „totalSymbols“ – 1 initialisieren. Wir verwenden einen Bubble-Sort-Ansatz, bei dem das „Swapped“-Flag anfangs auf „true“ gesetzt ist, und gehen in eine while-Schleife, bis keine Swaps mehr auftreten. Darin setzen wir „swapped“ auf false zurück, fahren dann eine Schleife von 0 bis „totalSymbols“ – 2, setzen „do_swap“ auf false und erhalten „a“ und „b“ als „indices[j]“ und „indices[j+1]“. Abhängig von „sortMode“: für 0 (name ASC), wir erhalten Namen über „SymbolName(a, true)“ und „SymbolName(b, true)“, tauschen wenn „na > nb“; für 1 (vol DESC), tauschen wenn „vol_array[a] < vol_array[b]“; for 2 (change ABS DESC), swap if „MathAbs(change_array[a]) < MathAbs(change_array[b])“; for 3 (RSI DESC), swap if „rsi_array[a] < rsi_array[b]“. Wenn „do_swap“, tauschen wir „indices[j]“ und „indices[j+1]“ unter Verwendung einer „temp“-Variablen und setzen „swapped“ auf true.
Als Nächstes zeigen wir sortierte Symbole an, indem wir eine Schleife über „totalSymbols“ ziehen, „i“ als „indices[j]“ erhalten und dann „symbol“ über „SymbolName(i, true)“, „bidPrice“ aus „bid_array[i]“, „spread“ aus „spread_array[i]“, „change“ aus „change_array[i]“, „vol“ aus „vol_array[i]“, und „rsi“ aus „rsi_array[i]“. Wir setzen „clr_s“ auf „ActiveColor“, wenn es mit „_Symbol“ übereinstimmt, sonst auf „DataColor“; andere Farben werden standardmäßig auf „DataColor“ gesetzt. Für Preisänderung: wenn „change > 0“, „clr_p“, „clr_ch“, „clr_a1“ auf „UpColor“ und „clr_a2“ auf „DataColor“; wenn < 0, auf „DownColor“ mit „clr_a1“ als „DataColor“ einstellen. Für Volatilitätsalarm: wenn „vol > Vol_Alert_Threshold“, setzen wir „clr_vol“ auf „UpColor“ und aktualisieren „clr_s“, wenn es nicht das aktuelle Symbol ist. Für RSI: Wir setzen „clr_rsi“ auf „DownColor“ wenn >70, „UpColor“ wenn <30, sonst „DataColor“.
Wir formatieren „displaySymbol“ mit „*“, wenn es mit „_Symbol“ übereinstimmt. Texte über „createText“ aktualisieren: symbol („Symbol_j“) mit „displaySymbol“, „clr_s“, animieren wenn hohe Volumina und aktiviert; bid („Bid_j“) mit „Bid(symbol)“, „clr_p“, animiert wenn aktiviert; spread („Spread_j“) mit „Spread(symbol)“, „clr_sp“; change („Change_j“) formatiert „%.2f%%“ über „StringFormat“, „clr_ch“; vol („Vol_j“) formatiert „%.2f%%“, „clr_vol“, animiert wenn hohe Vol und aktiviert; RSI („RSI_j“) formatiert „%.1f“, „clr_rsi“, Animation, wenn überkauft/überverkauft und aktiviert; Pfeil nach oben („ArrowUp_j“) mit „CharToString(236)“, „clr_a1“, „Wingdings“; Pfeil nach unten („ArrowDown_j“) mit „CharToString(238)“, „clr_a2“, „Wingdings“. Bei hohen Volumina „HolographicPulse“ mit „clr_s“ und „GlowColor“ auf Symboltext anwenden. Bei jeder Iteration wird „y“ um „cellHeight“ erhöht und schließlich neu gezeichnet. Wenn wir kompilieren, erhalten wir das folgende Ergebnis.

Aus der Visualisierung geht hervor, dass die Aktualisierungen bei jedem Markttick wirksam werden. Jetzt können wir den von uns erstellten Schaltflächen Leben einhauchen. Dies wird über die Ereignisbehandlung durch OnChartEvent erreicht.
//+------------------------------------------------------------------+ //| Chart Event Handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK) { //--- Handle click event if (sparam == "ToggleBtn") { //--- Check toggle button dashboardVisible = !dashboardVisible; //--- Toggle visibility objManager.DeleteAllObjects(); //--- Delete objects if (dashboardVisible) { //--- Check if visible InitDashboard(); //--- Reinitialize dashboard } else { createButton("ToggleBtn", "TOGGLE DASHBOARD", X_Offset + 10, Y_Offset + 10, 150, 25, TitleColor, PanelColor, UpColor); //--- Create toggle button } } else if (sparam == "SwitchTFBtn") { //--- Check switch TF button int currentIdx = -1; //--- Initialize current index for (int i = 0; i < ArraySize(periods); i++) { //--- Find current timeframe if (periods[i] == _Period) { //--- Match found currentIdx = i; //--- Set index break; //--- Exit loop } } int nextIdx = (currentIdx + 1) % ArraySize(periods); //--- Calculate next index if (!ChartSetSymbolPeriod(0, _Symbol, periods[nextIdx])) { //--- Switch timeframe LogError(__FUNCTION__ + ": Failed to switch timeframe, Error: " + IntegerToString(GetLastError())); //--- Log error } createButton("SwitchTFBtn", "NEXT TF", X_Offset + 170, (int)ObjectGetInteger(0, "SwitchTFBtn", OBJPROP_YDISTANCE), 120, 25, UpColor, PanelColor, UpColor, EnableAnimations); //--- Update button } else if (sparam == "SortBtn") { //--- Check sort button sortMode = (sortMode + 1) % 4; //--- Cycle sort mode createButton("SortBtn", "SORT: " + sortNames[sortMode], X_Offset + 300, (int)ObjectGetInteger(0, "SortBtn", OBJPROP_YDISTANCE), 150, 25, TitleColor, PanelColor, UpColor, EnableAnimations); //--- Update button } ObjectSetInteger(0, sparam, OBJPROP_STATE, false); //--- Reset button state ChartRedraw(0); //--- Redraw chart } }
Wir implementieren die Ereignishandler durch OnChartEvent, um interaktive Ereignisse zu behandeln und auf Schaltflächenklicks zu reagieren, um die Sichtbarkeit umzuschalten, den Zeitrahmen zu wechseln und den Sortiermodus zu wechseln. Für CHARTEVENT_OBJECT_CLICK prüfen wir „sparam“ gegen „ToggleBtn“, schalten „dashboardVisible“ um, löschen Objekte mit „objManager.DeleteAllObjects“, und Reinitialisierung mit „InitDashboard“, wenn sichtbar, oder Erstellung eines neuen „ToggleBtn“ mit „createButton“, wenn versteckt. Wenn „sparam“ „SwitchTFBtn“ ist, finden wir den aktuellen Zeitrahmenindex in „periods“ mit einer Schleife, berechnen „nextIdx“ als „(currentIdx + 1) % ArraySize(periods)“, wechseln den Chart mit „ChartSetSymbolPeriod“ unter Verwendung von „periods[nextIdx]“, protokollieren Fehler mit „LogError“ und aktualisieren den Button mit „createButton“ inklusive Animation, wenn „EnableAnimations“.
Für „SortBtn“ setzen wir „sortMode“ mit „(sortMode + 1) % 4“ und aktualisieren den Schaltflächentext zu "SORT: " + "sortNames[sortMode]" unter Verwendung von „createButton“ mit Animation. Wir setzen den Schaltflächenstatus mit ObjectSetInteger für OBJPROP_STATE auf false zurück und zeichnen das Chart mit der Funktion ChartRedraw neu. Dies ermöglicht die Kontrolle über die Anzeige und die Datenorganisation des Dashboards. Nach dem Kompilieren erhalten wir die folgende Ausgabe.

Wir sehen, dass wir das Dashboard bei jedem Markttick aktualisieren können und auf die Schaltflächenklicks für die Umschaltung des Dashboards, den Wechsel des Zeitrahmens und die Sortierung der Indizes für die Symbolmetriken reagieren und somit unsere Ziele erreichen. Nun bleibt nur noch die Prüfung der Durchführbarkeit des Projekts, die im vorangegangenen Abschnitt behandelt wurde.
Backtests
Wir haben die Tests durchgeführt, und unten sehen Sie die kompilierte Visualisierung in einem einzigen Graphics Interchange Format (GIF) Bitmap-Bildformat.

Schlussfolgerung
Abschließend haben wir ein dynamisches holografisches Dashboard in MQL5 erstellt, das Symbole und Zeitrahmen mit RSI, Volatilitätswarnungen und Sortierung überwacht und mit Impulsanimationen und interaktiven Schaltflächen für ein immersives Handelserlebnis sorgt. Wir haben die Architektur und Implementierung detailliert beschrieben und verwenden Klassenkomponenten wie „CObjectManager“ und Funktionen wie „HolographicPulse“, um visuell ansprechende Einblicke in Echtzeit zu liefern. Sie können dieses Dashboard an Ihre Handelsbedürfnisse anpassen und Ihre Analyse mit holografischen Effekten und Kontrollen aufwerten.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/18880
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.
Entwicklung des Price Action Analysis Toolkit (Teil 34): Umwandlung von Marktrohdaten in Prognosemodellen mithilfe einer fortschrittlichen Pipeline der Datenerfassung
Datenwissenschaft und ML (Teil 46): Aktienmarktprognosen mit N-BEATS in Python
Klassische Strategien neu interpretieren (Teil 14): Analyse mehrerer Strategien
Entwicklung des Price Action Analysis Toolkit (Teil 33): Candle-Range Theory Tool
- 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.