English Русский
preview
MQL5-Handelswerkzeuge (Teil 21): Hinzufügen eines Cyberpunk-Theme zu Regressionsdiagrammen

MQL5-Handelswerkzeuge (Teil 21): Hinzufügen eines Cyberpunk-Theme zu Regressionsdiagrammen

MetaTrader 5Handelssysteme |
34 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

Sie verlassen sich bei der Analyse von Symbolpaaren im Handel auf Standard-Regressionsdiagramme, doch diese wirken bei längeren Sitzungen statisch und wenig ansprechend, was es erschwert, den Fokus auf Korrelationen zu behalten oder Trends inmitten eintöniger Darstellungen zu erkennen – insbesondere in Umgebungen mit schwachem Licht, in denen Blendungen die Lesbarkeit beeinträchtigen. Einfachen Darstellungen fehlen dynamische Elemente, um wichtige Datenpunkte hervorzuheben oder das Interesse aufrechtzuerhalten, was dazu führt, dass in volatilen Märkten wichtige Erkenntnisse übersehen werden. Dieser Artikel richtet sich an Entwickler der Sprache MetaQuotes Language 5 (MQL5) sowie an algorithmische Händler, die ihre Charting-Tools um eindrucksvolle Themes ergänzen möchten, um die Visualisierung zu verbessern.

In unserem vorherigen Artikel (Teil 20) haben wir in MQL5 ein Canvas-basiertes Diagrammtool für statistische Korrelations- und lineare Regressionsanalysen zwischen zwei Symbolen erstellt, das verschoben und in der Größe angepasst werden kann. In Teil 21 erweitern wir dieses Tool um einen Cyberpunk-Themenmodus mit Neon-Effekten, Animationen, holografischen Rahmen und futuristischen Elementen. Dieses System mit zwei Themenmodi umfasst eine Umschaltfunktion per Schaltfläche, dynamische Hintergründe mit Sternen und Neon-Grafiken für eine fesselnde Analyse von Symbolpaaren im Handel. Wir werden Folgendes behandeln:

  1. Cyberpunk-Thema für eine verbesserte Visualisierung der Regression
  2. Implementierung in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende werden Sie ein thematisches Regressionsdiagramm haben, das Standard- und Cyberpunk-Stile miteinander verbindet – legen wir los!


Cyberpunk-Thema für eine verbesserte Visualisierung der Regression

Der Cyberpunk-Themenmodus verbessert die Visualisierung von Regressionsanalysen durch die Einbindung von Neonlicht, animierten Impulsen, holografischen Rahmen und dynamischen Rastern und verwandelt Standarddiagramme in eindrucksvolle, futuristische Darstellungen, die fesselnd wirken und zugleich die analytische Genauigkeit bewahren. Es nutzt Farbmischungen, Alpha-Compositing und zeitgesteuerte Animationen, um Effekte wie leuchtende Linien, funkelnde Hintergründe und Panels im Glassmorphism-Stil zu erzeugen – ideal für den Handel im Nachtmodus oder zur individuellen Gestaltung.

Dieser Themenmodus lässt sich über eine Schaltfläche umschalten und ermöglicht sowohl den Cyberpunk- als auch den Standardmodus für eine vielseitige Analyse von Symbolpaaren. Wir werden eine Themenumschaltung mit Animationen über einen Timer hinzufügen, Cyberpunk-Elemente wie Neonpunkte und leuchtende Rahmen mithilfe von Überblendungen darstellen und gleichzeitig Standardfunktionen für die Dual-Mode-Kompatibilität anpassen. Kurz gesagt: Hier ist eine visuelle Darstellung unserer Ziele.

GIF ZUM THEMA CYBERPUNK


Implementierung in MQL5

Um das Programm in MQL5 zu verbessern, müssen wir neue globale Variablen und Eingabeparameter hinzufügen, um es an die neuen Verbesserungen und Themen anzupassen, die wir einführen werden.

Erweiterung von Eingaben und globalen Variablen für die Themenverwaltung

Um die neuen Cyberpunk-Funktionen zu unterstützen, führen wir Eingabeparameter zur Steuerung der Leuchtintensität sowie globale Variablen für Farben, Flags und Animations-Tracking ein.

//+------------------------------------------------------------------+
//|  Canvas Graphing PART 2 - Statistical Regression (Cyberpunk).mq5 |
//|                           Copyright 2026, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Allan Munene Mutiiria."
#property link "https://t.me/Forex_Algo_Trader"
#property version "1.00"
#property strict
#property description "Dual Theme Regression: Standard & Cyberpunk Modes"

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "=== CYBERPUNK MODE SETTINGS ==="
input double             glowIntensity = 0.8;           // Glow Intensity (0-1)

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
bool isCyberpunkThemeEnabled = false;                   // Initialize cyberpunk theme flag
int themeToggleButtonX = 0;                             // Initialize theme button X
int themeToggleButtonY = 0;                             // Initialize theme button Y
const int THEME_TOGGLE_BUTTON_SIZE = 25;                // Set theme toggle button size

const color CYBER_PRIMARY_COLOR = C'0,191,255';         // Set cyber primary color
const color CYBER_SECONDARY_COLOR = C'255,0,255';       // Set cyber secondary color
const color CYBER_DATA_POINTS_COLOR = C'255,50,150';    // Set cyber data points color
const color CYBER_REGRESSION_COLOR = C'0,150,255';      // Set cyber regression color

int currentAnimationFrame = 0;                          // Initialize animation frame

Zunächst fügen wir eine neue Eingabegruppe === CYBERPUNK-MODUS-EINSTELLUNGEN === hinzu, mit dem Parameter glowIntensity vom Typ double im Bereich von 0 bis 1, dessen Standardwert 0,8 ist, um die Intensität der Leuchteffekte im Cyberpunk-Themenmodus zu steuern. Als Nächstes deklarieren wir globale Variablen für die Themenverwaltung: isCyberpunkThemeEnabled, initialisiert auf false, als Flag zum Umschalten zwischen den Modi; themeToggleButtonX und themeToggleButtonY werden auf 0 gesetzt, um die Positionierung der Schaltflächen festzulegen; die Konstante THEME_TOGGLE_BUTTON_SIZE wird auf 25 Pixel festgelegt, um die Abmessungen zu definieren. Wir definieren Cyberpunk-spezifische Farben mithilfe der Notation C'...': "CYBER_PRIMARY_COLOR" als leuchtendes Cyan C'0,191,255', "CYBER_SECONDARY_COLOR" als Magenta C'255,0,255', "CYBER_DATA_POINTS_COLOR" als Rosa C'255,50,150' und "CYBER_REGRESSION_COLOR" als Blau C'0,150,255', wodurch lebendige, thematisch bezogene Farbpaletten entstehen.

Sie können jede andere Farbkombination wählen, die Ihnen gefällt; wir haben diese Option lediglich zur Veranschaulichung willkürlich ausgewählt. Abschließend setzen wir currentAnimationFrame auf 0, um den Fortschritt der Animation bei dynamischen Effekten zu verfolgen. Als Nächstes werden wir eine Hilfsfunktion erstellen, um Farben im Cyberpunk-Modus sanft ineinander übergehen zu lassen, damit die Darstellung nicht flach wirkt.

Implementierung eines Dienstprogramms zur Farbmischung

Um dynamische Effekte wie Leuchteffekte und Farbverläufe zu ermöglichen, definieren wir eine Funktion zur Interpolation zwischen zwei Farben.

//+------------------------------------------------------------------+
//| Blend colors                                                     |
//+------------------------------------------------------------------+
color BlendColors(color color1, color color2, double ratio)
  {
   //--- Extract color1 red
   uchar r1 = (uchar)((color1 >> 16) & 0xFF);
   //--- Extract color1 green
   uchar g1 = (uchar)((color1 >> 8) & 0xFF);
   //--- Extract color1 blue
   uchar b1 = (uchar)(color1 & 0xFF);
   
   //--- Extract color2 red
   uchar r2 = (uchar)((color2 >> 16) & 0xFF);
   //--- Extract color2 green
   uchar g2 = (uchar)((color2 >> 8) & 0xFF);
   //--- Extract color2 blue
   uchar b2 = (uchar)(color2 & 0xFF);
   
   //--- Blend red
   uchar r = (uchar)(r1 * (1.0 - ratio) + r2 * ratio);
   //--- Blend green
   uchar g = (uchar)(g1 * (1.0 - ratio) + g2 * ratio);
   //--- Blend blue
   uchar b = (uchar)(b1 * (1.0 - ratio) + b2 * ratio);
   
   //--- Return blended color
   return (r << 16) | (g << 8) | b;
  }

Hier implementieren wir die Funktion BlendColors, um sanfte Farbübergänge für Cyberpunk-Effekte zu erzeugen. Dabei extrahieren wir mithilfe von Bitverschiebungen die RGB-Komponenten aus zwei Eingabefarben, mischen dann jeden Kanal, indem wir den ersten mit (1 – ratio) und den zweiten mit ratio gewichten, und kombinieren diese schließlich zu einem neuen Farbwert für dynamische Farbverläufe oder Animationen. Stellen Sie sich das wie einen magischen Farbmischer vor. Man gibt zwei Farben ein und sagt: Mische sie zu diesem Anteil (Verhältnis etwa 0,5 für halb und halb), und schon entsteht eine neue Farbe. Im Cyber-Modus werden wir damit sanfte, leuchtende Effekte erzeugen, beispielsweise indem wir Blau und Rosa mischen, um violettes Licht zu erzeugen. Ohne diese Funktion würden die Farben flach wirken; mit ihr sieht alles harmonischer und professioneller aus. Als Nächstes fügen wir in der Kopfzeile eine Schaltfläche zum Wechseln des Themes hinzu, damit wir problemlos zwischen den Themes wechseln können.

Schaltfläche zum Umschalten des Themes hinzufügen

Um einen Moduswechsel zu ermöglichen, zeigen wir in der Kopfzeile eine Schaltfläche an und zentrieren die Regressionsgerade, um ein ausgewogenes Erscheinungsbild zu erzielen.

//--- Adjust line drawing for centering in drawRegressionPlot
//--- Draw line
mainCanvas.LineAA(lineStartScreenX, lineStartScreenY + w - regressionLineWidth/2,
  lineEndScreenX, lineEndScreenY + w - regressionLineWidth/2, argbLine);

//+------------------------------------------------------------------+
//| Draw theme toggle button                                         |
//+------------------------------------------------------------------+
void drawThemeToggleButton()
  {
   //--- Compute button X
   themeToggleButtonX = currentWidthPixels - THEME_TOGGLE_BUTTON_SIZE - 8;
   //--- Compute button Y
   themeToggleButtonY = (HEADER_BAR_HEIGHT - THEME_TOGGLE_BUTTON_SIZE) / 2;
   
   //--- Declare button color
   color buttonColor;
   //--- Check cyber mode
   if (isCyberpunkThemeEnabled)
     {
      //--- Set cyber color
      buttonColor = isHoveringThemeButton ? LightenColor(CYBER_PRIMARY_COLOR, 0.3) : CYBER_PRIMARY_COLOR;
     }
   else
     {
      //--- Set standard color
      buttonColor = isHoveringThemeButton ? LightenColor(themeColor, 0.3) : themeColor;
     }
   
   //--- Get button ARGB
   uint argbButton = ColorToARGB(buttonColor, 200);
   //--- Get border ARGB
   uint argbBorder = ColorToARGB(isCyberpunkThemeEnabled ? CYBER_SECONDARY_COLOR : themeColor, 255);
   
   //--- Fill button
   mainCanvas.FillRectangle(themeToggleButtonX, themeToggleButtonY,
     themeToggleButtonX + THEME_TOGGLE_BUTTON_SIZE,
     themeToggleButtonY + THEME_TOGGLE_BUTTON_SIZE, argbButton);
   
   //--- Draw border
   mainCanvas.Rectangle(themeToggleButtonX, themeToggleButtonY,
     themeToggleButtonX + THEME_TOGGLE_BUTTON_SIZE,
     themeToggleButtonY + THEME_TOGGLE_BUTTON_SIZE, argbBorder);
   
   //--- Set icon font
   mainCanvas.FontSet("Arial Black", 16);
   //--- Get text ARGB
   uint argbText = ColorToARGB(clrWhite, 255);
   //--- Set icon text
   string iconText = isCyberpunkThemeEnabled ? "◆" : "●";
   //--- Draw icon
   mainCanvas.TextOut(themeToggleButtonX + THEME_TOGGLE_BUTTON_SIZE/2, themeToggleButtonY + 3,
     iconText, argbText, TA_CENTER);
  }

Zunächst passen wir die Funktion drawRegressionPlot so an, dass die Regressionsgerade vertikal zentriert ist. Dies geschieht durch Anpassung der Y-Positionen in der Schleife für die Breite. Ziehe die Hälfte der Linienbreite regressionLineWidth/2 sowohl von der Start- als auch von der Endposition der Y-Koordinate auf dem Bildschirm ab. Dadurch wird beim Zeichnen mit LineAA eine gleichmäßige Strichstärke entlang der berechneten Linie gewährleistet. Früher sah die dicke Linie vielleicht etwas dezentriert aus, wie eine schiefe Straße. Wenn man die Hälfte der Breite abzieht, wird es perfekt zentriert. Es ist nur eine kleine Korrektur, aber dadurch sieht die Linie ordentlich und professionell aus – so, als würde man ein Bild an der Wand gerade hängen.

Als Nächstes implementieren wir die Funktion drawThemeToggleButton. Diese Funktion blendet in der Kopfzeile eine Umschaltfläche zum Wechseln zwischen den Modi ein. Sie positioniert die Schaltfläche rechts in der Kopfzeile mit einem kleinen Randabstand und wählt eine Farbe entsprechend dem Theme und dem Hover-Zustand aus. Für die Dynamik wird LightenColor verwendet. Die Funktion füllt ein Rechteck mit halbtransparentem ARGB, zeichnet einen Rahmen in der themenspezifischen Farbe und legt die Schriftart Arial Black in fetter Schrift fest. Anschließend wird mit der Methode TextOut ein quadratisches Symbol für Cyberpunk bzw. ein kreisförmiges Symbol für Standard in weißem ARGB zentriert. Sie könnten wie bisher Symbole aus dem Wingdings-Zeichensatz verwenden. Wir haben uns jedoch dafür entschieden, um einen schlichten, technisch anmutenden Look zu erzielen. Als Nächstes rendern wir die Cyberpunk-Visualisierung. Beginnen wir mit der Überlagerung der Hauptzeichenfläche.

Zeichnen von Cyberpunk-Hintergrund und Rahmen

Um die Grundlage für das Thema zu schaffen, gestalten wir sternenbesetzte Farbverläufe und animierte holografische Rahmen.

//+------------------------------------------------------------------+
//| Draw cyberpunk background                                        |
//+------------------------------------------------------------------+
void drawCyberpunkBackground()
  {
   //--- Set top color
   color topColor = C'10,10,25';
   //--- Set bottom color
   color bottomColor = C'20,5,30';
   
   //--- Compute alpha
   uchar alphaChannel = (uchar)(255 * backgroundOpacityLevel);
   
   //--- Loop over rows
   for (int y = HEADER_BAR_HEIGHT; y < currentHeightPixels; y++)
     {
      //--- Compute factor
      double factor = (double)(y - HEADER_BAR_HEIGHT) / (currentHeightPixels - HEADER_BAR_HEIGHT);
      //--- Blend row color
      color rowColor = BlendColors(topColor, bottomColor, factor);
      //--- Get ARGB
      uint argbColor = ColorToARGB(rowColor, alphaChannel);

      //--- Loop over columns
      for (int x = 0; x < currentWidthPixels; x++)
        {
         //--- Check for star
         if (MathRand() % 800 == 0)
           {
            //--- Compute brightness
            uchar brightness = (uchar)(100 + MathRand() % 155);
            //--- Get star color
            uint starColor = ColorToARGB(C'255,255,255', brightness);
            //--- Blend star
            blendPixelSet(mainCanvas, x, y, starColor);
           }
         else
           {
            //--- Blend normal
            blendPixelSet(mainCanvas, x, y, argbColor);
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Draw holographic border                                          |
//+------------------------------------------------------------------+
void drawHolographicBorder()
  {
   //--- Compute phase
   double phase = (currentAnimationFrame % 360) * M_PI / 180.0;
   //--- Compute glow factor
   double glowFactor = (MathSin(phase) + 1.0) * 0.5;
   
   //--- Blend border color
   color borderColor = BlendColors(CYBER_PRIMARY_COLOR, CYBER_SECONDARY_COLOR, glowFactor);
   
   //--- Loop for glow layers
   for (int i = 0; i < 3; i++)
     {
      //--- Compute alpha
      uchar alpha = (uchar)(80 * glowIntensity * (3 - i) / 3.0);
      //--- Get glow color
      uint glowColor = ColorToARGB(borderColor, alpha);
      
      //--- Draw top glow
      for (int x = i; x < currentWidthPixels - i; x++)
        {
         //--- Blend pixel
         blendPixelSet(mainCanvas, x, i, glowColor);
        }
      //--- Draw bottom glow
      for (int x = i; x < currentWidthPixels - i; x++)
        {
         //--- Blend pixel
         blendPixelSet(mainCanvas, x, currentHeightPixels - 1 - i, glowColor);
        }
      //--- Draw left glow
      for (int y = i; y < currentHeightPixels - i; y++)
        {
         //--- Blend pixel
         blendPixelSet(mainCanvas, i, y, glowColor);
        }
      //--- Draw right glow
      for (int y = i; y < currentHeightPixels - i; y++)
        {
         //--- Blend pixel
         blendPixelSet(mainCanvas, currentWidthPixels - 1 - i, y, glowColor);
        }
     }
   
   //--- Get border ARGB
   uint argbBorder = ColorToARGB(borderColor, 255);
   //--- Draw outer border
   mainCanvas.Rectangle(0, 0, currentWidthPixels - 1, currentHeightPixels - 1, argbBorder);
   //--- Draw inner border
   mainCanvas.Rectangle(1, 1, currentWidthPixels - 2, currentHeightPixels - 2, argbBorder);
   
   //--- Draw corner accents
   drawCornerAccents(borderColor);
  }

//+------------------------------------------------------------------+
//| Draw corner accents                                              |
//+------------------------------------------------------------------+
void drawCornerAccents(color accentColor)
  {
   //--- Get accent ARGB
   uint argbAccent = ColorToARGB(accentColor, 255);
   //--- Set accent size
   int accentSize = 15;
   
   //--- Loop for top-left
   for (int i = 0; i < 3; i++)
     {
      //--- Draw horizontal
      mainCanvas.Line(5, 5 + i, accentSize, 5 + i, argbAccent);
      //--- Draw vertical
      mainCanvas.Line(5 + i, 5, 5 + i, accentSize, argbAccent);
     }
   
   //--- Loop for top-right
   for (int i = 0; i < 3; i++)
     {
      //--- Draw horizontal
      mainCanvas.Line(currentWidthPixels - accentSize - 1, 5 + i, currentWidthPixels - 6, 5 + i, argbAccent);
      //--- Draw vertical
      mainCanvas.Line(currentWidthPixels - 6 - i, 5, currentWidthPixels - 6 - i, accentSize, argbAccent);
     }
   
   //--- Loop for bottom-left
   for (int i = 0; i < 3; i++)
     {
      //--- Draw horizontal
      mainCanvas.Line(5, currentHeightPixels - 6 - i, accentSize, currentHeightPixels - 6 - i, argbAccent);
      //--- Draw vertical
      mainCanvas.Line(5 + i, currentHeightPixels - accentSize - 1, 5 + i, currentHeightPixels - 6, argbAccent);
     }
   
   //--- Loop for bottom-right
   for (int i = 0; i < 3; i++)
     {
      //--- Draw horizontal
      mainCanvas.Line(currentWidthPixels - accentSize - 1, currentHeightPixels - 6 - i, currentWidthPixels - 6, currentHeightPixels - 6 - i, argbAccent);
      //--- Draw vertical
      mainCanvas.Line(currentWidthPixels - 6 - i, currentHeightPixels - accentSize - 1, currentWidthPixels - 6 - i, currentHeightPixels - 6, argbAccent);
     }
  }

Für den Cyberpunk-Rahmen implementieren wir die Funktion drawCyberpunkBackground, um einen dunklen, sternenübersäten Farbverlauf für das Cyberpunk-Thema zu erstellen, wobei wir den oberen Bereich auf ein tiefes Blau-Schwarz (C'10,10,25') und den unteren Bereich auf ein Violett-Schwarz (C'20,5,30') setzen, berechnen den Alpha-Wert anhand der Deckkraft, durchlaufen dann die Zeilen in einer Schleife, um die Farben mit BlendColors zu mischen, und fügen zufällig weiße Sterne über MathRand% 800 == 0 mit unterschiedlicher Helligkeit hinzu, wobei alle Pixel mit blendPixelSet überblendet werden, um einen kosmischen Effekt zu erzeugen.

Als Nächstes erstellen wir drawHolographicBorder für animierte, leuchtende Rahmen, berechnen dabei die sinusbasierte Phase aus currentAnimationFrame für die Pulsation, mischen Primär- und Sekundär-Cyberfarben für einen dynamischen Rand und schichten dann Leuchteffekte mit abnehmender Deckkraft in Schleifen für oben/unten/links/rechts, zeichnen die Hauptrahmen mit Rectangle und rufen drawCornerAccents für zusätzliche Akzente auf. In drawCornerAccents wandeln wir die Akzentfarbe in ARGB um, legen die Größe auf 15 fest und zeichnen in einer dreimaligen Schleife mit der Funktion Line kurze horizontale und vertikale Linien an jeder Ecke (oben links, oben rechts, unten links, unten rechts), wodurch klammerartige Akzente für einen technischen Rahmen entstehen. Nun müssen wir eine futuristische Kopfzeile zeichnen.

Zeichnen einer futuristischen Kopfzeile

Als krönenden Abschluss rendern wir eine Kopfzeile mit Farbverlauf, leuchtendem Text und Animationen.

//+------------------------------------------------------------------+
//| Draw futuristic header                                           |
//+------------------------------------------------------------------+
void drawFuturisticHeader()
  {
   //--- Compute phase
   double phase = (currentAnimationFrame % 360) * M_PI / 180.0;
   //--- Compute anim factor
   double animFactor = (MathSin(phase) + 1.0) * 0.5;
   
   //--- Loop over header rows
   for (int y = 0; y < HEADER_BAR_HEIGHT; y++)
     {
      //--- Compute grad factor
      double gradFactor = (double)y / HEADER_BAR_HEIGHT;
      
      //--- Declare header color
      color headerColor;
      //--- Check dragging
      if (isDraggingCanvas)
        {
         //--- Blend dragged
         headerColor = BlendColors(DarkenColor(CYBER_PRIMARY_COLOR, 0.7), DarkenColor(CYBER_SECONDARY_COLOR, 0.7), gradFactor);
        }
      //--- Check hovering
      else if (isHoveringHeader)
        {
         //--- Blend hovered
         headerColor = BlendColors(DarkenColor(CYBER_PRIMARY_COLOR, 0.5), DarkenColor(CYBER_SECONDARY_COLOR, 0.5), gradFactor);
        }
      else
        {
         //--- Blend normal
         headerColor = BlendColors(DarkenColor(CYBER_PRIMARY_COLOR, 0.8), DarkenColor(CYBER_SECONDARY_COLOR, 0.8), gradFactor);
        }
      
      //--- Get header ARGB
      uint argbHeader = ColorToARGB(headerColor, 255);
      
      //--- Loop over columns
      for (int x = 0; x < currentWidthPixels; x++)
        {
         //--- Set pixel
         mainCanvas.PixelSet(x, y, argbHeader);
        }
     }
   
   //--- Blend border
   color borderColor = BlendColors(CYBER_PRIMARY_COLOR, CYBER_SECONDARY_COLOR, animFactor);
   //--- Get border ARGB
   uint argbBorder = ColorToARGB(borderColor, 255);
   //--- Draw outer
   mainCanvas.Rectangle(0, 0, currentWidthPixels - 1, HEADER_BAR_HEIGHT, argbBorder);
   //--- Draw inner
   mainCanvas.Rectangle(1, 1, currentWidthPixels - 2, HEADER_BAR_HEIGHT - 1, argbBorder);
   
   //--- Set title font
   mainCanvas.FontSet("Arial Black", titleFontSize);
   
   //--- Format title
   string titleText = StringFormat("◆ %s vs %s - Linear Regression ◆", secondarySymbol, primarySymbol);
   
   //--- Get glow ARGB
   uint glowColor = ColorToARGB(CYBER_PRIMARY_COLOR, (uchar)(120 * glowIntensity));
   //--- Loop for glow Y
   for (int offsetY = -1; offsetY <= 1; offsetY++)
     {
      //--- Loop for glow X
      for (int offsetX = -1; offsetX <= 1; offsetX++)
        {
         //--- Skip center
         if (offsetX == 0 && offsetY == 0) continue;
         //--- Draw glow text
         mainCanvas.TextOut(currentWidthPixels / 2 + offsetX, (HEADER_BAR_HEIGHT - titleFontSize) / 2 + offsetY, 
           titleText, glowColor, TA_CENTER);
        }
     }
   
   //--- Set text color
   uint textColor = ColorToARGB(C'255,105,180', 255);
   //--- Draw title
   mainCanvas.TextOut(currentWidthPixels / 2, (HEADER_BAR_HEIGHT - titleFontSize) / 2, 
     titleText, textColor, TA_CENTER);
  }

Hier implementieren wir die Funktion drawFuturisticHeader, um eine animierte Kopfzeile mit Farbverlauf für das Cyberpunk-Thema darzustellen. Dazu berechnen wir zunächst eine Phase aus currentAnimationFrame unter Verwendung einer Sinusfunktion für pulsierende Effekte sowie einen Animationsfaktor, der auf den Bereich zwischen 0 und 1 normiert ist. Anschließend durchlaufen wir die Zeilen der Kopfzeile in einer Schleife, um einen vertikalen Gradientenfaktor zu berechnen, mischen die je nach Status (Ziehen, Hover oder Standard) abgedunkelten primären und sekundären Cyber-Farben mit BlendColors und DarkenColor, konvertieren sie in ARGB und setzen jedes Pixel zeilenweise mit PixelSet für eine dynamische Schattierung.

Um einen Rahmen hinzuzufügen, mischen wir Primär- und Sekundärfarben mit dem Animationsfaktor und zeichnen mit der Funktion Rectangle äußere und innere Rechtecke in den gemischten ARGB-Farben. Wir haben die Schriftart Arial Black mit FontSet, formatieren den Titel mit Symbolen und Paarnamen mithilfe von StringFormat, erzeugen dann einen Glüheffekt, indem wir versetzte Texte in einem 3x3-Raster (die Mitte wird übersprungen) mit Cyber-Primärfarben in ARGB bei geringer Deckkraft zeichnen, und überlagern den Haupttitel in leuchtendem Pink (ARGB) zentriert mit der Methode TextOut. Als Nächstes müssen wir das Cyberpunk-Regressionsdiagramm zeichnen, doch zunächst benötigen wir einige Hilfsfunktionen zum Darstellen des Rasters und der Datenpunkte.

Zeichnen von Cyberpunk-Rastern und Neonpunkten

Um die Darstellung mehrschichtiger zu gestalten, fügen wir animierte Raster und leuchtende Datenmarkierungen hinzu.

//+------------------------------------------------------------------+
//| Draw cyberpunk grid                                              |
//+------------------------------------------------------------------+
void drawCyberpunkGrid(int startX, int startY, int width, int height)
  {
   //--- Set grid spacing
   int gridSpacing = 40;
   //--- Compute phase
   double phase = (currentAnimationFrame % 120) * M_PI / 60.0;
   //--- Compute alpha
   uchar gridAlpha = (uchar)(30 + 15 * MathSin(phase));
   
   //--- Set grid color
   color gridColor = CYBER_PRIMARY_COLOR;
   //--- Get grid ARGB
   uint argbGrid = ColorToARGB(gridColor, gridAlpha);
   
   //--- Loop vertical lines
   for (int x = startX; x < startX + width; x += gridSpacing)
     {
      //--- Loop over height
      for (int y = startY; y < startY + height; y++)
        {
         //--- Blend grid pixel
         blendPixelSet(mainCanvas, x, y, argbGrid);
        }
     }
   
   //--- Loop horizontal lines
   for (int y = startY; y < startY + height; y += gridSpacing)
     {
      //--- Loop over width
      for (int x = startX; x < startX + width; x++)
        {
         //--- Blend grid pixel
         blendPixelSet(mainCanvas, x, y, argbGrid);
        }
     }
  }

//+------------------------------------------------------------------+
//| Draw neon point                                                  |
//+------------------------------------------------------------------+
void drawNeonPoint(int centerX, int centerY, int radius, color pointColor)
  {
   //--- Loop for glow
   for (int glowRadius = radius + 4; glowRadius > radius; glowRadius--)
     {
      //--- Compute alpha
      uchar glowAlpha = (uchar)(80 * glowIntensity * (radius + 4 - glowRadius) / 4.0);
      //--- Get glow color
      uint glowColor = ColorToARGB(pointColor, glowAlpha);
      //--- Draw glow circle
      drawCirclePoint(centerX, centerY, glowRadius, glowColor);
     }
   
   //--- Get point ARGB
   uint argbPoint = ColorToARGB(pointColor, 255);
   //--- Draw point circle
   drawCirclePoint(centerX, centerY, radius, argbPoint);
   
   //--- Get center ARGB
   uint argbCenter = ColorToARGB(C'255,255,255', 200);
   //--- Draw center circle
   drawCirclePoint(centerX, centerY, radius - 1, argbCenter);
  }

Wir implementieren die Funktion drawCyberpunkGrid, um im Cyberpunk-Modus eine animierte Rasterüberlagerung für den Diagrammbereich zu erstellen. Dabei legen wir den Abstand auf 40 Pixel fest, berechnen anhand von currentAnimationFrame eine Phase für eine sinusförmige Pulsation und variieren die Deckkraft zwischen 30 und 45, um einen dezenten Schimmer zu erzeugen. Als Nächstes verwenden wir die primäre Cyberpunk-Farbe, konvertieren sie in ARGB mit dynamischem Alpha-Kanal und zeichnen dann in einer Schleife vertikale Linien in regelmäßigen Abständen, indem wir mit blendPixelSet Pixel über die gesamte Höhe mischen. Auf ähnliche Weise zeichnen wir horizontale Linien über die gesamte Breite und fügen so einen futuristischen, matrixartigen Hintergrund hinzu, ohne die Daten zu überladen. Dieses Raster trägt durch Überblendungen mit geringer Deckkraft dazu bei, das thematische Eintauchen in die Welt zu verstärken, und sorgt dafür, dass es sich nahtlos in andere Elemente einfügt. Sie können es aber auch weglassen, wenn Sie es nicht wollen.

Um Neon-Effekte hinzuzufügen, erstellen wir die Funktion drawNeonPoint für Datenpunkte. Dabei durchlaufen wir in einer Schleife vom Radius +4 bis hinunter zum Radius +1, um konzentrische Leuchtkreise mit abnehmender Deckkraft zu zeichnen, die mit glowIntensity skaliert werden. Dabei verwenden wir drawCirclePoint (unter der Annahme einer Basis-Kreiszeichnungsfunktion) in der Punktfarbe ARGB. Anschließend zeichnen wir den Mittelpunkt mit vollem Radius und voller Deckkraft, gefolgt von einem kleineren inneren Kreis in halbtransparentem Weiß C'255,255,255' mit einer Deckkraft von 200 als Highlight. So entsteht eine pulsierende Sci-Fi-Visualisierung, die sich bei dunklen Themes besonders gut abhebt. Auch hier lässt sich die Farbauswahl anpassen. Wir haben für die Demonstration lediglich beliebige Werte verwendet. Mit diesen Hilfsfunktionen können wir nun die Cyberpunk-Diagramme darstellen.

Darstellung der Cyberpunk-Regression

Um die Kernanalyse darzustellen, passen wir das Diagramm mit Rastern, Achsen, Skalenstrichen, Linien und Punkten im Cyberpunk-Stil an.

//+------------------------------------------------------------------+
//| Draw regression plot cyberpunk                                   |
//+------------------------------------------------------------------+
void drawRegressionPlotCyberpunk()
  {
   //--- Return if no data
   if (!dataLoadedSuccessfully) return;

   //--- Set plot left
   int plotAreaLeft = 60;
   //--- Set plot right
   int plotAreaRight = currentWidthPixels - 40;
   //--- Set plot top
   int plotAreaTop = HEADER_BAR_HEIGHT + 10;
   //--- Set plot bottom
   int plotAreaBottom = currentHeightPixels - 50;

   //--- Set draw left
   int drawAreaLeft = plotAreaLeft + plotPadding;
   //--- Set draw right
   int drawAreaRight = plotAreaRight - plotPadding;
   //--- Set draw top
   int drawAreaTop = plotAreaTop + plotPadding;
   //--- Set draw bottom
   int drawAreaBottom = plotAreaBottom - plotPadding;

   //--- Compute plot width
   int plotWidth = drawAreaRight - drawAreaLeft;
   //--- Compute plot height
   int plotHeight = drawAreaBottom - drawAreaTop;

   //--- Return if invalid
   if (plotWidth <= 0 || plotHeight <= 0) return;

   //--- Init min X
   double minX = primaryClosePrices[0];
   //--- Init max X
   double maxX = primaryClosePrices[0];
   //--- Init min Y
   double minY = secondaryClosePrices[0];
   //--- Init max Y
   double maxY = secondaryClosePrices[0];

   //--- Get data points
   int dataPoints = ArraySize(primaryClosePrices);
   //--- Loop over points
   for (int i = 1; i < dataPoints; i++)
     {
      //--- Update min X
      if (primaryClosePrices[i] < minX) minX = primaryClosePrices[i];
      //--- Update max X
      if (primaryClosePrices[i] > maxX) maxX = primaryClosePrices[i];
      //--- Update min Y
      if (secondaryClosePrices[i] < minY) minY = secondaryClosePrices[i];
      //--- Update max Y
      if (secondaryClosePrices[i] > maxY) maxY = secondaryClosePrices[i];
     }

   //--- Compute range X
   double rangeX = maxX - minX;
   //--- Compute range Y
   double rangeY = maxY - minY;

   //--- Set min range X
   if (rangeX == 0) rangeX = 1;
   //--- Set min range Y
   if (rangeY == 0) rangeY = 1;

   //--- Draw grid
   drawCyberpunkGrid(drawAreaLeft, drawAreaTop, plotWidth, plotHeight);

   //--- Set axis color
   color axisColor = CYBER_PRIMARY_COLOR;
   //--- Get axis ARGB
   uint argbAxis = ColorToARGB(axisColor, 255);
   
   //--- Get glow ARGB
   uint glowColor = ColorToARGB(axisColor, (uchar)(60 * glowIntensity));
   //--- Loop for glow
   for (int g = 1; g <= 2; g++)
     {
      //--- Draw Y glow
      for (int y = plotAreaTop; y <= plotAreaBottom; y++)
        {
         //--- Blend left glow
         blendPixelSet(mainCanvas, plotAreaLeft - g - 1, y, glowColor);
         //--- Blend right glow
         blendPixelSet(mainCanvas, plotAreaLeft + g + 1, y, glowColor);
        }
      //--- Draw X glow
      for (int x = plotAreaLeft; x <= plotAreaRight; x++)
        {
         //--- Blend bottom glow
         blendPixelSet(mainCanvas, x, plotAreaBottom + g + 1, glowColor);
         //--- Blend top glow
         blendPixelSet(mainCanvas, x, plotAreaBottom - g - 1, glowColor);
        }
     }
   
   //--- Loop for thick Y-axis
   for (int thick = 0; thick < 2; thick++)
     {
      //--- Draw Y-axis line
      mainCanvas.Line(plotAreaLeft - thick, plotAreaTop, plotAreaLeft - thick, plotAreaBottom, argbAxis);
     }
   
   //--- Loop for thick X-axis
   for (int thick = 0; thick < 2; thick++)
     {
      //--- Draw X-axis line
      mainCanvas.Line(plotAreaLeft, plotAreaBottom + thick, plotAreaRight, plotAreaBottom + thick, argbAxis);
     }

   //--- Set tick font
   mainCanvas.FontSet("Consolas", axisLabelFontSize);
   //--- Get tick label ARGB
   uint argbTickLabel = ColorToARGB(CYBER_PRIMARY_COLOR, 255);
   
   //--- Declare Y ticks
   double yTickValues[];
   //--- Compute Y ticks
   int numYTicks = calculateOptimalTicks(minY, maxY, plotHeight, yTickValues);
   
   //--- Loop over Y ticks
   for (int i = 0; i < numYTicks; i++)
     {
      //--- Get Y value
      double yValue = yTickValues[i];
      //--- Skip out of range
      if (yValue < minY || yValue > maxY) continue;
      
      //--- Compute Y pos
      int yPos = drawAreaBottom - (int)((yValue - minY) / rangeY * plotHeight);
      
      //--- Draw tick
      mainCanvas.Line(plotAreaLeft - 5, yPos, plotAreaLeft, yPos, argbAxis);
      
      //--- Format label
      string yLabel = formatTickLabel(yValue, rangeY);
      //--- Draw label
      mainCanvas.TextOut(plotAreaLeft - 8, yPos - axisLabelFontSize/2, yLabel, argbTickLabel, TA_RIGHT);
     }

   //--- Declare X ticks
   double xTickValues[];
   //--- Compute X ticks
   int numXTicks = calculateOptimalTicks(minX, maxX, plotWidth, xTickValues);
   
   //--- Loop over X ticks
   for (int i = 0; i < numXTicks; i++)
     {
      //--- Get X value
      double xValue = xTickValues[i];
      //--- Skip out of range
      if (xValue < minX || xValue > maxX) continue;
      
      //--- Compute X pos
      int xPos = drawAreaLeft + (int)((xValue - minX) / rangeX * plotWidth);
      
      //--- Draw tick
      mainCanvas.Line(xPos, plotAreaBottom, xPos, plotAreaBottom + 5, argbAxis);
      
      //--- Format label
      string xLabel = formatTickLabel(xValue, rangeX);
      //--- Draw label
      mainCanvas.TextOut(xPos, plotAreaBottom + 7, xLabel, argbTickLabel, TA_CENTER);
     }

   //--- Compute start Y
   double lineStartY = regressionIntercept + regressionSlope * minX;
   //--- Compute end Y
   double lineEndY = regressionIntercept + regressionSlope * maxX;

   //--- Set start screen X
   int lineStartScreenX = drawAreaLeft;
   //--- Compute start screen Y
   int lineStartScreenY = drawAreaBottom - (int)((lineStartY - minY) / rangeY * plotHeight);
   //--- Set end screen X
   int lineEndScreenX = drawAreaRight;
   //--- Compute end screen Y
   int lineEndScreenY = drawAreaBottom - (int)((lineEndY - minY) / rangeY * plotHeight);

   //--- Get line ARGB
   uint argbLine = ColorToARGB(CYBER_REGRESSION_COLOR, 255);
   //--- Loop for width
   for (int w = 0; w < regressionLineWidth; w++)
     {
      //--- Draw line
      mainCanvas.LineAA(lineStartScreenX, lineStartScreenY + w - regressionLineWidth/2, 
                                  lineEndScreenX, lineEndScreenY + w - regressionLineWidth/2, argbLine);
     }

   //--- Compute pulse phase
   double pulsePhase = (currentAnimationFrame % 60) * M_PI / 30.0;
   //--- Compute pulse factor
   double pulseFactor = (MathSin(pulsePhase) + 1.0) * 0.3 + 0.7;
   //--- Compute point size
   int effectivePointSize = (int)(dataPointSize * pulseFactor);
   
   //--- Loop over points
   for (int i = 0; i < dataPoints; i++)
     {
      //--- Compute screen X
      int screenX = drawAreaLeft + (int)((primaryClosePrices[i] - minX) / rangeX * plotWidth);
      //--- Compute screen Y
      int screenY = drawAreaBottom - (int)((secondaryClosePrices[i] - minY) / rangeY * plotHeight);

      //--- Draw neon point
      drawNeonPoint(screenX, screenY, effectivePointSize, CYBER_DATA_POINTS_COLOR);
     }

   //--- Set axis label font
   mainCanvas.FontSet("Arial Bold", labelFontSize);
   //--- Get axis label ARGB
   uint argbAxisLabel = ColorToARGB(CYBER_SECONDARY_COLOR, 255);

   //--- Set X label
   string xAxisLabel = "► " + primarySymbol + " (X-AXIS)";
   //--- Draw X label
   mainCanvas.TextOut(currentWidthPixels / 2, currentHeightPixels - 20, xAxisLabel, argbAxisLabel, TA_CENTER);

   //--- Set Y label
   string yAxisLabel = "► " + secondarySymbol + " (Y-AXIS)";
   //--- Set vertical angle
   mainCanvas.FontAngleSet(900);
   //--- Draw Y label
   mainCanvas.TextOut(12, currentHeightPixels / 2, yAxisLabel, argbAxisLabel, TA_CENTER);
   //--- Reset angle
   mainCanvas.FontAngleSet(0);
  }

Hier implementieren wir die Funktion drawRegressionPlotCyberpunk, um die Regression im Cyberpunk-Modus zu visualisieren. Dabei wird zunächst geprüft, ob Daten vorhanden sind; sind keine vorhanden, wird die Funktion beendet. Anschließend werden die Plot- und Zeichenbereiche mit Abständen festgelegt, die Breiten und Höhen berechnet und die Funktion beendet, falls die Eingabe ungültig ist. Als Nächstes ermitteln wir die Minimal- und Maximalwerte für X (primär) und Y (sekundär) durch Iteration der Preise, passen die Nullbereiche auf 1 an, rufen drawCyberpunkGrid für das Hintergrundraster auf, legen die Achsen auf Cyber-Primär-ARGB fest, fügen mit blendPixelSet Glühebenen um die Y- und X-Achsen für mehr Tiefe hinzu und zeichnen in Schleifen mit Line verdickte Linien. Zum Beschriften legen wir die Schriftart Consolas mit der Primärfarbe Cyber ARGB fest, berechnen die Y-Ticks und zeichnen in einer Schleife kurze Linien sowie rechtsbündig formatierte Beschriftungen mithilfe von formatTickLabel; analog dazu erfolgt dies für die X-Achse mit zentrierten Beschriftungen am unteren Rand.

Für die Linie berechnen wir die Start- und End-Y-Koordinaten anhand von Regressionsparametern, ordnen diese den Bildschirmpositionen zu, verwenden den ARGB-Wert der Regressionslinie und zeichnen in einer Schleife Anti-Aliasing-Segmente mit LineAA, die durch Anpassung der Halbbreite zentriert werden. Um die Punkte hervorzuheben, berechnen wir aus currentAnimationFrame eine pulsierende Phase mit Sinus, einem Skalierungsfaktor von 0,7–1,0, durchlaufen die Datenpunkte, um ihre Bildschirmpositionen zu berechnen, und rendern sie mit drawNeonPoint in der Farbe der Datenpunkte, um einen animierten Leuchteffekt zu erzielen. Abschließend haben wir für die Beschriftungen die fette Schriftart Arial Bold mit Cyber-Sekundär-ARGB festgelegt, fest codierte Pfeil-Präfixe für den besonderen Effekt hinzugefügt – auch dies haben wir der Einfachheit halber getan –, X unten zentriert, Y mit FontAngleSet um 900 gedreht, um eine vertikale Ausrichtung zu erzielen, und den Winkel zurückgesetzt, womit die thematische Darstellung abgeschlossen ist. Bei der Kompilierung erhalten wir dann etwa folgendes Ergebnis.

CYBERPUNK REGRESSION – DIAGRAMM

Nachdem das erledigt ist, können wir die Bedienfelder und die Legende für dieses Cyberpunk-Thema hinzufügen.

//+------------------------------------------------------------------+
//| Draw glass morphism stats panel                                  |
//+------------------------------------------------------------------+
void drawGlassMorphismStatsPanel()
  {
   //--- Set panel X
   int panelX = statsPanelX;
   //--- Set panel Y
   int panelY = HEADER_BAR_HEIGHT + statsPanelY;
   //--- Set panel width
   int panelWidth = statsPanelWidth;
   //--- Set panel height
   int panelHeight = statsPanelHeight;

   //--- Compute bg color
   color panelBgColor = DarkenColor(CYBER_PRIMARY_COLOR, 0.85);
   //--- Set alpha
   uchar bgAlpha = 120;
   //--- Get panel bg ARGB
   uint argbPanelBg = ColorToARGB(panelBgColor, bgAlpha);
   
   //--- Loop over rows
   for (int y = panelY; y <= panelY + panelHeight; y++)
     {
      //--- Loop over columns
      for (int x = panelX; x <= panelX + panelWidth; x++)
        {
         //--- Blend bg pixel
         blendPixelSet(mainCanvas, x, y, argbPanelBg);
        }
     }
   
   //--- Blend border
   color borderColor = BlendColors(CYBER_PRIMARY_COLOR, CYBER_SECONDARY_COLOR, 0.5);
   //--- Get border ARGB
   uint argbBorder = ColorToARGB(borderColor, 255);
   
   //--- Get glow ARGB
   uint glowColor = ColorToARGB(borderColor, (uchar)(60 * glowIntensity));
   //--- Loop for glow
   for (int g = 1; g <= 2; g++)
     {
      //--- Draw top glow
      for (int x = panelX - g; x <= panelX + panelWidth + g; x++)
        {
         //--- Blend pixel
         blendPixelSet(mainCanvas, x, panelY - g, glowColor);
        }
      //--- Draw bottom glow
      for (int x = panelX - g; x <= panelX + panelWidth + g; x++)
        {
         //--- Blend pixel
         blendPixelSet(mainCanvas, x, panelY + panelHeight + g, glowColor);
        }
      //--- Draw left glow
      for (int y = panelY - g; y <= panelY + panelHeight + g; y++)
        {
         //--- Blend pixel
         blendPixelSet(mainCanvas, panelX - g, y, glowColor);
        }
      //--- Draw right glow
      for (int y = panelY - g; y <= panelY + panelHeight + g; y++)
        {
         //--- Blend pixel
         blendPixelSet(mainCanvas, panelX + panelWidth + g, y, glowColor);
        }
     }
   
   //--- Draw top border
   for (int x = panelX; x <= panelX + panelWidth; x++)
     {
      //--- Blend border pixel
      blendPixelSet(mainCanvas, x, panelY, argbBorder);
      //--- Blend bottom border
      blendPixelSet(mainCanvas, x, panelY + panelHeight, argbBorder);
     }
   //--- Draw left and right borders
   for (int y = panelY; y <= panelY + panelHeight; y++)
     {
      //--- Blend left
      blendPixelSet(mainCanvas, panelX, y, argbBorder);
      //--- Blend right
      blendPixelSet(mainCanvas, panelX + panelWidth, y, argbBorder);
     }

   //--- Set stats font
   mainCanvas.FontSet("Consolas", panelFontSize);
   //--- Get text ARGB
   uint argbText = ColorToARGB(CYBER_PRIMARY_COLOR, 255);

   //--- Set text Y
   int textY = panelY + 8;
   //--- Set line spacing
   int lineSpacing = panelFontSize;

   //--- Format equation
   string equationText = StringFormat("Y = %.3f + %.3f * X", regressionIntercept, regressionSlope);
   //--- Draw equation
   mainCanvas.TextOut(panelX + 8, textY, equationText, argbText, TA_LEFT);
   //--- Update Y
   textY += lineSpacing;

   //--- Format correlation
   string correlationText = StringFormat("Correlation: %.4f", correlationCoefficient);
   //--- Draw correlation
   mainCanvas.TextOut(panelX + 8, textY, correlationText, argbText, TA_LEFT);
   //--- Update Y
   textY += lineSpacing;

   //--- Format R-squared
   string rSquaredText = StringFormat("R-Squared: %.4f", rSquared);
   //--- Draw R-squared
   mainCanvas.TextOut(panelX + 8, textY, rSquaredText, argbText, TA_LEFT);
   //--- Update Y
   textY += lineSpacing;

   //--- Format points
   string dataPointsText = StringFormat("Points: %d", ArraySize(primaryClosePrices));
   //--- Draw points
   mainCanvas.TextOut(panelX + 8, textY, dataPointsText, argbText, TA_LEFT);
  }

//+------------------------------------------------------------------+
//| Draw glass morphism legend                                       |
//+------------------------------------------------------------------+
void drawGlassMorphismLegend()
  {
   //--- Set legend X
   int legendX = statsPanelX;
   //--- Set legend Y
   int legendY = HEADER_BAR_HEIGHT + statsPanelY + statsPanelHeight;
   //--- Set legend width
   int legendWidth = statsPanelWidth;
   //--- Set legend height
   int legendHeightThis = legendHeight;

   //--- Compute bg color
   color legendBgColor = DarkenColor(CYBER_SECONDARY_COLOR, 0.85);
   //--- Set alpha
   uchar bgAlpha = 120;
   //--- Get legend bg ARGB
   uint argbLegendBg = ColorToARGB(legendBgColor, bgAlpha);
   
   //--- Loop over rows
   for (int y = legendY; y <= legendY + legendHeightThis; y++)
     {
      //--- Loop over columns
      for (int x = legendX; x <= legendX + legendWidth; x++)
        {
         //--- Blend bg pixel
         blendPixelSet(mainCanvas, x, y, argbLegendBg);
        }
     }
   
   //--- Blend border
   color borderColor = BlendColors(CYBER_SECONDARY_COLOR, CYBER_PRIMARY_COLOR, 0.5);
   //--- Get border ARGB
   uint argbBorder = ColorToARGB(borderColor, 255);
   
   //--- Get glow ARGB
   uint glowColor = ColorToARGB(borderColor, (uchar)(60 * glowIntensity));
   //--- Loop for glow
   for (int g = 1; g <= 2; g++)
     {
      //--- Draw top and bottom glow
      for (int x = legendX - g; x <= legendX + legendWidth + g; x++)
        {
         //--- Blend top
         blendPixelSet(mainCanvas, x, legendY - g, glowColor);
         //--- Blend bottom
         blendPixelSet(mainCanvas, x, legendY + legendHeightThis + g, glowColor);
        }
      //--- Draw left and right glow
      for (int y = legendY - g; y <= legendY + legendHeightThis + g; y++)
        {
         //--- Blend left
         blendPixelSet(mainCanvas, legendX - g, y, glowColor);
         //--- Blend right
         blendPixelSet(mainCanvas, legendX + legendWidth + g, y, glowColor);
        }
     }
   
   //--- Draw top and bottom borders
   for (int x = legendX; x <= legendX + legendWidth; x++)
     {
      //--- Blend top
      blendPixelSet(mainCanvas, x, legendY, argbBorder);
      //--- Blend bottom
      blendPixelSet(mainCanvas, x, legendY + legendHeightThis, argbBorder);
     }
   //--- Draw left and right borders
   for (int y = legendY; y <= legendY + legendHeightThis; y++)
     {
      //--- Blend left
      blendPixelSet(mainCanvas, legendX, y, argbBorder);
      //--- Blend right
      blendPixelSet(mainCanvas, legendX + legendWidth, y, argbBorder);
     }

   //--- Set legend font
   mainCanvas.FontSet("Consolas", panelFontSize);
   //--- Get text ARGB
   uint argbText = ColorToARGB(CYBER_SECONDARY_COLOR, 255);

   //--- Set item Y
   int itemY = legendY + 10;
   //--- Set line spacing
   int lineSpacing = panelFontSize;

   //--- Draw neon point
   drawNeonPoint(legendX + 12, itemY, dataPointSize, CYBER_DATA_POINTS_COLOR);
   //--- Draw data label
   mainCanvas.TextOut(legendX + 22, itemY - 4, "Data Points", argbText, TA_LEFT);
   //--- Update Y
   itemY += lineSpacing;

   //--- Get line color
   uint argbLineColor = ColorToARGB(CYBER_REGRESSION_COLOR, 255);
   //--- Loop to draw line
   for (int i = 0; i < 15; i++)
     {
      //--- Blend line pixel
      blendPixelSet(mainCanvas, legendX + 7 + i, itemY, argbLineColor);
      //--- Blend below pixel
      blendPixelSet(mainCanvas, legendX + 7 + i, itemY + 1, argbLineColor);
     }
   //--- Draw line label
   mainCanvas.TextOut(legendX + 27, itemY - 4, "Regression Line", argbText, TA_LEFT);
  }

Zunächst implementieren wir die Funktion drawGlassMorphismStatsPanel, um ein halbtransparentes, leuchtendes Statistik-Overlay für das Cyberpunk-Thema zu erstellen. Dabei positionieren wir es anhand der Eingaben mit einem Versatz zur Kopfzeile, verdunkeln die Cyber-Primärfarbe mit DarkenColor für den Hintergrund, legen eine niedrige Deckkraft von 120 fest und den Bereich Pixel für Pixel mithilfe von blendPixelSet überblenden, um einen Milchglaseffekt zu erzielen. Als Nächstes mischen wir die Rahmenfarben aus den Cyber-Primär- und -Sekundärfarben mit BlendColors, bereiten ARGB vor, fügen äußere Leuchteffekte hinzu, indem wir Schleifen um die Rahmen des Panels mit abnehmender Intensität erweitern, und zeichnen dann die oberen, unteren, linken und rechten Rahmen durch das Überblenden von Linien. Wir legen mit FontSet die Schriftart Consolas fest, verwenden Cyber Primary ARGB für den Text, initialisieren Y mit Abständen und Zwischenräumen entsprechend der Schriftgröße, formatieren und zeichnen die Gleichung, die Korrelation, den R-Quadrat-Wert und die Anzahl der Punkte mit StringFormat und TextOut linksbündig und aktualisieren Y in jeder Zeile, um eine vertikale Stapelung zu erzielen.

Ähnlich verfahren wir bei drawGlassMorphismLegend: Wir positionieren die Statistiken mit passender Breite darunter, verdunkeln die Cyber-Sekundärfarbe für den Hintergrund, mischen die Füllfarbe, wenden eine identische Randüberblendung an und fügen eine Leuchteffekterweiterung hinzu. Wir zeichnen die oberen, unteren, linken und rechten Rahmen, legen die Schriftart Consolas mit sekundärem ARGB-Text in der Farbe Cyber fest, zeichnen mithilfe von drawNeonPoint ein Neonpunkt-Symbol an der angepassten Position in der Farbe Cyber, fügen die Beschriftung Datenpunkte hinzu und aktualisieren Y; anschließend fügen wir ein kurzes horizontales Liniensegment (15 Pixel breit, 2 Pixel hoch) in der Farbe Cyber-Regression ein und beschriften es mit Regressionsgerade als visuelle Erläuterung. Für die Neon-Anzeige zur Größenanpassung verfolgen wir den folgenden Ansatz.

Zeichnen eines Neon-Indikators für die Größenänderung

Um die Größenänderung zu erleichtern, zeigen wir hervorgehobene Griffpunkte mit einem Leuchteffekt an.

//+------------------------------------------------------------------+
//| Draw neon resize indicator                                       |
//+------------------------------------------------------------------+
void drawNeonResizeIndicator()
  {
   //--- Blend indicator color
   color indicatorColor = BlendColors(CYBER_PRIMARY_COLOR, CYBER_SECONDARY_COLOR, 0.5);
   //--- Get indicator ARGB
   uint argbIndicator = ColorToARGB(indicatorColor, 200);
   
   //--- Get glow ARGB
   uint glowColor = ColorToARGB(indicatorColor, (uchar)(100 * glowIntensity));
   
   //--- Check corner
   if (hoverResizeMode == RESIZE_CORNER || activeResizeMode == RESIZE_CORNER)
     {
      //--- Compute corner X
      int cornerX = currentWidthPixels - resizeGripSize;
      //--- Compute corner Y
      int cornerY = currentHeightPixels - resizeGripSize;
      
      //--- Loop for glow
      for (int g = 1; g <= 3; g++)
        {
         //--- Loop Y glow
         for (int y = cornerY - g; y < currentHeightPixels + g; y++)
           {
            //--- Loop X glow
            for (int x = cornerX - g; x < currentWidthPixels + g; x++)
              {
               //--- Blend glow
               blendPixelSet(mainCanvas, x, y, glowColor);
              }
           }
        }
      
      //--- Fill corner
      mainCanvas.FillRectangle(cornerX, cornerY, currentWidthPixels - 1, currentHeightPixels - 1, argbIndicator);
      
      //--- Set line color
      uint lineColor = ColorToARGB(C'255,255,255', 255);
      //--- Loop for lines
      for (int i = 0; i < 4; i++)
        {
         //--- Compute offset
         int offset = i * 3;
         //--- Draw diagonal
         mainCanvas.Line(cornerX + offset, currentHeightPixels - 1, 
                                  currentWidthPixels - 1, cornerY + offset, lineColor);
        }
     }
   
   //--- Check right
   if (hoverResizeMode == RESIZE_RIGHT_EDGE || activeResizeMode == RESIZE_RIGHT_EDGE)
     {
      //--- Compute indicator Y
      int indicatorY = currentHeightPixels / 2 - 15;
      //--- Loop for glow
      for (int g = 1; g <= 3; g++)
        {
         //--- Fill glow
         mainCanvas.FillRectangle(currentWidthPixels - 3 - g, indicatorY - g, 
                                          currentWidthPixels - 1 + g, indicatorY + 30 + g, glowColor);
        }
      //--- Fill indicator
      mainCanvas.FillRectangle(currentWidthPixels - 3, indicatorY, 
                                       currentWidthPixels - 1, indicatorY + 30, argbIndicator);
     }
   
   //--- Check bottom
   if (hoverResizeMode == RESIZE_BOTTOM_EDGE || activeResizeMode == RESIZE_BOTTOM_EDGE)
     {
      //--- Compute indicator X
      int indicatorX = currentWidthPixels / 2 - 15;
      //--- Loop for glow
      for (int g = 1; g <= 3; g++)
        {
         //--- Fill glow
         mainCanvas.FillRectangle(indicatorX - g, currentHeightPixels - 3 - g, 
           indicatorX + 30 + g, currentHeightPixels - 1 + g, glowColor);
        }
      //--- Fill indicator
      mainCanvas.FillRectangle(indicatorX, currentHeightPixels - 3, 
        indicatorX + 30, currentHeightPixels - 1, argbIndicator);
     }
  }

Wir implementieren die Funktion drawNeonResizeIndicator, um im Cyberpunk-Modus visuelle Hinweise für die Größenänderung zu geben. Dazu mischen wir zunächst die Cyber-Primär- und -Sekundärfarben mit BlendColors für den Indikator, wandeln das Ergebnis in ARGB mit einem Alpha-Wert von 200 um und erstellen eine Leuchtvariante mit einem auf das 100-Fache skalierten Alpha-Wert. Für die Größenänderung der Ecken (beim Hover oder im aktiven Zustand) berechnen wir die Position unten rechts anhand der Griffgröße, fügen durch eine Schleife ein mehrschichtiges Leuchten hinzu, um die erweiterten Bereiche um die Ecke herum mit blendPixelSet auszufüllen, füllen dann das Griffquadrat mit FillRectangle aus und zeichnen mit Line vier versetzte diagonale Linien in reinem Weiß (ARGB), um einen Streifeneffekt zu erzielen.

Als Nächstes zentrieren wir für den rechten Rand einen vertikalen Indikator Y, fügen einen Leuchteffekt hinzu, indem wir in Schleifen leicht verbreiterte und versetzte Rechtecke füllen, gefolgt von der Hauptfüllung. Verfahren Sie ebenso mit der unteren Kante: Setzen Sie die horizontale Markierung X in die Mitte, fügen Sie Leuchtfüllungen hinzu und vervollständigen Sie das Ganze mit dem Hauptmarkierungsrechteck. Dieser Neon-Themenmodus mit Leuchteffekten sorgt für mehr Interaktivität und lässt die Bereiche zur Größenanpassung im dunklen Cyberpunk-Thema durch mehrschichtige Überblendungen, die für Tiefe sorgen, besonders hervorstechen. Wir können unsere Diagramme nun mithilfe der folgenden Logik einheitlich aufrufen und erstellen.

//+------------------------------------------------------------------+
//| Render visualization                                             |
//+------------------------------------------------------------------+
void renderVisualization()
  {
   //--- Check cyberpunk mode
   if (isCyberpunkThemeEnabled)
     {
      //--- Render cyberpunk
      renderCyberpunkTheme();
     }
   else
     {
      //--- Render standard
      renderStandardTheme();
     }
  }

//+------------------------------------------------------------------+
//| Render standard theme                                            |
//+------------------------------------------------------------------+
void renderStandardTheme()
  {
   //--- Erase canvas
   mainCanvas.Erase(0);

   //--- Check background fill
   if (enableBackgroundFill)
     {
      //--- Draw gradient
      drawGradientBackground();
     }

   //--- Draw border
   drawCanvasBorder();
   //--- Draw header
   drawHeaderBar();
   //--- Draw toggle button
   drawThemeToggleButton();
   //--- Draw plot
   drawRegressionPlot();

   //--- Check show stats
   if (showStatistics)
     {
      //--- Draw stats panel
      drawStatisticsPanel();
      //--- Draw legend
      drawLegend();
     }

   //--- Check resize hover
   if (isHoveringResizeZone && enableResizing)
     {
      //--- Draw indicator
      drawResizeIndicator();
     }

   //--- Update canvas
   mainCanvas.Update();
  }

//+------------------------------------------------------------------+
//| Render cyberpunk theme                                           |
//+------------------------------------------------------------------+
void renderCyberpunkTheme()
  {
   //--- Erase canvas
   mainCanvas.Erase(0);

   //--- Draw cyber background
   drawCyberpunkBackground();
   //--- Draw holographic border
   drawHolographicBorder();
   //--- Draw futuristic header
   drawFuturisticHeader();
   //--- Draw toggle button
   drawThemeToggleButton();
   //--- Draw cyber plot
   drawRegressionPlotCyberpunk();

   //--- Check show stats
   if (showStatistics)
     {
      //--- Draw glass stats
      drawGlassMorphismStatsPanel();
      //--- Draw glass legend
      drawGlassMorphismLegend();
     }

   //--- Check resize hover
   if (isHoveringResizeZone && enableResizing)
     {
      //--- Draw neon indicator
      drawNeonResizeIndicator();
     }

   //--- Update canvas
   mainCanvas.Update();
  }

Hier implementieren wir die Funktion renderVisualization, um das Diagramm in Abhängigkeit vom Theme-Modus darzustellen. Dabei prüfen wir isCyberpunkThemeEnabled, um für futuristische Effekte renderCyberpunkTheme oder für die klassische Darstellung renderStandardTheme aufzurufen, was einen nahtlosen Wechsel ohne überflüssigen Code ermöglicht. In renderStandardTheme löschen wir die Zeichenfläche mit Erase, zeichnen optional einen Farbverlaufshintergrund, sofern dieser aktiviert ist, fügen Rahmen, die Kopfzeile, die Schaltfläche zum Umschalten des Themes, das Hauptdiagramm zur Regression hinzu und, falls showStatistics auf true gesetzt ist, das Statistikfeld und die Legende; bei Mauszeiger-Überfahrt wird die Größe angepasst und die Funktion aktiviert, ein Indikator hinzugefügt und anschließend mit der Methode Update aktualisiert.

Ebenso löscht renderCyberpunkTheme alle Elemente, zeichnet einen Cyber-Hintergrund, einen holografischen Rahmen, eine futuristische Kopfzeile, eine Umschalt-Schaltfläche, ein Cyber-Diagramm, ein Statistikpanel im Glassmorphism-Stil und eine Legende (sofern angezeigt) sowie gegebenenfalls eine Neon-Größenanpassungsanzeige und führt anschließend eine Aktualisierung durch, wodurch durch spezielle Funktionen thematische Konsistenz gewährleistet wird. Dadurch wird die Visualisierung nun je nach ausgewähltem Themenmodus bedingt dargestellt, allerdings müssen wir noch einen Timer für die Animationen einrichten. Animationen basieren auf einer Timer-Einstellung, die die Bilder in regelmäßigen Abständen aktualisiert, wodurch die Effekte im Cyberpunk-Modus flüssig bleiben.

Timer für Animationen einrichten

//+------------------------------------------------------------------+
//| Initialize expert                                                |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Render visualization
   renderVisualization();

   //--- Enable mouse events
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
   //--- Redraw chart
   ChartRedraw();
   
   //--- Set timer for animations
   EventSetMillisecondTimer(50);

   //--- Return success
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Deinitialize expert                                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- Kill timer
   EventKillTimer();
   //--- Destroy canvas
   mainCanvas.Destroy();
   //--- Redraw chart
   ChartRedraw();
  }

//+------------------------------------------------------------------+
//| Handle timer                                                     |
//+------------------------------------------------------------------+
void OnTimer()
  {
   //--- Check cyberpunk enabled
   if (isCyberpunkThemeEnabled)
     {
      //--- Increment frame
      currentAnimationFrame++;
      //--- Reset frame
      if (currentAnimationFrame > 360) currentAnimationFrame = 0;
      //--- Render
      renderVisualization();
      //--- Redraw chart
      ChartRedraw();
     }
  }

Wir beginnen in OnInit damit, die bisherige Logik um die Unterstützung von Animationen zu erweitern, rufen renderVisualization für die anfängliche Darstellung auf, aktivieren Mausbewegungsereignisse, zeichnen das Diagramm neu und setzen einen 50-Millisekunden-Timer mithilfe von EventSetMillisecondTimer, um regelmäßige Aktualisierungen zu steuern, bevor wir INIT_SUCCEEDED zurückgeben. Anschließend beenden wir in OnDeinit den Timer mit EventKillTimer, um Animationen zu stoppen, die Canvas zu löschen und das Diagramm zur Bereinigung neu zu zeichnen.

Zur Steuerung der Animationen implementieren wir OnTimer: Dabei prüfen wir, ob der Cyberpunk-Modus aktiviert ist, erhöhen den Wert von currentAnimationFrame und setzen ihn zur Wiederholung auf 360 zurück. Anschließend rendern wir die Visualisierung mit renderVisualization neu und zeichnen das Diagramm neu, um framebasierte Änderungen wie Impulse oder Leuchteffekte anzuwenden. Das ist alles. Denken Sie abschließend daran, dass wir in der Kopfzeile eine zusätzliche Schaltfläche hinzugefügt haben, die wir beim Überfahren mit der Maus erkennen müssen. Wir werden die Logik entsprechend erweitern, um sie in den Ereignis-Handler des Diagramms einzubinden.

Umgang mit dem Themenumschalter bei Ereignissen

Um auf Klicks zu reagieren, erweitern wir den Ereignis-Handler für die Schaltflächenerkennung.

//+------------------------------------------------------------------+
//| Handle chart event                                               |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   //--- Check mouse move
   if (id == CHARTEVENT_MOUSE_MOVE)
     {
      //--- Set mouse X
      int mouseX = (int)lparam;
      //--- Set mouse Y
      int mouseY = (int)dparam;
      //--- Set mouse state
      int mouseState = (int)sparam;

      //--- Store prev canvas hover
      bool previousHoverState = isHoveringCanvas;
      //--- Store prev header hover
      bool previousHeaderHoverState = isHoveringHeader;
      //--- Store prev resize hover
      bool previousResizeHoverState = isHoveringResizeZone;
      //--- Store prev theme hover
      bool previousThemeButtonHover = isHoveringThemeButton;

      //--- Update canvas hover
      isHoveringCanvas = (mouseX >= currentPositionX && mouseX <= currentPositionX + currentWidthPixels &&
        mouseY >= currentPositionY && mouseY <= currentPositionY + currentHeightPixels);

      //--- Update header hover
      isHoveringHeader = isMouseOverHeaderBar(mouseX, mouseY);
      //--- Update theme hover
      isHoveringThemeButton = isMouseOverThemeButton(mouseX, mouseY);
      //--- Update resize hover
      isHoveringResizeZone = isMouseInResizeZone(mouseX, mouseY, hoverResizeMode);

      //--- Check if redraw needed
      bool needRedraw = (previousHoverState != isHoveringCanvas || 
        previousHeaderHoverState != isHoveringHeader ||
        previousResizeHoverState != isHoveringResizeZone ||
        previousThemeButtonHover != isHoveringThemeButton);

      //--- Check button press
      if (mouseState == 1 && previousMouseButtonState == 0)
        {
         //--- Check theme button
         if (isHoveringThemeButton)
           {
            //--- Toggle theme
            isCyberpunkThemeEnabled = !isCyberpunkThemeEnabled;
            //--- Render
            renderVisualization();
            //--- Redraw chart
            ChartRedraw();
           }
         //--- Check drag start
         else if (enableDragging && isHoveringHeader && !isHoveringResizeZone)
           {
            //--- Set dragging
            isDraggingCanvas = true;
            //--- Set start X
            dragStartX = mouseX;
            //--- Set start Y
            dragStartY = mouseY;
            //--- Set canvas X
            canvasStartX = currentPositionX;
            //--- Set canvas Y
            canvasStartY = currentPositionY;
            //--- Disable scroll
            ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
            //--- Set redraw
            needRedraw = true;
           }
         //--- Check resize start
         else if (isHoveringResizeZone)
           {
            //--- Set resizing
            isResizingCanvas = true;
            //--- Set active mode
            activeResizeMode = hoverResizeMode;
            //--- Set start X
            resizeStartX = mouseX;
            //--- Set start Y
            resizeStartY = mouseY;
            //--- Set initial width
            resizeInitialWidth = currentWidthPixels;
            //--- Set initial height
            resizeInitialHeight = currentHeightPixels;
            //--- Disable scroll
            ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
            //--- Set redraw
            needRedraw = true;
           }
        } 
      //--- Check drag
      else if (mouseState == 1 && previousMouseButtonState == 1)
        {
         //--- Handle drag
         if (isDraggingCanvas)
           {
            //--- Handle drag
            handleCanvasDrag(mouseX, mouseY);
           }
         //--- Handle resize
         else if (isResizingCanvas)
           {
            //--- Handle resize
            handleCanvasResize(mouseX, mouseY);
           }
        } 
      //--- Check button release
      else if (mouseState == 0 && previousMouseButtonState == 1)
        {
         //--- Check active
         if (isDraggingCanvas || isResizingCanvas)
           {
            //--- Reset dragging
            isDraggingCanvas = false;
            //--- Reset resizing
            isResizingCanvas = false;
            //--- Reset mode
            activeResizeMode = NO_RESIZE;
            //--- Enable scroll
            ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
            //--- Set redraw
            needRedraw = true;
           }
        }

      //--- Check redraw
      if (needRedraw)
        {
         //--- Render
         renderVisualization();
         //--- Redraw chart
         ChartRedraw();
        }

      //--- Update last X
      lastMouseX = mouseX;
      //--- Update last Y
      lastMouseY = mouseY;
      //--- Update prev state
      previousMouseButtonState = mouseState;
     }
  }

Um das Umschalten der Themenmodus-Schaltfläche zu handhaben, erweitern wir die Ereignisbehandlung von OnChartEvent, um diese Funktion neben den bestehenden Interaktionen zu integrieren. Dabei überprüfen wir zunächst CHARTEVENT_MOUSE_MOVE, extrahieren die Mauskoordinaten und den Mausstatus, speichern frühere Hover-Zustände einschließlich der neuen Themen-Schaltfläche und aktualisieren die Flags für die Zeichenfläche sowie die Kopfzeile mit isMouseOverHeaderBar, die Themen-Schaltfläche über isMouseOverThemeButton und den Größenänderungsbereich über isMouseInResizeZone. Anschließend wird ermittelt, ob aufgrund einer Änderung des Mauszeigers eine Neuzeichnung erforderlich ist.

Beim Herunterdrücken der Maustaste (Zustand 1, zuvor 0) prüfen wir zunächst, ob der Mauszeiger über der Themen-Schaltfläche schwebt, und schalten isCyberpunkThemeEnabled um, führen mit renderVisualization eine Neu-Rendering durch und zeichnen das Diagramm neu; andernfalls wird das Ziehen (sofern aktiviert) und das Bewegen des Mauszeigers über die Kopfzeile ohne Größenänderung durch Setzen von Flags, Erfassen von Startpunkten, Deaktivieren des Scrollens und Markieren der Neuzeichnung behandelt; oder die Größenänderung wird initiiert, wenn sich der Mauszeiger in der Zone befindet, indem der Modus und die Anfangswerte gesetzt sowie das Scrollen deaktiviert werden. Solange diese Funktion aufgerufen ist (Zustand 1, prev 1), rufen wir handleCanvasDrag für das Ziehen oder handleCanvasResize für die Größenänderung auf. Beim Loslassen (Zustand 0, vorher 1) werden Flags und Modus zurückgesetzt, das Scrollen aktiviert und das Flag Neuzeichnen gesetzt. Falls eine Neuzeichnung erforderlich ist, rufen Sie die Funktionen renderVisualization und ChartRedraw auf. Abschließend aktualisieren wir die letzten Mauspositionen und den vorherigen Zustand, um die Kontinuität zu gewährleisten. Wir haben die spezifischen Änderungen zur Verdeutlichung hervorgehoben. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

CYBERPUNK-DIAGRAMM

Aus der Visualisierung geht hervor, dass wir das Regressionsdiagramm durch die Einbindung eines Cyberpunk-Themas aufgewertet und damit unsere Ziele erreicht haben. Nun bleibt nur noch, die Funktionsfähigkeit des Systems zu testen, was im folgenden Abschnitt behandelt wird.


Backtests

Wir haben die Tests durchgeführt, und hier ist die kompilierte Visualisierung in einem einzigen Graphics Interchange Format (GIF) Bitmap-Bildformat.

BACKTEST-GIF

Während der Tests konnte ohne Verzögerung nahtlos zwischen den Themenmodi umgeschaltet werden und ohne Verzögerung zwischen den Modi. Neon-Leuchteffekte heben Datenpunkte hervor, um Ausreißer besser erkennen zu können, und Animationen pulsierten synchron mit den Bildaktualisierungen, wodurch eine flüssige Leistung gewährleistet wurde.


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir das Charting-Tool Regression in MQL5 um einen Cyberpunk-Modus mit Neon-Leuchteffekten, Animationen und holografischen Effekten erweitert haben, um eine eindrucksvolle Visualisierung zu ermöglichen. Wir haben die Umschaltung zwischen Themenmodi, dynamische Hintergründe mit Sternen, leuchtende Rahmen sowie Neonpunkte und -linien integriert und dabei die Kompatibilität mit dem Standardmodus gewahrt. Dieses System mit zwei Themenmodi wertet die Analyse von Symbolpaaren durch eine futuristische Ästhetik auf und unterstützt Echtzeitaktualisierungen sowie Interaktionen für aufschlussreiche Handelserkenntnisse. Nach der Lektüre dieses Artikels werden Sie in der Lage sein:

  • Themen in Diagrammen umschalten, um die Sichtbarkeit an unterschiedliche Lichtverhältnisse anzupassen
  • Verwenden Sie Neonmarkierungen, um Abweichungen von Korrelationen schnell zu erkennen
  • Verwenden Sie pulsierende Linien als visuelle Hinweise auf die Stärke des Trends bei Pair-Trades

In den folgenden Teilen werden wir uns mit der grafischen Darstellung von statistischen Verteilungsmodellen sowie deren Darstellung in Balkendiagrammen befassen. Bleiben Sie dran!

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/21306

Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Vom Anfänger zum Experten: Automatisierung von Intraday-Strategien Vom Anfänger zum Experten: Automatisierung von Intraday-Strategien
Wir setzen das Konzept des EMA-50-Retests in einen verhaltensgesteuerten Expert Advisor für den Intraday-Handel um. Die Studie formalisiert Trendrichtung, EMA-Interaktionen (Pierce-and-Close), Reaktionsbestätigungen und optionale Filter und implementiert diese anschließend in MQL5 mithilfe modularer Funktionen und ressourcensicherer Handles. Durch visuelle Tests im Strategy Tester wird die Richtigkeit der Signale überprüft. Das Ergebnis ist eine übersichtliche Vorlage für die Codierung diskretionär gehandelter Abpraller.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Automatisierung des Indikators Market Memory Zones: Bereiche, zu denen der Kurs voraussichtlich zurückkehrt Automatisierung des Indikators Market Memory Zones: Bereiche, zu denen der Kurs voraussichtlich zurückkehrt
Dieser Artikel verwandelt Market Memory Zones von einem reinen Chart-Konzept in einen vollwertigen MQL5-Expert Advisor. Das System automatisiert die Erkennung von Displacement-Zonen, Strukturübergängen (CHoCH) und Liquidity Sweep-Zonen mithilfe von ATR- und Kerzenstruktur-Filtern, wendet eine Bestätigung auf einem niedrigeren Zeitrahmen an und sorgt für eine risikobasierte Positionsgrößenbestimmung mit dynamischen Stop-Loss- und strukturbasierten Take-Profit-Zielen. Sie erhalten die Code-Architektur für die Erkennung, die Einstiegspunkte, das Handelsmanagement und die Visualisierung sowie eine kurze Auswertung über den Backtest.