MQL5-Werkzeuge für den Handel (Teil 12): Erweiterung des Korrelationsmatrix-Dashboards um interaktive Funktionen
Einführung
In unserem vorherigen Artikel (Teil 11) haben wir ein Dashboard für eine Korrelationsmatrix in MetaQuotes Language 5 (MQL5) entwickelt, das die Beziehungen zwischen den Vermögenswerten mithilfe der Methoden Pearson, Spearman und Kendall über einen konfigurierbaren Zeitrahmen und eine konfigurierbare Anzahl von Balken berechnet. In Teil 12 erweitern wir das Dashboard der Korrelationsmatrix um Interaktivität. Mit dieser Erweiterung werden Funktionen wie das Ziehen und Minimieren von Panels über Mausereignisse, Hover-Effekte für visuelles Feedback, die Sortierung von Symbolen nach Korrelationsstärke in auf- und absteigenden Modi, das Umschalten zwischen Korrelation und dem p-Wert, der Wechsel zwischen hellem und dunklem Farbschema mit dynamischen Farbaktualisierungen und Tooltips zu den Zellen für detaillierte Informationen eingeführt. Wir werden die folgenden Themen behandeln:
- Verstehen der Erweiterungen der interaktiven Korrelationsmatrix
- Implementation in MQL5
- Backtests
- Schlussfolgerung
Am Ende werden Sie ein interaktives MQL5-Korrelationsmatrix-Dashboard mit verbesserter Nutzerfreundlichkeit verwenden, das Sie sofort anpassen können – legen wir los!
Verstehen der Erweiterungen der interaktiven Korrelationsmatrix
Die interaktive Korrelationsmatrix baut auf dem Basis-Dashboard auf, indem sie nutzerfreundliche Funktionen einführt, die eine dynamische Manipulation und Anpassung ermöglichen, sodass wir die Beziehungen zwischen Vermögenswerten in Echtzeit analysieren können, ohne den Arbeitsablauf zu unterbrechen. Zu den wichtigsten Neuerungen gehören die Verarbeitung von Mausereignissen für das Ziehen von Feldern, um das Dashboard auf dem Chart neu zu positionieren, das Minimieren/Maximieren, um zwischen einer kompakten Kopfansicht und der Vollanzeige umzuschalten, Schwebeeffekte auf Schaltflächen und Zeitrahmenzellen für visuelles Feedback, wie Farbänderungen, und Klickreaktionen, um zwischen Modi oder Ansichten zu wechseln.
Wir haben auch die Sortierung von Symbolen nach der durchschnittlichen absoluten Korrelationsstärke in ursprünglicher, absteigender oder aufsteigender Reihenfolge integriert, um hoch korrelierte Vermögenswerte zu gruppieren, ebenso wie das Umschalten zwischen der Korrelation und dem p-Wert für tiefere statistische Einblicke, den Wechsel zwischen hellem und dunklem Farbschema, um sich den Nutzerpräferenzen mit entsprechenden Farben anzupassen, und Tooltips zu den Zellen, die Details wie Symbole, Zeitrahmen, Methode, Korrelation, p-Wert und verwendete Balken liefern.
Wir planen, die Eingaben und Enumerationen für neue Modi wie das Umschalten der Ansicht zu erweitern, Globals für Interaktionszustände hinzuzufügen, das Parsing zu modifizieren, um Originalsymbole zu speichern, eine Sortierlogik auf der Grundlage von Durchschnittsstärken mit Indexumordnung zu implementieren, die Erstellungs- und Positionsfunktionen zu aktualisieren, um die Minimierung und das Ziehen zu unterstützen, die Farben in Farbschemenumschaltungen dynamisch anzupassen, die Dashboard-Aktualisierungen der Darstellung der p-Werte und Tooltips zu verbessern und alle Interaktionen in einem Event-Handler für nahtlose Nutzerfreundlichkeit zu behandeln. Kurz gesagt: Hier ist eine visuelle Darstellung unserer Ziele.

Implementation in MQL5
Um das Programm in MQL5 zu verbessern, müssen wir einige neue Eingabeparameter zur Steuerung des Multi-Dashboard-Kontrollmechanismus und einige zusätzliche Globals definieren.
input color ColorLightThemeBg = clrWhite; // Light theme background input color ColorLightThemeText = clrBlack; // Light theme text enum ViewMode { VIEW_CORR, // Show correlations VIEW_PVAL // Show p-values }; #define SORT_BUTTON "BUTTON_SORT" // Define sort button identifier #define THEME_BUTTON "BUTTON_THEME" // Define theme toggle button identifier #define COLOR_LIGHT_GRAY C'230,230,230' // Define light gray for neutral cells #define COLOR_DARK_GRAY C'105,105,105' // Define dark gray for headers #define BUTTON_HOVER_SIZE 24 // Define hover size for buttons (centered around anchor) #define NUM_LEGEND_ITEMS 15 // Define increased to cover max possible (e.g., 11 in heatmap) bool panel_is_visible = true; //--- Set panel visibility flag bool panel_minimized = false; //--- Set minimized state flag bool panel_dragging = false; //--- Set dragging flag int panel_drag_x = 0, panel_drag_y = 0; //--- Initialize drag start mouse coordinates int panel_start_x = 0, panel_start_y = 0; //--- Initialize drag start panel coordinates int prev_mouse_state = 0; //--- Initialize previous mouse state bool prev_header_hovered = false; //--- Set previous header hover state bool prev_toggle_hovered = false; //--- Set previous toggle hover state bool prev_close_hovered = false; //--- Set previous close hover state bool prev_heatmap_hovered = false; //--- Set previous heatmap hover state bool prev_pval_hovered = false; //--- Set previous pval hover state bool prev_sort_hovered = false; //--- Set previous sort hover state bool prev_theme_hovered = false; //--- Set previous theme hover state bool prev_tf_hovered[NUM_TF]; //--- Declare previous TF hover states array int last_mouse_x = 0, last_mouse_y = 0; //--- Initialize last mouse position bool is_light_theme = false; //--- Set theme flag (dark by default) int sort_mode = 0; //--- Initialize sort mode (0=original) string original_symbols[MAX_SYMBOLS]; //--- Declare array to store original order ViewMode global_view_mode = VIEW_CORR;
Wir beginnen die Implementierung mit dem Hinzufügen von Eingabeparametern für die Anpassung des hellen Farbschemas, einschließlich „ColorLightThemeBg", das für Hintergründe auf Weiß und „ColorLightThemeText", das für Text auf Schwarz gesetzt ist. Anschließend definieren wir die Enumeration „ViewMode" mit den Optionen „VIEW_CORR" für die Anzeige von Korrelationen und „VIEW_PVAL" für die Anzeige von p-Werten, sodass zwischen den Ansichten umgeschaltet werden kann. Als nächstes führen wir Definitionen für neue Elemente der Nutzeroberfläche wie „SORT_BUTTON" und „THEME_BUTTON" ein, zusammen mit Farben wie „COLOR_LIGHT_GRAY" als helles Grau für neutrale Zellen im hellen Modus, „COLOR_DARK_GRAY" als Dunkelgrau für Kopfzeilen, „BUTTON_HOVER_SIZE" mit vierundzwanzig Pixeln für Bereiche zur Erkennung des Schwebens von Schaltflächen und „NUM_LEGEND_ITEMS" erweitert auf fünfzehn, um feinere Legenden in Modi wie Heatmap.
Wir deklarieren globale Variablen, um Interaktionszustände zu verwalten: „panel_is_visible“ wird auf „true“ gesetzt, um die Sichtbarkeit des Dashboards anzuzeigen, „panel_minimized“ auf „false“ für die Vollbildansicht, „panel_dragging“ auf „false“ mit Koordinaten wie „panel_drag_x“ und „panel_start_x“ auf Null für die Verarbeitung von Drag-Aktionen, „prev_mouse_state“ auf Null, um Mausknopfzustände zu verfolgen, boolesche Flags wie „prev_header_hovered“, gesetzt auf „false“ zur Überwachung von Hover-Zuständen über Elementen wie Header, Toggle, Schließen, Heatmap, p-Wert, Sortieren und Schaltflächen der Farbschemen, ein Array „prev_tf_hovered“ für Hover-Zustände über Zeitrahmen-Zellen, „last_mouse_x“ und „last_mouse_y“ auf Null für die letzte Cursorposition, „is_light_theme“ auf „false“ bei Start im Dunkelmodus und „sort_mode“ auf Null für die ursprüngliche Symbolreihenfolge. Außerdem fügen wir das String-Array „original_symbols" mit der maximalen Symbolgröße hinzu, um die anfängliche Symbolreihenfolge für Sortierrückstellungen zu erhalten, und setzen „global_view_mode" auf „VIEW_CORR" als Standardansicht.
Wenn wir nun die Symbole analysieren, müssen wir die Klassifizierung der ursprünglichen Symbole speichern, damit wir sie abrufen können, wenn wir zum ursprünglichen Zustand wechseln. Wir fügen sie einfach dem definierten Array hinzu, wie unten gezeigt. Zur besseren Übersichtlichkeit haben wir die Ergänzung hervorgehoben.
//+------------------------------------------------------------------+ //| Parse symbols list into array | //+------------------------------------------------------------------+ void parse_symbols() { string temp = SymbolsList; //--- Copy input symbols list num_symbols = 0; //--- Reset symbol count while (StringFind(temp, ",") >= 0 && num_symbols < MAX_SYMBOLS) { //--- Loop through comma-separated symbols int pos = StringFind(temp, ","); //--- Find comma position string sym = StringSubstr(temp, 0, pos); //--- Extract symbol if (SymbolSelect(sym, true)) { //--- Select symbol if available symbols_array[num_symbols] = sym; //--- Store valid symbol original_symbols[num_symbols] = sym; //--- Store in original order num_symbols++; //--- Increment count } else { //--- Handle unavailable symbol Print("Warning: Symbol ", sym, " not available."); //--- Print warning } temp = StringSubstr(temp, pos + 1); //--- Update remaining string } if (StringLen(temp) > 0 && num_symbols < MAX_SYMBOLS) { //--- Handle last symbol if (SymbolSelect(temp, true)) { //--- Select last symbol if available symbols_array[num_symbols] = temp; //--- Store last valid symbol original_symbols[num_symbols] = temp; //--- Store in original order num_symbols++; //--- Increment count } else { //--- Handle unavailable last symbol Print("Warning: Symbol ", temp, " not available."); //--- Print warning } } if (num_symbols < 2) { //--- Check minimum symbols Print("Error: At least 2 valid symbols required. Found: ", num_symbols); //--- Print error ExpertRemove(); //--- Remove expert } }
Um die Sortierung nach Stärke von ursprünglich über absteigend bis hin zu aufsteigend für die Clusterung analytischer Werte einzuführen, müssen wir die entsprechenden Funktionen einführen. Hier ist die Logik, die wir dafür verwendet haben.
//+------------------------------------------------------------------+ //| Sort symbols by average correlation strength | //+------------------------------------------------------------------+ void sort_symbols_by_strength() { if (sort_mode == 0) { //--- Check original mode ArrayCopy(symbols_array, original_symbols); //--- Restore original symbols update_correlations(); //--- Recompute correlations return; //--- Exit function } double avg_corr[MAX_SYMBOLS]; //--- Declare average correlation array ArrayInitialize(avg_corr, 0.0); //--- Initialize averages for (int i = 0; i < num_symbols; i++) { //--- Loop symbols for (int j = 0; j < num_symbols; j++) { //--- Loop pairs if (i != j) avg_corr[i] += MathAbs(correlation_matrix[i][j]); //--- Accumulate absolute correlations } avg_corr[i] /= (num_symbols - 1); //--- Compute average } int indices[MAX_SYMBOLS]; //--- Declare indices array for (int i = 0; i < num_symbols; i++) indices[i] = i; //--- Initialize indices for (int i = 0; i < num_symbols - 1; i++) { //--- Loop outer for sorting for (int j = i + 1; j < num_symbols; j++) { //--- Loop inner for comparison bool swap = (sort_mode == 1) ? (avg_corr[indices[i]] < avg_corr[indices[j]]) : (avg_corr[indices[i]] > avg_corr[indices[j]]); //--- Determine swap if (swap) { //--- Check if swap needed int temp = indices[i]; //--- Store temporary indices[i] = indices[j]; //--- Swap indices indices[j] = temp; //--- Complete swap } } } string new_symbols[MAX_SYMBOLS]; //--- Declare new symbols array double new_corr[MAX_SYMBOLS][MAX_SYMBOLS]; //--- Declare new correlation matrix double new_pval[MAX_SYMBOLS][MAX_SYMBOLS]; //--- Declare new p-value matrix for (int i = 0; i < num_symbols; i++) { //--- Loop to reorder int old_i = indices[i]; //--- Get old index new_symbols[i] = symbols_array[old_i]; //--- Set new symbol for (int j = 0; j < num_symbols; j++) { //--- Loop columns int old_j = indices[j]; //--- Get old column index new_corr[i][j] = correlation_matrix[old_i][old_j]; //--- Copy correlation new_pval[i][j] = pvalue_matrix[old_i][old_j]; //--- Copy p-value } } ArrayCopy(symbols_array, new_symbols); //--- Update symbols ArrayCopy(correlation_matrix, new_corr); //--- Update correlations ArrayCopy(pvalue_matrix, new_pval); //--- Update p-values } //+------------------------------------------------------------------+ //| Cycle sort mode | //+------------------------------------------------------------------+ void cycle_sort_mode() { sort_mode = (sort_mode + 1) % 3; //--- Cycle mode string direction; //--- Declare direction string if (sort_mode == 0) direction = "original"; //--- Set original else if (sort_mode == 1) direction = "descending"; //--- Set descending else direction = "ascending"; //--- Set ascending Print("Sort mode cycled to ", direction); //--- Print new mode sort_symbols_by_strength(); //--- Sort symbols }
Zunächst implementieren wir die Funktion „sort_symbols_by_strength", um die Symbole auf der Grundlage ihrer durchschnittlichen absoluten Korrelationsstärke gemäß dem aktuellen Sortiermodus neu zu ordnen. Wenn „sort_mode" für die ursprüngliche Reihenfolge Null ist, kopieren wir das Array „original_symbols" mit ArrayCopy zurück nach „symbols_array" und berechnen die Matrizen mit „update_correlations" neu, bevor wir die Funktion beenden. Andernfalls deklarieren und initialisieren wir mit ArrayInitialize ein Double-Array „avg_corr" mit einer maximalen Symbolgröße von null, durchlaufen dann eine Schleife über Symbole und Paare, akkumulieren die absoluten Werte aus der „correlation_matrix" ohne Selbstpaare und dividieren durch „num_symbols" minus eins, um Durchschnittswerte zu erhalten. Wir richten ein Array „indices" ein, das sequenziell initialisiert wird, und sortieren es mit dem Bubblesort: In den verschachtelten Schleifen bestimmen wir, ob ein Platztausch erforderlich ist, indem wir den „sort_mode" auf eins für aufsteigend (niedrigerer Durchschnitt zuerst) oder zwei für absteigend (höherer zuerst) einstellen und die Indizes ggf. die Plätze tauschen.
Dann deklarieren wir neue Arrays für Symbole, Korrelationen und p-Werte und führen eine Schleife zur Neuordnung durch: Für jede neue Position erhalten wir den alten Index, setzen das Symbol und kopieren Matrixzeilen und -spalten entsprechend von der alten zur neuen Position. Schließlich aktualisieren wir die globalen Arrays mit „ArrayCopy" für Symbole, „correlation_matrix" und „pvalue_matrix". Als Nächstes erstellen wir die Funktion „cycle_sort_mode“, um den Wert von „sort_mode“ modulo drei zu erhöhen und so zwischen „original“, „absteigend“ und „aufsteigend“ zu wechseln. Wir setzen einen Richtungsstring basierend auf dem neuen Modus und drucken ihn aus. Dann rufen wir „sort_symbols_by_strength" auf, um die Sortierung anzuwenden. Als Nächstes müssen wir die Konsistenz zwischen hellen und dunklen Farbschemen sicherstellen und die visuelle Kohärenz verbessern, wenn die Farbschemen für die Zeiträume umgeschaltet werden. Um dies zu erreichen, müssen wir die Hervorhebungen wie folgt aktualisieren, damit der inaktive Hintergrund vom verwendeten Farbschema abhängt.
//+------------------------------------------------------------------+ //| Update TF highlights | //+------------------------------------------------------------------+ void update_tf_highlights() { color inactive_bg = is_light_theme ? clrSilver : C'60,60,60'; //--- Set inactive background for (int i = 0; i < num_tf_visible; i++) { //--- Loop visible TFs string rect_name = TF_CELL_RECT + IntegerToString(i); //--- Get rectangle name color bg = (i == current_tf_index) ? ColorStrongPositiveBg : inactive_bg; //--- Set background ObjectSetInteger(0, rect_name, OBJPROP_BGCOLOR, bg); //--- Update background } ChartRedraw(0); //--- Redraw chart }
Hier haben wir einen ternären Operator verwendet, um die inaktive Hintergrundfarbe im hellen Modus auf Silber zu setzen; andernfalls bleibt die ursprüngliche Farbe erhalten. Wir werden dasselbe mit den Legendenelementen vornehmen, damit sich deren Hintergrund und Textbeschriftungen nun an das ausgewählte Design anpassen und so für eine einheitliche Darstellung sorgen.
//+------------------------------------------------------------------+ //| Recreate legend objects based on current mode | //+------------------------------------------------------------------+ void recreate_legend() { //--- Existing logic color neutral_bg = is_light_theme ? COLOR_LIGHT_GRAY : ColorNeutralBg; //--- Set neutral background color text_color = is_light_theme ? ColorLightThemeText : COLOR_WHITE; //--- Set text color for (int i = 0; i < num_legend_visible; i++) { //--- Loop to create int x_offset = x_start + i * (WIDTH_LEGEND_CELL + LEGEND_SPACING); //--- Compute offset string rect_name = LEGEND_CELL_RECTANGLE + IntegerToString(i); //--- Get rectangle name string text_name = LEGEND_CELL_TEXT + IntegerToString(i); //--- Get text name create_rectangle(rect_name, x_offset, legend_y + 2, WIDTH_LEGEND_CELL, HEIGHT_LEGEND, neutral_bg); //--- Create rectangle create_label(text_name, "0%", x_offset + WIDTH_LEGEND_CELL / 2, legend_y + 2 + HEIGHT_LEGEND / 2 - 1, 10, text_color, "Arial"); //--- Create label } } //+------------------------------------------------------------------+ //| Update legend colors and texts based on mode | //+------------------------------------------------------------------+ void update_legend() { color default_txt = is_light_theme ? ColorLightThemeText : ColorTextStrong; //--- Set default text color //--- Rest of the logic } //+------------------------------------------------------------------+ //| Update borders for main panel and legend | //+------------------------------------------------------------------+ void update_borders() { color border_col = is_light_theme ? COLOR_BLACK : COLOR_WHITE; //--- Set border color //--- Rest of the logic }
Wir ändern die Funktion „recreate_legend", um Änderungen des Farbschemas zu unterstützen, indem wir die neutrale Hintergrundfarbe bedingt einstellen: Wenn „is_light_theme" wahr ist, verwenden wir „COLOR_LIGHT_GRAY", andernfalls „ColorNeutralBg". In ähnlicher Weise stellen wir die Textfarbe auf „ColorLightThemeText" im hellen Modus oder Weiß im dunklen Modus ein. In der Erstellungsschleife werden diese Farben beim Aufruf von „create_rectangle" für jedes Legendenelement mit dem berechneten neutralen Hintergrund und bei „create_label" mit der aktualisierten Textfarbe angewendet, um sicherzustellen, dass die Legende optisch an das aktuelle Farbschema angepasst ist.
Als Nächstes aktualisieren wir die Funktion „update_legend", um die Standard-Textfarbe auf der Grundlage des Farbschemas festzulegen: „ColorLightThemeText" für hell oder „ColorTextStrong" für dunkel, die dann in der Schleife für die Behandlung von Textfarben in verschiedenen Korrelationsfällen verwendet wird. Anschließend implementieren wir die Funktion „update_borders", um die Rahmenfarben für die wichtigsten Felder zu aktualisieren. Mit „is_light_theme" legen wir die Rahmenfarbe als Schwarz im hellen Farbschema oder Weiß im dunklen Farbschema fest und wenden sie auf die Kopf-, Haupt- und Legendenfelder an, indem wir OBJPROP_COLOR mit der Funktion ObjectSetInteger setzen. Wir benötigen nun neue Funktionen, um Themen, Minimierung, Ziehen und Schweben der Dashboard-Komponenten zu handhaben. Wir beginnen mit der Behandlung von Toggle-Klicks.
//+------------------------------------------------------------------+ //| Toggle theme (dark/light) | //+------------------------------------------------------------------+ void toggle_theme() { is_light_theme = !is_light_theme; //--- Switch theme color main_bg = is_light_theme ? ColorLightThemeBg : C'30,30,30'; //--- Set main background color header_bg = is_light_theme ? clrSilver : C'60,60,60'; //--- Set header background color text_color = is_light_theme ? ColorLightThemeText : COLOR_WHITE; //--- Set text color color neutral_bg = is_light_theme ? COLOR_LIGHT_GRAY : ColorNeutralBg; //--- Set neutral background color diagonal_bg = is_light_theme ? clrGray : ColorDiagonalBg; //--- Set diagonal background color theme_icon_color = is_light_theme ? clrBlack : clrWhite; //--- Set theme icon color color button_text = is_light_theme ? clrNavy : clrGold; //--- Set button text color color close_text = is_light_theme ? clrBlack : clrWhite; //--- Set close text color color header_icon_color = is_light_theme ? clrDodgerBlue : clrAqua; //--- Set header icon color ObjectSetInteger(0, MAIN_PANEL, OBJPROP_BGCOLOR, main_bg); //--- Update main background ObjectSetInteger(0, HEADER_PANEL, OBJPROP_BGCOLOR, header_bg); //--- Update header background ObjectSetInteger(0, LEGEND_PANEL, OBJPROP_BGCOLOR, main_bg); //--- Update legend background ObjectSetInteger(0, HEADER_PANEL_TEXT, OBJPROP_COLOR, text_color); //--- Update header text color ObjectSetInteger(0, HEADER_PANEL_ICON, OBJPROP_COLOR, header_icon_color); //--- Update header icon color ObjectSetInteger(0, CLOSE_BUTTON, OBJPROP_COLOR, close_text); //--- Update close color ObjectSetInteger(0, TOGGLE_BUTTON, OBJPROP_COLOR, button_text); //--- Update toggle color ObjectSetInteger(0, HEATMAP_BUTTON, OBJPROP_COLOR, button_text); //--- Update heatmap color ObjectSetInteger(0, PVAL_BUTTON, OBJPROP_COLOR, button_text); //--- Update pval color ObjectSetInteger(0, SORT_BUTTON, OBJPROP_COLOR, button_text); //--- Update sort color ObjectSetInteger(0, THEME_BUTTON, OBJPROP_COLOR, theme_icon_color); //--- Update theme color for (int i = 0; i < num_symbols; i++) { //--- Loop symbols ObjectSetInteger(0, SYMBOL_ROW_RECTANGLE + IntegerToString(i), OBJPROP_BGCOLOR, header_bg); //--- Update row background ObjectSetInteger(0, SYMBOL_ROW_TEXT + IntegerToString(i), OBJPROP_COLOR, text_color); //--- Update row text color ObjectSetInteger(0, SYMBOL_COL_RECTANGLE + IntegerToString(i), OBJPROP_BGCOLOR, header_bg); //--- Update column background ObjectSetInteger(0, SYMBOL_COL_TEXT + IntegerToString(i), OBJPROP_COLOR, text_color); //--- Update column text color for (int j = 0; j < num_symbols; j++) { //--- Loop cells string cell_name = CELL_RECTANGLE + IntegerToString(i) + "_" + IntegerToString(j); //--- Get cell name color bg = (i == j) ? diagonal_bg : neutral_bg; //--- Set base background ObjectSetInteger(0, cell_name, OBJPROP_BGCOLOR, bg); //--- Update cell background } } update_dashboard(); //--- Reapply colors update_borders(); //--- Update borders ChartRedraw(0); //--- Redraw chart }
Wir implementieren die Funktion „toggle_theme", um zwischen dem hellen und dem dunklen Dashboard-Farbschema zu wechseln. Die Funktion kehrt das boolesche Flag „is_light_theme" um und setzt die lokalen Farben entsprechend. Der Haupthintergrund ist entweder „ColorLightThemeBg" oder dunkelgrau. Die Kopfzeile wechselt zu Silber oder Mittelgrau. Der Text wird zu „ColorLightThemeText" oder weiß. Die neutrale Farbe ist hellgrau oder „ColorNeutralBg". Die diagonale Farbe ist grau oder „ColorDiagonalBg". Das Symbol für das Farbschema wird schwarz oder weiß.
Der Text auf der Schaltfläche ist marineblau oder goldfarben. Der Text zum Schließen ist schwarz oder weiß. Das Kopfzeilensymbol wechselt zu Dodger Blue oder Aqua. Wir verwenden ObjectSetInteger, um Objekteigenschaften zu aktualisieren. Die Hintergründe des Hauptfensters, der Kopfzeile und der Legende erhalten die neuen Haupt- oder Kopffarben. Kopfzeilentext und -symbol ändern sich in ihre aktualisierten Text- und Symbolfarben. Die Schaltflächen zum Schließen, Umschalten, der Heatmap, dem p-Wert, dem Sortieren und dem Farbschema erhalten ihre neuen Farben.
In einer Schleife über die Symbole aktualisieren wir die Zeilen- und Spaltenrechtecke auf den neuen Kopfzeilenhintergrund und setzen ihre Texte auf die neue Textfarbe. Innerhalb der Schleife bilden wir für jede Zelle den Namen, bestimmen den Basishintergrund – diagonal, wenn er auf der Diagonalen liegt, ansonsten neutral – und legen ihn fest. Schließlich rufen wir „update_dashboard" auf, um die Zellenfarben neu zuzuweisen, „update_borders" für die Panel-Ränder und ChartRedraw, um die Anzeige zu aktualisieren. Für den minimierten Modus erstellen wir nur die Kopfkomponenten, wie unten beschrieben.
//+------------------------------------------------------------------+ //| Create minimized dashboard UI | //+------------------------------------------------------------------+ void create_minimized_dashboard() { color header_bg = is_light_theme ? clrSilver : C'60,60,60'; //--- Set header background color text_color = is_light_theme ? ColorLightThemeText : COLOR_WHITE; //--- Set text color color button_text = is_light_theme ? clrNavy : clrGold; //--- Set button text color color close_text = is_light_theme ? clrBlack : clrWhite; //--- Set close text color color header_icon_color = is_light_theme ? clrDodgerBlue : clrAqua; //--- Set header icon color int panel_width = WIDTH_SYMBOL + num_symbols * (WIDTH_CELL - 1) + 4; //--- Compute panel width create_rectangle(HEADER_PANEL, panel_x, panel_y, panel_width, HEIGHT_HEADER, header_bg); //--- Create header panel create_label(HEADER_PANEL_ICON, CharToString(181), panel_x + 12, panel_y + 14, 18, header_icon_color, "Wingdings"); //--- Create header icon create_label(HEADER_PANEL_TEXT, "Correlation Matrix", panel_x + 90, panel_y + 12, 13, text_color); //--- Create header text create_label(CLOSE_BUTTON, CharToString('r'), panel_x + (panel_width - 17), panel_y + 14, 18, close_text, "Webdings"); //--- Create close button create_label(TOGGLE_BUTTON, CharToString('o'), panel_x + (panel_width - 47), panel_y + 14, 18, button_text, "Wingdings"); //--- Create toggle button update_borders(); //--- Update borders ChartRedraw(0); //--- Redraw chart }
Hier implementieren wir die Funktion „create_minimized_dashboard", um eine kompakte Version der Nutzeroberfläche zu erzeugen, wenn das Panel minimiert ist und aus Platzgründen nur die Kopfzeile angezeigt wird. Es setzt lokale Farben auf der Grundlage des Farbschemas: Kopfzeilenhintergrund auf Silber im hellen Modus oder Mittelgrau im dunklen Modus, Textfarbe auf „ColorLightThemeText" oder Weiß, Schaltflächentext auf Marineblau oder Gold, Schließtext auf Schwarz oder Weiß und Kopfzeilensymbol auf Dodgerblau oder Aqua.
Wir berechnen die Breite des Panels aus Konstanten, die an die Anzahl der Symbole angepasst sind, und rufen dann „create_rectangle" für das „HEADER_PANEL" mit der aktuellen Position und der Kopfhöhe auf, wobei der Hintergrund des Farbschemas verwendet wird. Wir fügen Beschriftungen für das Kopfzeilensymbol mit dem Wingdings-Zeichen 181, den Titel als Korrelationsmatrix, die Schließen-Schaltfläche mit dem Webdings-Zeichen „r" und die Umschalt-Schaltfläche mit dem Wingdings-Zeichen „o" hinzu, die alle relativ zum Panel positioniert sind und die Farben des Farbschemas verwenden.
Schließlich rufen wir „update_borders" auf, um die Ränder, und „ChartRedraw“, um die Anzeige zu aktualisieren. Um die Klicks für den Abschluss und den Wechsel des Zeitrahmens zu verarbeiten, implementieren wir die folgenden Funktionen.
//+------------------------------------------------------------------+ //| Delete all dashboard objects | //+------------------------------------------------------------------+ void delete_all_objects() { ObjectDelete(0, MAIN_PANEL); //--- Delete main panel ObjectDelete(0, HEADER_PANEL); //--- Delete header panel ObjectDelete(0, HEADER_PANEL_ICON); //--- Delete header icon ObjectDelete(0, HEADER_PANEL_TEXT); //--- Delete header text ObjectDelete(0, CLOSE_BUTTON); //--- Delete close button ObjectDelete(0, TOGGLE_BUTTON); //--- Delete toggle button ObjectDelete(0, HEATMAP_BUTTON); //--- Delete heatmap button ObjectDelete(0, PVAL_BUTTON); //--- Delete pval button ObjectDelete(0, SORT_BUTTON); //--- Delete sort button ObjectDelete(0, THEME_BUTTON); //--- Delete theme button for (int i = 0; i < NUM_TF; i++) { //--- Loop TFs ObjectDelete(0, TF_CELL_RECT + IntegerToString(i)); //--- Delete TF rectangle ObjectDelete(0, TF_CELL_TEXT + IntegerToString(i)); //--- Delete TF text } ObjectDelete(0, "SYMBOL_ROW_HEADER"); //--- Delete row header rectangle ObjectDelete(0, "SYMBOL_ROW_HEADER_TEXT"); //--- Delete row header text for (int i = 0; i < num_symbols; i++) { //--- Loop symbols ObjectDelete(0, SYMBOL_ROW_RECTANGLE + IntegerToString(i)); //--- Delete row rectangle ObjectDelete(0, SYMBOL_ROW_TEXT + IntegerToString(i)); //--- Delete row text ObjectDelete(0, SYMBOL_COL_RECTANGLE + IntegerToString(i)); //--- Delete column rectangle ObjectDelete(0, SYMBOL_COL_TEXT + IntegerToString(i)); //--- Delete column text for (int j = 0; j < num_symbols; j++) { //--- Loop cells ObjectDelete(0, CELL_RECTANGLE + IntegerToString(i) + "_" + IntegerToString(j)); //--- Delete cell rectangle ObjectDelete(0, CELL_TEXT + IntegerToString(i) + "_" + IntegerToString(j)); //--- Delete cell text } } ObjectDelete(0, LEGEND_PANEL); //--- Delete legend panel for (int i = 0; i < NUM_LEGEND_ITEMS; i++) { //--- Loop legend items ObjectDelete(0, LEGEND_CELL_RECTANGLE + IntegerToString(i)); //--- Delete legend rectangle ObjectDelete(0, LEGEND_CELL_TEXT + IntegerToString(i)); //--- Delete legend text } } //+------------------------------------------------------------------+ //| Switch to specific timeframe | //+------------------------------------------------------------------+ void switch_timeframe(int index) { if (index < 0 || index >= num_tf_visible) return; //--- Check valid index global_correlation_tf = tf_list[index]; //--- Set new timeframe current_tf_index = index; //--- Update index Print("Switched timeframe to ", tf_strings[index]); //--- Print switch update_tf_highlights(); //--- Update highlights update_dashboard(); //--- Update dashboard }
Für das Schließen von Klicks implementieren wir die Funktion „delete_all_objects", um alle mit dem Dashboard verbundenen grafischen Objekte beim Schließen oder Zurücksetzen zu löschen. Es verwendet ObjectDelete mit dem Subfenster „zero“, um das Hauptfenster, das Kopfzeilenfenster, das Kopfzeilensymbol sowie die Schaltflächen zum Schließen, Umschalten, für die Heatmap, den p-Wert, die Sortierung und das Design zu entfernen. Wir durchlaufen in einer Schleife die Anzahl der Zeitrahmen, um jedes Rechteck und jeden Text der Zeitrahmenzellen mithilfe von Präfixen und Indexzeichenfolgen zu löschen. Anschließend löschen wir das Rechteck und den Text der Symbolzeilenüberschrift und entfernen in einer Schleife über alle Symbole hinweg die Zeilen- und Spaltenrechtecke sowie die Texte. Darin werden Schleifen über die Zellen gelegt, um jedes Korrelationszellenrechteck und jeden Text mit verketteten Namen zu löschen. Schließlich löschen wir das Legendenfeld und ziehen eine Schleife über die Legendenelemente, um ihre Rechtecke und Texte zu entfernen.
Als Nächstes erstellen wir die Funktion „switch_timeframe", um den Analysezeitrahmen auf der Grundlage eines bestimmten Index zu ändern. Sie prüft, ob der Index innerhalb von Null bis „num_tf_visible" minus Eins gültig ist, und bricht andernfalls vorzeitig ab. Wir setzen „global_correlation_tf" auf den Wert an diesem Index in „tf_list", aktualisieren „current_tf_index", drucken die Umschaltmeldung mit der entsprechenden Zeichenkette aus „tf_strings" aus, rufen „update_tf_highlights" auf, um das Bildmaterial zu aktualisieren, und „update_dashboard", um die neuen Daten neu zu berechnen und anzuzeigen. Als Nächstes müssen wir das gesamte Dashboard aktualisieren, damit es auf das Farbschema reagiert, und die neuen Schaltflächen hinzufügen, die wir für Farbschema und Sortierung benötigen.
//+------------------------------------------------------------------+ //| Create full dashboard UI | //+------------------------------------------------------------------+ void create_full_dashboard() { color main_bg = is_light_theme ? ColorLightThemeBg : C'30,30,30'; //--- Set main background color header_bg = is_light_theme ? clrSilver : C'60,60,60'; //--- Set header background color text_color = is_light_theme ? ColorLightThemeText : COLOR_WHITE; //--- Set text color color neutral_bg = is_light_theme ? COLOR_LIGHT_GRAY : ColorNeutralBg; //--- Set neutral background color button_text = is_light_theme ? clrNavy : clrGold; //--- Set button text color color theme_icon_color = is_light_theme ? clrBlack : clrWhite; //--- Set theme icon color color close_text = is_light_theme ? clrBlack : clrWhite; //--- Set close text color color header_icon_color = is_light_theme ? clrDodgerBlue : clrAqua; //--- Set header icon color int panel_width = WIDTH_SYMBOL + num_symbols * (WIDTH_CELL - 1) + 4; //--- Compute panel width int panel_height = HEIGHT_HEADER + HEIGHT_TF_CELL + GAP_HEIGHT + HEIGHT_RECTANGLE * (num_symbols + 1) - num_symbols + 2; //--- Compute panel height create_rectangle(MAIN_PANEL, panel_x, panel_y, panel_width, panel_height, main_bg); //--- Create main panel create_rectangle(HEADER_PANEL, panel_x, panel_y, panel_width, HEIGHT_HEADER, header_bg); //--- Create header panel create_label(HEADER_PANEL_ICON, CharToString(181), panel_x + 12, panel_y + 14, 18, header_icon_color, "Wingdings"); //--- Create header icon create_label(HEADER_PANEL_TEXT, "Correlation Matrix", panel_x + 90, panel_y + 12, 13, text_color); //--- Create header text create_label(CLOSE_BUTTON, CharToString('r'), panel_x + (panel_width - 17), panel_y + 14, 18, close_text, "Webdings"); //--- Create close button ObjectSetString(0, CLOSE_BUTTON, OBJPROP_TOOLTIP, "Close Panel"); //--- Set close tooltip create_label(TOGGLE_BUTTON, CharToString('r'), panel_x + (panel_width - 47), panel_y + 14, 18, button_text, "Wingdings"); //--- Create toggle button ObjectSetString(0, TOGGLE_BUTTON, OBJPROP_TOOLTIP, "Toggle Minimize/Maximize"); //--- Set toggle tooltip string heatmap_icon = CharToString(global_display_mode == MODE_STANDARD ? (uchar)82 : (uchar)110); //--- Set heatmap icon create_label(HEATMAP_BUTTON, heatmap_icon, panel_x + (panel_width - 77), panel_y + 14, 18, button_text, "Wingdings"); //--- Create heatmap button ObjectSetString(0, HEATMAP_BUTTON, OBJPROP_TOOLTIP, "Toggle Heatmap/Standard Mode"); //--- Set heatmap tooltip create_label(PVAL_BUTTON, CharToString('X'), panel_x + (panel_width - 107), panel_y + 14, 18, button_text, "Wingdings"); //--- Create pval button ObjectSetString(0, PVAL_BUTTON, OBJPROP_TOOLTIP, "Toggle Correlation/P-Value View"); //--- Set pval tooltip string sort_icon; //--- Declare sort icon if (sort_mode == 0) sort_icon = CharToString('N'); //--- Set neutral for original else if (sort_mode == 1) sort_icon = CharToString('K'); //--- Set descending else sort_icon = CharToString('J'); //--- Set ascending create_label(SORT_BUTTON, sort_icon, panel_x + (panel_width - 137), panel_y + 14, 18, button_text, "Wingdings 3"); //--- Create sort button ObjectSetString(0, SORT_BUTTON, OBJPROP_TOOLTIP, "Sort Symbols by Strength (Cycle Original/Desc/Asc)"); //--- Set sort tooltip create_label(THEME_BUTTON, CharToString('['), panel_x + (panel_width - 167), panel_y + 14, 18, theme_icon_color, "Wingdings"); //--- Create theme button ObjectSetString(0, THEME_BUTTON, OBJPROP_TOOLTIP, "Toggle Dark/Light Theme"); //--- Set theme tooltip int tf_y = panel_y + HEIGHT_HEADER; //--- Compute TF y position int tf_x_start = panel_x + 2; //--- Set TF start x for (int i = 0; i < num_tf_visible; i++) { //--- Loop visible TFs int x_offset = tf_x_start + i * WIDTH_TF_CELL; //--- Compute offset string rect_name = TF_CELL_RECT + IntegerToString(i); //--- Get rectangle name string text_name = TF_CELL_TEXT + IntegerToString(i); //--- Get text name color bg = (i == current_tf_index) ? ColorStrongPositiveBg : header_bg; //--- Set background create_rectangle(rect_name, x_offset, tf_y, WIDTH_TF_CELL, HEIGHT_TF_CELL, bg); //--- Create TF rectangle create_label(text_name, tf_strings[i], x_offset + (WIDTH_TF_CELL / 2), tf_y + (HEIGHT_TF_CELL / 2), 10, text_color, "Arial Bold"); //--- Create TF text } int matrix_y = tf_y + HEIGHT_TF_CELL + GAP_HEIGHT; //--- Compute matrix y create_rectangle("SYMBOL_ROW_HEADER", panel_x + 2, matrix_y, WIDTH_SYMBOL, HEIGHT_RECTANGLE, header_bg); //--- Create row header rectangle create_label("SYMBOL_ROW_HEADER_TEXT", "Symbols", panel_x + (WIDTH_SYMBOL / 2 + 2), matrix_y + (HEIGHT_RECTANGLE / 2), 10, text_color, "Arial Bold"); //--- Create row header text for (int i = 0; i < num_symbols; i++) { //--- Loop row symbols int y_offset = matrix_y + HEIGHT_RECTANGLE * (i + 1) - (1 + i); //--- Compute y offset create_rectangle(SYMBOL_ROW_RECTANGLE + IntegerToString(i), panel_x + 2, y_offset, WIDTH_SYMBOL, HEIGHT_RECTANGLE, header_bg); //--- Create row rectangle create_label(SYMBOL_ROW_TEXT + IntegerToString(i), symbols_array[i], panel_x + (WIDTH_SYMBOL / 2 + 2), y_offset + (HEIGHT_RECTANGLE / 2 - 1), 10, text_color, "Arial Bold"); //--- Create row text } for (int j = 0; j < num_symbols; j++) { //--- Loop column symbols int x_offset = panel_x + WIDTH_SYMBOL + j * WIDTH_CELL - j + 1; //--- Compute x offset create_rectangle(SYMBOL_COL_RECTANGLE + IntegerToString(j), x_offset, matrix_y, WIDTH_CELL, HEIGHT_RECTANGLE, header_bg); //--- Create column rectangle create_label(SYMBOL_COL_TEXT + IntegerToString(j), symbols_array[j], x_offset + (WIDTH_CELL / 2), matrix_y + (HEIGHT_RECTANGLE / 2), 10, text_color, "Arial Bold"); //--- Create column text } for (int i = 0; i < num_symbols; i++) { //--- Loop rows for cells int y_offset = matrix_y + HEIGHT_RECTANGLE * (i + 1) - (1 + i); //--- Compute y offset for (int j = 0; j < num_symbols; j++) { //--- Loop columns for cells string cell_name = CELL_RECTANGLE + IntegerToString(i) + "_" + IntegerToString(j); //--- Get cell name string text_name = CELL_TEXT + IntegerToString(i) + "_" + IntegerToString(j); //--- Get text name int x_offset = panel_x + WIDTH_SYMBOL + j * WIDTH_CELL - j + 1; //--- Compute x offset create_rectangle(cell_name, x_offset, y_offset, WIDTH_CELL, HEIGHT_RECTANGLE, neutral_bg); //--- Create cell rectangle create_label(text_name, "0.00", x_offset + (WIDTH_CELL / 2), y_offset + (HEIGHT_RECTANGLE / 2 - 1), 10, text_color, "Arial"); //--- Create cell text } } int legend_y = panel_y + panel_height + GAP_MAIN_LEGEND; //--- Compute legend y create_rectangle(LEGEND_PANEL, panel_x, legend_y, panel_width, HEIGHT_LEGEND_PANEL, main_bg); //--- Create legend panel recreate_legend(); //--- Recreate legend ChartRedraw(0); //--- Redraw chart } //+------------------------------------------------------------------+ //| Update dashboard cells with correlation values and colors | //+------------------------------------------------------------------+ void update_dashboard() { update_correlations(); //--- Update correlations double strong_pos = StrongPositiveThresholdPct / 100.0; //--- Set positive threshold double strong_neg = StrongNegativeThresholdPct / 100.0; //--- Set negative threshold color text_base = is_light_theme ? ColorLightThemeText : ColorTextStrong; //--- Set base text color for (int i = 0; i < num_symbols; i++) { //--- Loop rows for (int j = 0; j < num_symbols; j++) { //--- Loop columns double corr = correlation_matrix[i][j]; //--- Get correlation double pval = pvalue_matrix[i][j]; //--- Get p-value string text = ""; //--- Initialize text if (global_view_mode == VIEW_CORR) { //--- Handle correlation view double corr_pct = corr * 100.0; //--- Compute percentage text = DoubleToString(corr_pct, 1) + "%" + get_significance_stars(pval); //--- Format correlation text } else { //--- Handle p-value view text = DoubleToString(pval, 4) + get_significance_stars(pval); //--- Format p-value text } color bg_color = is_light_theme ? clrLightGray : ColorNeutralBg; //--- Initialize background color txt_color = is_light_theme ? clrBlack : ColorTextZero; //--- Initialize text color if (i == j) { //--- Handle diagonal bg_color = is_light_theme ? clrGray : ColorDiagonalBg; //--- Set diagonal background txt_color = text_base; //--- Set text color } else { //--- Handle off-diagonal if (global_display_mode == MODE_STANDARD) { //--- Handle standard mode if (corr >= strong_pos) { //--- Check strong positive bg_color = ColorStrongPositiveBg; //--- Set positive background txt_color = text_base; //--- Set text color } else if (corr <= strong_neg) { //--- Check strong negative bg_color = ColorStrongNegativeBg; //--- Set negative background txt_color = text_base; //--- Set text color } else { //--- Handle mild bg_color = is_light_theme ? clrLightGray : ColorNeutralBg; //--- Set neutral background if (corr > 0.0) { //--- Check positive mild txt_color = is_light_theme ? clrBlue : ColorTextPositive; //--- Set positive text } else if (corr < 0.0) { //--- Check negative mild txt_color = is_light_theme ? clrRed : ColorTextNegative; //--- Set negative text } else { //--- Handle zero txt_color = text_base; //--- Set base text } } } else { //--- Handle heatmap mode txt_color = text_base; //--- Set text color bg_color = interpolate_heatmap_color(corr); //--- Set interpolated background } } string cell_name = CELL_RECTANGLE + IntegerToString(i) + "_" + IntegerToString(j); //--- Get cell name string text_name = CELL_TEXT + IntegerToString(i) + "_" + IntegerToString(j); //--- Get text name ObjectSetInteger(0, cell_name, OBJPROP_BGCOLOR, bg_color); //--- Update background ObjectSetString(0, text_name, OBJPROP_TEXT, text); //--- Update text ObjectSetInteger(0, text_name, OBJPROP_COLOR, txt_color); //--- Update text color string sym1 = symbols_array[i]; //--- Get first symbol string sym2 = symbols_array[j]; //--- Get second symbol string tf_str = EnumToString(global_correlation_tf); //--- Get timeframe string string method_str = EnumToString(CorrMethod); //--- Get method string string tooltip = StringFormat("Symbols: %s vs %s\nTimeframe: %s\nMethod: %s\nCorrelation: %.4f\nP-value: %.4f\nBars: %d", sym1, sym2, tf_str, method_str, corr, pval, CorrelationBars); //--- Format tooltip ObjectSetString(0, text_name, OBJPROP_TOOLTIP, tooltip); //--- Set text tooltip ObjectSetString(0, cell_name, OBJPROP_TOOLTIP, tooltip); //--- Set cell tooltip } } update_legend(); //--- Update legend ChartRedraw(0); //--- Redraw chart }
Zunächst modifizieren wir die Funktion „create_full_dashboard", um die Farben des jeweiligen Farbschemas und zusätzliche interaktive Elemente einzubauen. Wir setzen lokale Farben abhängig von „is_light_theme": den Haupthintergrund auf „ColorLightThemeBg" oder Dunkelgrau, die Kopfzeile auf Silber oder Mittelgrau, den Text auf „ColorLightThemeText" oder Weiß, Neutral auf „COLOR_LIGHT_GRAY" oder „ColorNeutralBg", den Schaltflächentext auf Marineblau oder Gold, das Farbschemasymbol auf Schwarz oder Weiß, den Text zum Schließen auf Schwarz oder Weiß und das Kopfzeilensymbol auf Dodgerblau oder Aqua.
Wir berechnen die Abmessungen der Panels wie zuvor und erstellen die Haupt- und Header-Panels mit diesen Hintergründen der Farbschemen. Wir fügen Beschriftungen für das Symbol in der Kopfzeile mit dem Zeichen 181 aus Wingdings hinzu, den Titel „Korrelationsmatrix“, die Schaltfläche zum Schließen mit dem Buchstaben „r“ aus Webdings, wobei der Tooltip über ObjectSetString mit OBJPROP_TOOLTIP auf „Fenster schließen“ gesetzt wird, der Umschalt-Schaltfläche mit „r“ aus Wingdings und Tooltip für Umschalten zwischen Minimieren/Maximieren, der Heatmap-Schaltfläche mit dynamischem Symbol je nach Anzeigemodus und Tooltip für Umschalten zwischen Heatmap/Standard, der p-Wert-Schaltfläche mit „X“ aus Wingdings und Tooltip für das Umschalten zwischen der Darstellung von Korrelationen und p-Werten, die Sortier-Schaltfläche mit Symbol abhängig von „sort_mode“ („N“ für Original, „K“ für absteigend, ‚J‘ für aufsteigend) aus Wingdings 3 und Tooltip zum Sortieren nach Stärkezyklusmodi sowie Farbschemen-Schaltfläche mit ‚[‘ aus Wingdings und Tooltip zum Umschalten zwischen dunklem und hellem Farbschema – alles unter Verwendung der Farben des jeweiligen Farbschemas.
Für die Zeile mit dem Zeitrahmen berechnen wir die Positionen und erstellen in einer Schleife Rechtecke mit einem vom aktuellen Index abhängigen Hintergrund und einer Kopffarbe sowie Beschriftungen mit Zeitrahmen-Strings in fett mit Arial Bold. Wir erstellen das Kopfzeilenrechteck der Symbole und den Text als Symbole mit den Farben des Farbschemas. Für Zeilensymbole erstellen wir in einer Schleife Rechtecke mit einem Kopfhintergrund und Beschriftungen mit Symbolnamen. In ähnlicher Weise erstellen wir Rechtecke und Beschriftungen für Spaltensymbole. Für die Zellen werden Schleifen verschachtelt, um Rechtecke mit einem neutralen Hintergrund und anfänglichen Textbeschriftungen als 0,00 in Arial zu erstellen. Anschließend wird das Legendenfeld mit dem Haupthintergrund erstellt, „recreate_legend" aufgerufen und neu gezeichnet. Dann aktualisieren wir die Funktion „update_dashboard", um Ansichtsmodi und Farbschemen zu unterstützen. Eine ähnliche Logik wird auch verwendet. Wenn wir diese Funktionen zu Testzwecken aufrufen, erhalten wir folgende Ergebnisse für das dunkle und das helle Farbschema.

Nachdem wir nun die Modi der Farbschemen in den Funktionen zur Erstellung des Dashboards gehandhabt haben, müssen wir den Schwebezustand von Schaltflächen und Klicks behandeln. Um dies zu erreichen, benötigen wir die folgenden Hilfsfunktionen.
//+------------------------------------------------------------------+ //| Check if cursor is inside header or buttons | //+------------------------------------------------------------------+ bool is_cursor_in_header_or_buttons(int mouse_x, int mouse_y) { int chart_width = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); //--- Get chart width int header_x = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_XDISTANCE); //--- Get header x int header_y = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_YDISTANCE); //--- Get header y int header_width = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_XSIZE); //--- Get header width int header_height = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_YSIZE);//--- Get header height int header_left = header_x; //--- Set left edge int header_right = header_left + header_width; //--- Set right edge bool in_header = (mouse_x >= header_left && mouse_x <= header_right && mouse_y >= header_y && mouse_y <= header_y + header_height); //--- Check in header int half_size = BUTTON_HOVER_SIZE / 2; //--- Compute half hover size int close_x = (int)ObjectGetInteger(0, CLOSE_BUTTON, OBJPROP_XDISTANCE); //--- Get close x int close_y = (int)ObjectGetInteger(0, CLOSE_BUTTON, OBJPROP_YDISTANCE); //--- Get close y bool in_close = (mouse_x >= close_x - half_size && mouse_x <= close_x + half_size && mouse_y >= close_y - half_size && mouse_y <= close_y + half_size); //--- Check in close int toggle_x = (int)ObjectGetInteger(0, TOGGLE_BUTTON, OBJPROP_XDISTANCE); //--- Get toggle x int toggle_y = (int)ObjectGetInteger(0, TOGGLE_BUTTON, OBJPROP_YDISTANCE); //--- Get toggle y bool in_toggle = (mouse_x >= toggle_x - half_size && mouse_x <= toggle_x + half_size && mouse_y >= toggle_y - half_size && mouse_y <= toggle_y + half_size); //--- Check in toggle int heatmap_x = (int)ObjectGetInteger(0, HEATMAP_BUTTON, OBJPROP_XDISTANCE); //--- Get heatmap x int heatmap_y = (int)ObjectGetInteger(0, HEATMAP_BUTTON, OBJPROP_YDISTANCE); //--- Get heatmap y bool in_heatmap = (mouse_x >= heatmap_x - half_size && mouse_x <= heatmap_x + half_size && mouse_y >= heatmap_y - half_size && mouse_y <= heatmap_y + half_size); //--- Check in heatmap int pval_x = (int)ObjectGetInteger(0, PVAL_BUTTON, OBJPROP_XDISTANCE); //--- Get pval x int pval_y = (int)ObjectGetInteger(0, PVAL_BUTTON, OBJPROP_YDISTANCE); //--- Get pval y bool in_pval = (mouse_x >= pval_x - half_size && mouse_x <= pval_x + half_size && mouse_y >= pval_y - half_size && mouse_y <= pval_y + half_size); //--- Check in pval int sort_x = (int)ObjectGetInteger(0, SORT_BUTTON, OBJPROP_XDISTANCE); //--- Get sort x int sort_y = (int)ObjectGetInteger(0, SORT_BUTTON, OBJPROP_YDISTANCE); //--- Get sort y bool in_sort = (mouse_x >= sort_x - half_size && mouse_x <= sort_x + half_size && mouse_y >= sort_y - half_size && mouse_y <= sort_y + half_size); //--- Check in sort int theme_x = (int)ObjectGetInteger(0, THEME_BUTTON, OBJPROP_XDISTANCE); //--- Get theme x int theme_y = (int)ObjectGetInteger(0, THEME_BUTTON, OBJPROP_YDISTANCE); //--- Get theme y bool in_theme = (mouse_x >= theme_x - half_size && mouse_x <= theme_x + half_size && mouse_y >= theme_y - half_size && mouse_y <= theme_y + half_size); //--- Check in theme bool in_tf = false; //--- Initialize TF check for (int i = 0; i < num_tf_visible; i++) { //--- Loop TFs string rect_name = TF_CELL_RECT + IntegerToString(i); //--- Get TF name int tf_x = (int)ObjectGetInteger(0, rect_name, OBJPROP_XDISTANCE); //--- Get TF x int tf_y = (int)ObjectGetInteger(0, rect_name, OBJPROP_YDISTANCE); //--- Get TF y if (mouse_x >= tf_x && mouse_x <= tf_x + WIDTH_TF_CELL && mouse_y >= tf_y && mouse_y <= tf_y + HEIGHT_TF_CELL) { //--- Check in TF in_tf = true; //--- Set in TF break; //--- Exit loop } } return in_header || in_close || in_toggle || in_heatmap || in_pval || in_sort || in_theme || in_tf; //--- Return if in any area } //+------------------------------------------------------------------+ //| Update button hover states | //+------------------------------------------------------------------+ void update_button_hover_states(int mouse_x, int mouse_y) { int half_size = BUTTON_HOVER_SIZE / 2; //--- Compute half hover size color hover_bg = clrDodgerBlue; //--- Set hover background color button_normal = is_light_theme ? clrNavy : clrGold; //--- Set normal button color color theme_normal = is_light_theme ? clrBlack : clrWhite; //--- Set normal theme color color close_normal = is_light_theme ? clrBlack : clrWhite; //--- Set normal close color color hover_text = is_light_theme ? clrWhite : clrYellow; //--- Set hover text color color theme_hover = is_light_theme ? clrWhite : clrYellow; //--- Set hover theme color color close_hover = clrRed; //--- Set hover close color int close_x = (int)ObjectGetInteger(0, CLOSE_BUTTON, OBJPROP_XDISTANCE); //--- Get close x int close_y = (int)ObjectGetInteger(0, CLOSE_BUTTON, OBJPROP_YDISTANCE); //--- Get close y bool is_close_hovered = (mouse_x >= close_x - half_size && mouse_x <= close_x + half_size && mouse_y >= close_y - half_size && mouse_y <= close_y + half_size); //--- Check close hover if (is_close_hovered != prev_close_hovered) { //--- Check state change ObjectSetInteger(0, CLOSE_BUTTON, OBJPROP_COLOR, is_close_hovered ? close_hover : close_normal); //--- Update close color ObjectSetInteger(0, CLOSE_BUTTON, OBJPROP_BGCOLOR, is_close_hovered ? hover_bg : clrNONE); //--- Update close background prev_close_hovered = is_close_hovered; //--- Update previous state ChartRedraw(0); //--- Redraw chart } int toggle_x = (int)ObjectGetInteger(0, TOGGLE_BUTTON, OBJPROP_XDISTANCE); //--- Get toggle x int toggle_y = (int)ObjectGetInteger(0, TOGGLE_BUTTON, OBJPROP_YDISTANCE); //--- Get toggle y bool is_toggle_hovered = (mouse_x >= toggle_x - half_size && mouse_x <= toggle_x + half_size && mouse_y >= toggle_y - half_size && mouse_y <= toggle_y + half_size); //--- Check toggle hover if (is_toggle_hovered != prev_toggle_hovered) { //--- Check state change ObjectSetInteger(0, TOGGLE_BUTTON, OBJPROP_COLOR, is_toggle_hovered ? hover_text : button_normal); //--- Update toggle color ObjectSetInteger(0, TOGGLE_BUTTON, OBJPROP_BGCOLOR, is_toggle_hovered ? hover_bg : clrNONE); //--- Update toggle background prev_toggle_hovered = is_toggle_hovered; //--- Update previous state ChartRedraw(0); //--- Redraw chart } int heatmap_x = (int)ObjectGetInteger(0, HEATMAP_BUTTON, OBJPROP_XDISTANCE); //--- Get heatmap x int heatmap_y = (int)ObjectGetInteger(0, HEATMAP_BUTTON, OBJPROP_YDISTANCE); //--- Get heatmap y bool is_heatmap_hovered = (mouse_x >= heatmap_x - half_size && mouse_x <= heatmap_x + half_size && mouse_y >= heatmap_y - half_size && mouse_y <= heatmap_y + half_size); //--- Check heatmap hover if (is_heatmap_hovered != prev_heatmap_hovered) { //--- Check state change ObjectSetInteger(0, HEATMAP_BUTTON, OBJPROP_COLOR, is_heatmap_hovered ? hover_text : button_normal); //--- Update heatmap color ObjectSetInteger(0, HEATMAP_BUTTON, OBJPROP_BGCOLOR, is_heatmap_hovered ? hover_bg : clrNONE); //--- Update heatmap background prev_heatmap_hovered = is_heatmap_hovered; //--- Update previous state ChartRedraw(0); //--- Redraw chart } int pval_x = (int)ObjectGetInteger(0, PVAL_BUTTON, OBJPROP_XDISTANCE); //--- Get pval x int pval_y = (int)ObjectGetInteger(0, PVAL_BUTTON, OBJPROP_YDISTANCE); //--- Get pval y bool is_pval_hovered = (mouse_x >= pval_x - half_size && mouse_x <= pval_x + half_size && mouse_y >= pval_y - half_size && mouse_y <= pval_y + half_size); //--- Check pval hover if (is_pval_hovered != prev_pval_hovered) { //--- Check state change ObjectSetInteger(0, PVAL_BUTTON, OBJPROP_COLOR, is_pval_hovered ? hover_text : button_normal); //--- Update pval color ObjectSetInteger(0, PVAL_BUTTON, OBJPROP_BGCOLOR, is_pval_hovered ? hover_bg : clrNONE); //--- Update pval background prev_pval_hovered = is_pval_hovered; //--- Update previous state ChartRedraw(0); //--- Redraw chart } int sort_x = (int)ObjectGetInteger(0, SORT_BUTTON, OBJPROP_XDISTANCE); //--- Get sort x int sort_y = (int)ObjectGetInteger(0, SORT_BUTTON, OBJPROP_YDISTANCE); //--- Get sort y bool is_sort_hovered = (mouse_x >= sort_x - half_size && mouse_x <= sort_x + half_size && mouse_y >= sort_y - half_size && mouse_y <= sort_y + half_size); //--- Check sort hover if (is_sort_hovered != prev_sort_hovered) { //--- Check state change ObjectSetInteger(0, SORT_BUTTON, OBJPROP_COLOR, is_sort_hovered ? hover_text : button_normal); //--- Update sort color ObjectSetInteger(0, SORT_BUTTON, OBJPROP_BGCOLOR, is_sort_hovered ? hover_bg : clrNONE); //--- Update sort background prev_sort_hovered = is_sort_hovered; //--- Update previous state ChartRedraw(0); //--- Redraw chart } int theme_x = (int)ObjectGetInteger(0, THEME_BUTTON, OBJPROP_XDISTANCE); //--- Get theme x int theme_y = (int)ObjectGetInteger(0, THEME_BUTTON, OBJPROP_YDISTANCE); //--- Get theme y bool is_theme_hovered = (mouse_x >= theme_x - half_size && mouse_x <= theme_x + half_size && mouse_y >= theme_y - half_size && mouse_y <= theme_y + half_size); //--- Check theme hover if (is_theme_hovered != prev_theme_hovered) { //--- Check state change ObjectSetInteger(0, THEME_BUTTON, OBJPROP_COLOR, is_theme_hovered ? theme_hover : theme_normal); //--- Update theme color ObjectSetInteger(0, THEME_BUTTON, OBJPROP_BGCOLOR, is_theme_hovered ? hover_bg : clrNONE); //--- Update theme background prev_theme_hovered = is_theme_hovered; //--- Update previous state ChartRedraw(0); //--- Redraw chart } for (int i = 0; i < num_tf_visible; i++) { //--- Loop TFs string rect_name = TF_CELL_RECT + IntegerToString(i); //--- Get TF name int tf_x = (int)ObjectGetInteger(0, rect_name, OBJPROP_XDISTANCE); //--- Get TF x int tf_y = (int)ObjectGetInteger(0, rect_name, OBJPROP_YDISTANCE); //--- Get TF y bool is_hovered = (mouse_x >= tf_x && mouse_x <= tf_x + WIDTH_TF_CELL && mouse_y >= tf_y && mouse_y <= tf_y + HEIGHT_TF_CELL); //--- Check hover if (is_hovered != prev_tf_hovered[i]) { //--- Check state change color bg = is_hovered ? clrDodgerBlue : (i == current_tf_index ? ColorStrongPositiveBg : (is_light_theme ? clrSilver : C'60,60,60')); //--- Set background ObjectSetInteger(0, rect_name, OBJPROP_BGCOLOR, bg); //--- Update background prev_tf_hovered[i] = is_hovered; //--- Update previous state ChartRedraw(0); //--- Redraw chart } } } //+------------------------------------------------------------------+ //| Update header hover state | //+------------------------------------------------------------------+ void update_header_hover_state(int mouse_x, int mouse_y) { int header_x = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_XDISTANCE); //--- Get header x int header_y = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_YDISTANCE); //--- Get header y int header_width = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_XSIZE); //--- Get header width int header_height = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_YSIZE); //--- Get header height int header_left = header_x; //--- Set left edge int header_right = header_left + header_width; //--- Set right edge int half_size = BUTTON_HOVER_SIZE / 2; //--- Compute half hover size int close_x = (int)ObjectGetInteger(0, CLOSE_BUTTON, OBJPROP_XDISTANCE); //--- Get close x int close_y = (int)ObjectGetInteger(0, CLOSE_BUTTON, OBJPROP_YDISTANCE); //--- Get close y bool in_close_area = (mouse_x >= close_x - half_size && mouse_x <= close_x + half_size && mouse_y >= close_y - half_size && mouse_y <= close_y + half_size); //--- Check close area int toggle_x = (int)ObjectGetInteger(0, TOGGLE_BUTTON, OBJPROP_XDISTANCE); //--- Get toggle x int toggle_y = (int)ObjectGetInteger(0, TOGGLE_BUTTON, OBJPROP_YDISTANCE); //--- Get toggle y bool in_toggle_area = (mouse_x >= toggle_x - half_size && mouse_x <= toggle_x + half_size && mouse_y >= toggle_y - half_size && mouse_y <= toggle_y + half_size); //--- Check toggle area int heatmap_x = (int)ObjectGetInteger(0, HEATMAP_BUTTON, OBJPROP_XDISTANCE); //--- Get heatmap x int heatmap_y = (int)ObjectGetInteger(0, HEATMAP_BUTTON, OBJPROP_YDISTANCE); //--- Get heatmap y bool in_heatmap_area = (mouse_x >= heatmap_x - half_size && mouse_x <= heatmap_x + half_size && mouse_y >= heatmap_y - half_size && mouse_y <= heatmap_y + half_size); //--- Check heatmap area int pval_x = (int)ObjectGetInteger(0, PVAL_BUTTON, OBJPROP_XDISTANCE); //--- Get pval x int pval_y = (int)ObjectGetInteger(0, PVAL_BUTTON, OBJPROP_YDISTANCE); //--- Get pval y bool in_pval_area = (mouse_x >= pval_x - half_size && mouse_x <= pval_x + half_size && mouse_y >= pval_y - half_size && mouse_y <= pval_y + half_size); //--- Check pval area int sort_x = (int)ObjectGetInteger(0, SORT_BUTTON, OBJPROP_XDISTANCE); //--- Get sort x int sort_y = (int)ObjectGetInteger(0, SORT_BUTTON, OBJPROP_YDISTANCE); //--- Get sort y bool in_sort_area = (mouse_x >= sort_x - half_size && mouse_x <= sort_x + half_size && mouse_y >= sort_y - half_size && mouse_y <= sort_y + half_size); //--- Check sort area int theme_x = (int)ObjectGetInteger(0, THEME_BUTTON, OBJPROP_XDISTANCE); //--- Get theme x int theme_y = (int)ObjectGetInteger(0, THEME_BUTTON, OBJPROP_YDISTANCE); //--- Get theme y bool in_theme_area = (mouse_x >= theme_x - half_size && mouse_x <= theme_x + half_size && mouse_y >= theme_y - half_size && mouse_y <= theme_y + half_size); //--- Check theme area bool is_header_hovered = (mouse_x >= header_left && mouse_x <= header_right && mouse_y >= header_y && mouse_y <= header_y + header_height && !in_close_area && !in_toggle_area && !in_heatmap_area && !in_pval_area && !in_sort_area && !in_theme_area); //--- Check header hover color header_bg = is_light_theme ? clrSilver : C'60,60,60'; //--- Set header background if (is_header_hovered != prev_header_hovered && !panel_dragging) { //--- Check state change ObjectSetInteger(0, HEADER_PANEL, OBJPROP_BGCOLOR, is_header_hovered ? clrRed : header_bg); //--- Update header color prev_header_hovered = is_header_hovered; //--- Update previous state ChartRedraw(0); //--- Redraw chart } update_button_hover_states(mouse_x, mouse_y); //--- Update button hovers }
Hier implementieren wir die Funktion „is_cursor_in_header_or_buttons", um festzustellen, ob sich der Mauszeiger über der Kopfzeile oder über interaktiven Elementen wie Schaltflächen oder Zeitrahmenzellen befindet, und geben einen booleschen Wert zurück. Die Chartbreite wird mit ChartGetInteger unter Verwendung von CHART_WIDTH_IN_PIXELS abgerufen, und Header-Eigenschaften wie x, y, Breite und Höhe über ObjectGetInteger mit „OBJPROP_XDISTANCE", „OBJPROP_YDISTANCE", „OBJPROP_XSIZE" und „OBJPROP_YSIZE". Wir berechnen den linken und rechten Rand der Kopfzeile und prüfen, ob die Mauskoordinaten innerhalb der Kopfzeilengrenzen liegen.
Für Schaltflächen berechnen wir die Hälfte der „BUTTON_HOVER_SIZE", holen die x- und y-Positionen jeder Schaltfläche auf die gleiche Weise und überprüfen, ob sich die Maus innerhalb der Hover-Bereiche für Schließen, Umschalten, die Heatmap, den p-Wert, Sortieren und Farbschema befindet. Für Zeitrahmenzellen initialisieren wir ein Flag auf false und ziehen eine Schleife über „num_tf_visible", wobei wir die Position jedes Rechtecks mit dem Namen aus dem Präfix „TF_CELL_RECT" und dem Index ermitteln, prüfen, ob sich die Maus innerhalb der Breite und Höhe befindet, und das Flag auf true setzen. Wir geben „true“ zurück, wenn sich das Element im Kopfbereich, auf einer Schaltfläche oder im Zeitraumbereich befindet.
Als Nächstes erstellen wir die Funktion „update_button_hover_states", um das visuelle Feedback für die Schaltflächen- und Zeitrahmen-Hover auf der Grundlage der Mausposition zu aktualisieren. Wir berechnen die Hälfte der Hover-Größe, setzen den Hover-Hintergrund auf Dodger-Blau und die normalen Farben für Schaltflächen, Farbschema und Schließen entsprechend des Farbschemas. Für jede Schaltfläche werden die Positionen ermittelt, der Schwebezustand wie oben geprüft, und wenn der Zustand von „prev_close_hovered" oder ähnlichem abweicht, wird die Farbe des Objekts mit ObjectSetInteger unter Verwendung von „OBJPROP_COLOR" und OBJPROP_BGCOLOR auf „schwebend" oder „normal" und der Hintergrund auf „schwebend" oder „keine" aktualisiert, der vorherige Zustand eingestellt und neu gezeichnet. Was die Zeitrahmen betrifft: Wir durchlaufen die sichtbaren Zellen, bilden Namen, ermitteln Positionen, prüfen, ob der Mauszeiger innerhalb der Grenzen schwebt, und wenn sich der Wert im Array „prev_tf_hovered“ geändert hat, setzen wir den Hintergrund beim Schweben auf „Dodger Blue“ oder bei Auswahl auf „Strong Positive“, andernfalls auf den normalen Themenhintergrund, aktualisieren mit „ObjectSetInteger“, setzen den vorherigen Zustand und zeichnen neu.
Anschließend definieren wir die Funktion „update_header_hover_state" zur Verwaltung der visuellen Kopfzeilen-Hover, mit Ausnahme der Schaltflächenbereiche. Wir rufen wie zuvor die Positionen und Größen der Kopfzeilen ab, berechnen die halbe Hover-Größe und prüfen die einzelnen Schaltflächenbereiche für Schließen, Umschalten, Heatmap, p-Wert, Sortieren und Farbschema. Wir stellen fest, ob der Mauszeiger in der Kopfzeile schwebt, aber nicht in einem Schaltflächenbereich, und wenn der Zustand von „prev_header_hovered" abweicht und nicht gezogen wird, aktualisieren wir den Kopfzeilenhintergrund mit „ObjectSetInteger" und „OBJPROP_BGCOLOR" auf Rot beim Schweben oder normal, setzen den vorherigen Zustand und zeichnen neu. Schließlich rufen wir „update_button_hover_states" auf, um das Überfahren von Schaltflächen gleichzeitig zu behandeln. Wir werden diese Funktionen nun im Ereignis-Handler der Chart-Interaktion verwenden. Um jedoch das Ziehen bei Mausbewegungen zu handhaben, benötigen wir eine weitere Hilfsfunktion, um die Positionen der Dashboard-Elemente zusammen mit der Maus dynamisch zu aktualisieren.
//+------------------------------------------------------------------+ //| Update panel object positions | //+------------------------------------------------------------------+ void update_panel_positions() { ObjectSetInteger(0, HEADER_PANEL, OBJPROP_XDISTANCE, panel_x); //--- Update header x ObjectSetInteger(0, HEADER_PANEL, OBJPROP_YDISTANCE, panel_y); //--- Update header y int panel_width = WIDTH_SYMBOL + num_symbols * (WIDTH_CELL - 1) + 4; //--- Compute width ObjectSetInteger(0, HEADER_PANEL, OBJPROP_XSIZE, panel_width); //--- Update header size ObjectSetInteger(0, HEADER_PANEL_ICON, OBJPROP_XDISTANCE, panel_x + 12); //--- Update icon x ObjectSetInteger(0, HEADER_PANEL_ICON, OBJPROP_YDISTANCE, panel_y + 14); //--- Update icon y ObjectSetInteger(0, HEADER_PANEL_TEXT, OBJPROP_XDISTANCE, panel_x + 105); //--- Update text x ObjectSetInteger(0, HEADER_PANEL_TEXT, OBJPROP_YDISTANCE, panel_y + 12); //--- Update text y ObjectSetInteger(0, CLOSE_BUTTON, OBJPROP_XDISTANCE, panel_x + (panel_width - 17)); //--- Update close x ObjectSetInteger(0, CLOSE_BUTTON, OBJPROP_YDISTANCE, panel_y + 14); //--- Update close y ObjectSetInteger(0, TOGGLE_BUTTON, OBJPROP_XDISTANCE, panel_x + (panel_width - 47)); //--- Update toggle x ObjectSetInteger(0, TOGGLE_BUTTON, OBJPROP_YDISTANCE, panel_y + 14); //--- Update toggle y ObjectSetInteger(0, HEATMAP_BUTTON, OBJPROP_XDISTANCE, panel_x + (panel_width - 77)); //--- Update heatmap x ObjectSetInteger(0, HEATMAP_BUTTON, OBJPROP_YDISTANCE, panel_y + 14); //--- Update heatmap y ObjectSetInteger(0, PVAL_BUTTON, OBJPROP_XDISTANCE, panel_x + (panel_width - 107)); //--- Update pval x ObjectSetInteger(0, PVAL_BUTTON, OBJPROP_YDISTANCE, panel_y + 14); //--- Update pval y ObjectSetInteger(0, SORT_BUTTON, OBJPROP_XDISTANCE, panel_x + (panel_width - 137)); //--- Update sort x ObjectSetInteger(0, SORT_BUTTON, OBJPROP_YDISTANCE, panel_y + 14); //--- Update sort y ObjectSetInteger(0, THEME_BUTTON, OBJPROP_XDISTANCE, panel_x + (panel_width - 167)); //--- Update theme x ObjectSetInteger(0, THEME_BUTTON, OBJPROP_YDISTANCE, panel_y + 14); //--- Update theme y if (!panel_minimized) { //--- Check if maximized int panel_height = HEIGHT_HEADER + HEIGHT_TF_CELL + GAP_HEIGHT + HEIGHT_RECTANGLE * (num_symbols + 1) - num_symbols + 2; //--- Compute height ObjectSetInteger(0, MAIN_PANEL, OBJPROP_XDISTANCE, panel_x); //--- Update main x ObjectSetInteger(0, MAIN_PANEL, OBJPROP_YDISTANCE, panel_y); //--- Update main y ObjectSetInteger(0, MAIN_PANEL, OBJPROP_XSIZE, panel_width); //--- Update main width ObjectSetInteger(0, MAIN_PANEL, OBJPROP_YSIZE, panel_height); //--- Update main height int tf_y = panel_y + HEIGHT_HEADER; //--- Compute TF y int tf_x_start = panel_x + 2; //--- Set TF start x for (int i = 0; i < num_tf_visible; i++) { //--- Loop TFs int x_offset = tf_x_start + i * WIDTH_TF_CELL; //--- Compute offset string rect_name = TF_CELL_RECT + IntegerToString(i); //--- Get rectangle name string text_name = TF_CELL_TEXT + IntegerToString(i); //--- Get text name ObjectSetInteger(0, rect_name, OBJPROP_XDISTANCE, x_offset); //--- Update TF rect x ObjectSetInteger(0, rect_name, OBJPROP_YDISTANCE, tf_y); //--- Update TF rect y ObjectSetInteger(0, text_name, OBJPROP_XDISTANCE, x_offset + (WIDTH_TF_CELL / 2)); //--- Update TF text x ObjectSetInteger(0, text_name, OBJPROP_YDISTANCE, tf_y + (HEIGHT_TF_CELL / 2)); //--- Update TF text y } int matrix_y = tf_y + HEIGHT_TF_CELL + GAP_HEIGHT; //--- Compute matrix y ObjectSetInteger(0, "SYMBOL_ROW_HEADER", OBJPROP_XDISTANCE, panel_x + 2); //--- Update row header x ObjectSetInteger(0, "SYMBOL_ROW_HEADER", OBJPROP_YDISTANCE, matrix_y); //--- Update row header y ObjectSetInteger(0, "SYMBOL_ROW_HEADER_TEXT", OBJPROP_XDISTANCE, panel_x + (WIDTH_SYMBOL / 2 + 2)); //--- Update row header text x ObjectSetInteger(0, "SYMBOL_ROW_HEADER_TEXT", OBJPROP_YDISTANCE, matrix_y + (HEIGHT_RECTANGLE / 2)); //--- Update row header text y for (int i = 0; i < num_symbols; i++) { //--- Loop rows int y_offset = matrix_y + HEIGHT_RECTANGLE * (i + 1) - (1 + i); //--- Compute y offset ObjectSetInteger(0, SYMBOL_ROW_RECTANGLE + IntegerToString(i), OBJPROP_XDISTANCE, panel_x + 2); //--- Update row rect x ObjectSetInteger(0, SYMBOL_ROW_RECTANGLE + IntegerToString(i), OBJPROP_YDISTANCE, y_offset); //--- Update row rect y ObjectSetInteger(0, SYMBOL_ROW_TEXT + IntegerToString(i), OBJPROP_XDISTANCE, panel_x + (WIDTH_SYMBOL / 2 + 2)); //--- Update row text x ObjectSetInteger(0, SYMBOL_ROW_TEXT + IntegerToString(i), OBJPROP_YDISTANCE, y_offset + (HEIGHT_RECTANGLE / 2 - 1)); //--- Update row text y int x_offset_col = panel_x + WIDTH_SYMBOL + i * WIDTH_CELL - i + 1; //--- Compute column x ObjectSetInteger(0, SYMBOL_COL_RECTANGLE + IntegerToString(i), OBJPROP_XDISTANCE, x_offset_col); //--- Update column rect x ObjectSetInteger(0, SYMBOL_COL_RECTANGLE + IntegerToString(i), OBJPROP_YDISTANCE, matrix_y); //--- Update column rect y ObjectSetInteger(0, SYMBOL_COL_TEXT + IntegerToString(i), OBJPROP_XDISTANCE, x_offset_col + (WIDTH_CELL / 2)); //--- Update column text x ObjectSetInteger(0, SYMBOL_COL_TEXT + IntegerToString(i), OBJPROP_YDISTANCE, matrix_y + (HEIGHT_RECTANGLE / 2)); //--- Update column text y for (int j = 0; j < num_symbols; j++) { //--- Loop columns string cell_name = CELL_RECTANGLE + IntegerToString(i) + "_" + IntegerToString(j); //--- Get cell name string text_name = CELL_TEXT + IntegerToString(i) + "_" + IntegerToString(j); //--- Get text name int x_offset = panel_x + WIDTH_SYMBOL + j * WIDTH_CELL - j + 1; //--- Compute cell x ObjectSetInteger(0, cell_name, OBJPROP_XDISTANCE, x_offset); //--- Update cell rect x ObjectSetInteger(0, cell_name, OBJPROP_YDISTANCE, y_offset); //--- Update cell rect y ObjectSetInteger(0, text_name, OBJPROP_XDISTANCE, x_offset + (WIDTH_CELL / 2)); //--- Update cell text x ObjectSetInteger(0, text_name, OBJPROP_YDISTANCE, y_offset + (HEIGHT_RECTANGLE / 2 - 1)); //--- Update cell text y } } int legend_y = panel_y + panel_height + GAP_MAIN_LEGEND; //--- Compute legend y ObjectSetInteger(0, LEGEND_PANEL, OBJPROP_XDISTANCE, panel_x); //--- Update legend x ObjectSetInteger(0, LEGEND_PANEL, OBJPROP_YDISTANCE, legend_y); //--- Update legend y ObjectSetInteger(0, LEGEND_PANEL, OBJPROP_XSIZE, panel_width); //--- Update legend width ObjectSetInteger(0, LEGEND_PANEL, OBJPROP_YSIZE, HEIGHT_LEGEND_PANEL); //--- Update legend height int total_legend_width = num_legend_visible * WIDTH_LEGEND_CELL + (num_legend_visible - 1) * LEGEND_SPACING; //--- Compute legend width int x_start = panel_x + (panel_width - total_legend_width) / 2; //--- Compute start x for (int i = 0; i < num_legend_visible; i++) { //--- Loop legends int x_offset = x_start + i * (WIDTH_LEGEND_CELL + LEGEND_SPACING); //--- Compute offset string rect_name = LEGEND_CELL_RECTANGLE + IntegerToString(i); //--- Get rectangle name string text_name = LEGEND_CELL_TEXT + IntegerToString(i); //--- Get text name ObjectSetInteger(0, rect_name, OBJPROP_XDISTANCE, x_offset); //--- Update legend rect x ObjectSetInteger(0, rect_name, OBJPROP_YDISTANCE, legend_y + 2); //--- Update legend rect y ObjectSetInteger(0, text_name, OBJPROP_XDISTANCE, x_offset + WIDTH_LEGEND_CELL / 2); //--- Update legend text x ObjectSetInteger(0, text_name, OBJPROP_YDISTANCE, legend_y + 2 + HEIGHT_LEGEND / 2 - 1); //--- Update legend text y } } ChartRedraw(0); //--- Redraw chart }
Hier implementieren wir die Funktion „update_panel_positions", um die Positionen aller Dashboard-Objekte anzupassen, wenn das Panel gezogen oder in der Größe verändert wird, um sicherzustellen, dass alles mit den aktuellen Werten „panel_x" und „panel_y" übereinstimmt. Wir aktualisieren die x- und y-Abstände der Kopfleiste und berechnen ihre Breite neu, um sie an das Symbol- und Zellenlayout anzupassen, indem wir „OBJPROP_XSIZE" entsprechend einstellen. Wir positionieren dann das Kopfzeilensymbol, den Text und die Schaltflächen wie Schließen, Umschalten, Heatmap, p-Wert, Sortieren und Farbschema neu, indem wir ihren relativen x-Versatz vom Panelrand berechnen und OBJPROP_XDISTANCE und OBJPROP_YDISTANCE" mit ObjectSetInteger" einstellen.
Wenn nicht minimiert, berechnen wir die gesamte Panelhöhe aus Konstanten und Zeilen und aktualisieren die Position, Größe und Abmessungen des Hauptpanels auf ähnliche Weise. Für die Zeile mit dem Zeitrahmen werden y und das Start-X berechnet, dann wird eine Schleife über die sichtbaren Zeitrahmen gezogen, um die X- und Y-Werte der einzelnen Rechtecke und Textbeschriftungen zu aktualisieren. Wir setzen die Matrix y, aktualisieren das Kopfzeilenrechteck der Symbole und die Textpositionen. Für Zeilensymbole berechnen wir in einer Schleife den y-Versatz und aktualisieren die x- und y-Werte der Rechtecke und Texte. Für die Spaltensymbole gilt das Gleiche für die x-Offsets und die festen y-Werte.
Für verschachtelte Zellen bilden wir Namen, berechnen Versätze und aktualisieren die Positionen von Rechtecken und Texten. Für die Legende berechnen wir ihr y unterhalb des Feldes mit einer Lücke und aktualisieren das x, y, die Breite und die Höhe des Legendenfeldes. Wir berechnen die Gesamtbreite der Legende und das Start-X für die Zentrierung und führen dann eine Schleife über die sichtbaren Elemente durch, um die X- und Y-Abstände der einzelnen Rechtecke und Texte zu aktualisieren. Schließlich wird mit ChartRedraw neu gezeichnet. Nun müssen wir die Erkennung von Mausereignissen aktivieren, damit wir sie nutzen können. Wir aktivieren dies in der Ereignisbehandlung von OnInit, indem wir den folgenden Codeschnipsel hinzufügen.
//--- Add this as the last line in ontick ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); //--- Enable mouse events
Wir verwenden einfach ChartSetInteger, um die Registrierung der Mausereignisse mit der Direktive CHART_EVENT_MOUSE_MOVE zu aktivieren. Wir können nun die Ereignislogik unseres Charts in OnChartEvent erstellen. Um dies zu erreichen, haben wir die folgende Logik angewandt.
//+------------------------------------------------------------------+ //| Handle chart event | //+------------------------------------------------------------------+ void OnChartEvent(const int event_id, const long& long_param, const double& double_param, const string& string_param) { if (event_id == CHARTEVENT_OBJECT_CLICK) { //--- Handle click event if (string_param == CLOSE_BUTTON) { //--- Check close click Print("Closing the panel now"); //--- Print closing PlaySound("alert.wav"); //--- Play sound panel_is_visible = false; //--- Hide panel delete_all_objects(); //--- Delete objects ChartRedraw(0); //--- Redraw chart } else if (string_param == TOGGLE_BUTTON) { //--- Check toggle click delete_all_objects(); //--- Delete objects panel_minimized = !panel_minimized; //--- Toggle minimized if (panel_minimized) { //--- Handle minimize Print("Minimizing the panel"); //--- Print minimizing create_minimized_dashboard(); //--- Create minimized update_borders(); //--- Update borders } else { //--- Handle maximize Print("Maximizing the panel"); //--- Print maximizing create_full_dashboard(); //--- Create full update_borders(); //--- Update borders update_tf_highlights(); //--- Update highlights update_dashboard(); //--- Update dashboard } prev_header_hovered = false; //--- Reset header hover prev_close_hovered = false; //--- Reset close hover prev_toggle_hovered = false; //--- Reset toggle hover prev_heatmap_hovered = false; //--- Reset heatmap hover prev_pval_hovered = false; //--- Reset pval hover prev_sort_hovered = false; //--- Reset sort hover prev_theme_hovered = false; //--- Reset theme hover ArrayInitialize(prev_tf_hovered, false); //--- Reset TF hovers color header_bg = is_light_theme ? clrSilver : C'60,60,60'; //--- Set header background ObjectSetInteger(0, HEADER_PANEL, OBJPROP_BGCOLOR, header_bg); //--- Update header color color button_text = is_light_theme ? clrNavy : clrGold; //--- Set button color color theme_icon_color = is_light_theme ? clrBlack : clrWhite; //--- Set theme color color close_text = is_light_theme ? clrBlack : clrWhite; //--- Set close color color header_icon_color = is_light_theme ? clrDodgerBlue : clrAqua; //--- Set icon color ObjectSetInteger(0, HEADER_PANEL_ICON, OBJPROP_COLOR, header_icon_color); //--- Update icon color ObjectSetInteger(0, CLOSE_BUTTON, OBJPROP_COLOR, close_text); //--- Update close color ObjectSetInteger(0, CLOSE_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Reset close background ObjectSetInteger(0, TOGGLE_BUTTON, OBJPROP_COLOR, button_text); //--- Update toggle color ObjectSetInteger(0, TOGGLE_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Reset toggle background if (!panel_minimized) { //--- Check if maximized ObjectSetInteger(0, HEATMAP_BUTTON, OBJPROP_COLOR, button_text); //--- Update heatmap color ObjectSetInteger(0, HEATMAP_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Reset heatmap background ObjectSetInteger(0, PVAL_BUTTON, OBJPROP_COLOR, button_text); //--- Update pval color ObjectSetInteger(0, PVAL_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Reset pval background ObjectSetInteger(0, SORT_BUTTON, OBJPROP_COLOR, button_text); //--- Update sort color ObjectSetInteger(0, SORT_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Reset sort background ObjectSetInteger(0, THEME_BUTTON, OBJPROP_COLOR, theme_icon_color); //--- Update theme color ObjectSetInteger(0, THEME_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Reset theme background } ChartRedraw(0); //--- Redraw chart } else if (string_param == HEATMAP_BUTTON) { //--- Check heatmap click global_display_mode = (global_display_mode == MODE_STANDARD ? MODE_HEATMAP : MODE_STANDARD); //--- Toggle mode string new_icon = CharToString(global_display_mode == MODE_STANDARD ? (uchar)82 : (uchar)110); //--- Set new icon ObjectSetString(0, HEATMAP_BUTTON, OBJPROP_TEXT, new_icon); //--- Update icon Print("Switching to ", (global_display_mode == MODE_HEATMAP ? "Heatmap" : "Standard"), " mode"); //--- Print switch recreate_legend(); //--- Recreate legend update_dashboard(); //--- Update dashboard ChartRedraw(0); //--- Redraw chart } else if (string_param == PVAL_BUTTON) { //--- Check pval click global_view_mode = (global_view_mode == VIEW_CORR ? VIEW_PVAL : VIEW_CORR); //--- Toggle view Print("Switching to ", (global_view_mode == VIEW_PVAL ? "P-Value" : "Correlation"), " view"); //--- Print switch update_dashboard(); //--- Update dashboard ChartRedraw(0); //--- Redraw chart } else if (string_param == SORT_BUTTON) { //--- Check sort click cycle_sort_mode(); //--- Cycle mode string new_sort_icon; //--- Declare new icon if (sort_mode == 0) new_sort_icon = CharToString('N'); //--- Set neutral else if (sort_mode == 1) new_sort_icon = CharToString('K'); //--- Set descending else new_sort_icon = CharToString('J'); //--- Set ascending ObjectSetString(0, SORT_BUTTON, OBJPROP_TEXT, new_sort_icon); //--- Update icon delete_all_objects(); //--- Delete objects create_full_dashboard(); //--- Create full update_borders(); //--- Update borders update_dashboard(); //--- Update dashboard ChartRedraw(0); //--- Redraw chart } else if (string_param == THEME_BUTTON) { //--- Check theme click toggle_theme(); //--- Toggle theme ChartRedraw(0); //--- Redraw chart } else { //--- Handle other clicks for (int i = 0; i < num_tf_visible; i++) { //--- Loop TFs if (string_param == TF_CELL_TEXT + IntegerToString(i)) { //--- Check TF click switch_timeframe(i); //--- Switch timeframe ChartRedraw(0); //--- Redraw chart break; //--- Exit loop } } } } else if (event_id == CHARTEVENT_MOUSE_MOVE && panel_is_visible) { //--- Handle mouse move int mouse_x = (int)long_param; //--- Get mouse x int mouse_y = (int)double_param; //--- Get mouse y int mouse_state = (int)string_param; //--- Get mouse state if (mouse_x == last_mouse_x && mouse_y == last_mouse_y && !panel_dragging) return; //--- Skip if unchanged last_mouse_x = mouse_x; //--- Update last x last_mouse_y = mouse_y; //--- Update last y update_header_hover_state(mouse_x, mouse_y); //--- Update header hover int header_x = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_XDISTANCE); //--- Get header x int header_y = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_YDISTANCE); //--- Get header y int header_width = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_XSIZE); //--- Get header width int header_height = (int)ObjectGetInteger(0, HEADER_PANEL, OBJPROP_YSIZE); //--- Get header height int header_left = header_x; //--- Set left edge int header_right = header_left + header_width; //--- Set right edge int half_size = BUTTON_HOVER_SIZE / 2; //--- Compute half size int close_x = (int)ObjectGetInteger(0, CLOSE_BUTTON, OBJPROP_XDISTANCE); //--- Get close x int close_y = (int)ObjectGetInteger(0, CLOSE_BUTTON, OBJPROP_YDISTANCE); //--- Get close y bool in_close_area = (mouse_x >= close_x - half_size && mouse_x <= close_x + half_size && mouse_y >= close_y - half_size && mouse_y <= close_y + half_size); //--- Check close area int toggle_x = (int)ObjectGetInteger(0, TOGGLE_BUTTON, OBJPROP_XDISTANCE); //--- Get toggle x int toggle_y = (int)ObjectGetInteger(0, TOGGLE_BUTTON, OBJPROP_YDISTANCE); //--- Get toggle y bool in_toggle_area = (mouse_x >= toggle_x - half_size && mouse_x <= toggle_x + half_size && mouse_y >= toggle_y - half_size && mouse_y <= toggle_y + half_size); //--- Check toggle area int heatmap_x = (int)ObjectGetInteger(0, HEATMAP_BUTTON, OBJPROP_XDISTANCE); //--- Get heatmap x int heatmap_y = (int)ObjectGetInteger(0, HEATMAP_BUTTON, OBJPROP_YDISTANCE); //--- Get heatmap y bool in_heatmap_area = (mouse_x >= heatmap_x - half_size && mouse_x <= heatmap_x + half_size && mouse_y >= heatmap_y - half_size && mouse_y <= heatmap_y + half_size); //--- Check heatmap area int pval_x = (int)ObjectGetInteger(0, PVAL_BUTTON, OBJPROP_XDISTANCE); //--- Get pval x int pval_y = (int)ObjectGetInteger(0, PVAL_BUTTON, OBJPROP_YDISTANCE); //--- Get pval y bool in_pval_area = (mouse_x >= pval_x - half_size && mouse_x <= pval_x + half_size && mouse_y >= pval_y - half_size && mouse_y <= pval_y + half_size); //--- Check pval area int sort_x = (int)ObjectGetInteger(0, SORT_BUTTON, OBJPROP_XDISTANCE); //--- Get sort x int sort_y = (int)ObjectGetInteger(0, SORT_BUTTON, OBJPROP_YDISTANCE); //--- Get sort y bool in_sort_area = (mouse_x >= sort_x - half_size && mouse_x <= sort_x + half_size && mouse_y >= sort_y - half_size && mouse_y <= sort_y + half_size); //--- Check sort area int theme_x = (int)ObjectGetInteger(0, THEME_BUTTON, OBJPROP_XDISTANCE); //--- Get theme x int theme_y = (int)ObjectGetInteger(0, THEME_BUTTON, OBJPROP_YDISTANCE); //--- Get theme y bool in_theme_area = (mouse_x >= theme_x - half_size && mouse_x <= theme_x + half_size && mouse_y >= theme_y - half_size && mouse_y <= theme_y + half_size); //--- Check theme area if (prev_mouse_state == 0 && mouse_state == 1) { //--- Check drag start if (mouse_x >= header_left && mouse_x <= header_right && mouse_y >= header_y && mouse_y <= header_y + header_height && !in_close_area && !in_toggle_area && !in_heatmap_area && !in_pval_area && !in_sort_area && !in_theme_area) { //--- Check draggable area panel_dragging = true; //--- Start dragging panel_drag_x = mouse_x; //--- Set drag x panel_drag_y = mouse_y; //--- Set drag y panel_start_x = header_x; //--- Set start x panel_start_y = header_y; //--- Set start y ObjectSetInteger(0, HEADER_PANEL, OBJPROP_BGCOLOR, clrMediumBlue); //--- Set drag color ChartSetInteger(0, CHART_MOUSE_SCROLL, false); //--- Disable scroll } } if (panel_dragging && mouse_state == 1) { //--- Handle dragging int dx = mouse_x - panel_drag_x; //--- Compute x delta int dy = mouse_y - panel_drag_y; //--- Compute y delta panel_x = panel_start_x + dx; //--- Update panel x panel_y = panel_start_y + dy; //--- Update panel y int chart_width = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); //--- Get chart width int chart_height = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS); //--- Get chart height int panel_width = WIDTH_SYMBOL + num_symbols * (WIDTH_CELL - 1) + 4; //--- Compute panel width int panel_height = HEIGHT_HEADER + HEIGHT_TF_CELL + GAP_HEIGHT + HEIGHT_RECTANGLE * (num_symbols + 1) - num_symbols + 2; //--- Compute height int total_height = panel_height + GAP_MAIN_LEGEND + HEIGHT_LEGEND_PANEL; //--- Compute total height panel_x = MathMax(0, MathMin(chart_width - panel_width, panel_x)); //--- Clamp x panel_y = MathMax(0, MathMin(chart_height - (panel_minimized ? HEIGHT_HEADER : total_height), panel_y)); //--- Clamp y update_panel_positions(); //--- Update positions ChartRedraw(0); //--- Redraw chart } if (mouse_state == 0 && prev_mouse_state == 1) { //--- Check drag end if (panel_dragging) { //--- Check was dragging panel_dragging = false; //--- Stop dragging update_header_hover_state(mouse_x, mouse_y); //--- Update hover ChartSetInteger(0, CHART_MOUSE_SCROLL, true); //--- Enable scroll ChartRedraw(0); //--- Redraw chart } } prev_mouse_state = mouse_state; //--- Update previous state } }
In der Ereignisbehandlung von OnChartEvent für CHARTEVENT_OBJECT_CLICK prüfen wir den Schaltflächennamen in „string_param": Lautet er „CLOSE_BUTTON", drucken wir eine Meldung, spielen einen Warnton mit PlaySound, setzen „panel_is_visible" auf false, rufen „delete_all_objects" auf und zeichnen mit „ChartRedraw" neu; ist er „TOGGLE_BUTTON" werden Objekte gelöscht, „panel_minimized" umgeschaltet und, falls minimiert, gedruckt, ein minimiertes Dashboard erstellt und die Ränder aktualisiert; andernfalls, falls maximiert, gedruckt, ein vollständiges Dashboard erstellt und die Ränder, Hervorhebungen und das Dashboard aktualisiert. Wir setzen alle vorherigen Hover-Zustände auf false zurück, einschließlich des Arrays mit ArrayInitialize für Timeframes, setzen den thematischen Kopfzeilen-Hintergrund und wenden ihn auf das Kopfzeilen-Panel an, aktualisieren die Farben für Icon, Close, Toggle und, falls nicht minimiert, für Heatmap, p-Wert, Sortierung und die Schaltfläche für das Farbschema, setzen deren Hintergründe auf none zurück und zeichnen dann neu.
Für CHARTEVENT_MOUSE_MOVE, wenn „panel_is_visible" wahr ist, setzen wir „long_param" auf Maus x, „double_param" auf y und „string_param" auf state. Wenn die Koordinaten mit „last_mouse_x" und „last_mouse_y" übereinstimmen und nicht gezogen werden, verlassen wir das Programm vorzeitig; andernfalls aktualisieren wir die letzten Positionen und rufen „update_header_hover_state" mit x und y auf. Wir rufen Header-Eigenschaften für Bounds und Button-Positionen ab, berechnen die halbe Hover-Größe, und prüfen Bereiche für Close, Toggle, Heatmap, p-Wert, Sortierung und Farbschema. Wenn der vorherige Zustand null war und der aktuelle eins ist und sich die Maus in der Kopfzeile, aber nicht in den Schaltflächenbereichen befindet, beginnen Sie mit dem Ziehen, indem Sie „panel_dragging" auf true setzen, die Zieh- und Startkoordinaten speichern, den Hintergrund der Kopfzeile auf mittelblau setzen und den Mauslauf im Chart deaktivieren.
Wenn das Ziehen aktiv ist und der Status „1“ ist, berechnen wir die Deltas, aktualisieren „panel_x“ und „panel_y“ ausgehend vom Startwert plus Deltas, rufen die Chart-Abmessungen mit ChartGetInteger für Breite und Höhe in Pixeln ab, berechnen die Panel-Abmessungen sowie die Gesamthöhe einschließlich Legende und begrenzen „panel_x“ und „panel_y“ mithilfe von MathMax und MathMin, um innerhalb der Chartgröße abzüglich der Panelgröße oder der Kopfzeile zu bleiben, falls minimiert, rufen dann „update_panel_positions“ auf und zeichnen alles neu. Wenn der Status null ist und zuvor eins war und gerade ein Ziehen stattfand, dann wird „panel_dragging“ beendet, „update_header_hover_state“ aufgerufen, das Scrollen aktiviert und die Anzeige neu gezeichnet. Schließlich wird „prev_mouse_state" auf den aktuellen Zustand aktualisiert. Wenn das erledigt ist, müssen wir das Dashboard konsequent aktualisieren, da wir keine Aktualisierungen aufrufen müssen, wenn das Dashboard geschlossen oder minimiert ist. Das wäre sinnlos. In der Ereignisbehandlung von Tick-Ereignissen verwenden wir also die folgende Logik.
//+------------------------------------------------------------------+ //| Handle tick event | //+------------------------------------------------------------------+ void OnTick() { if (panel_is_visible && !panel_minimized) { //--- Check if update needed update_dashboard(); //--- Update on tick } }
Wir ändern die Ereignisbehandlung in OnTick, um das Dashboard nur dann zu aktualisieren, wenn dies aus Effizienzgründen erforderlich ist. Es wird nun geprüft, ob „panel_is_visible" wahr und nicht „panel_minimized" ist, und wenn ja, wird „update_dashboard" aufgerufen, um Korrelationen, Grafiken und andere Elemente bei jedem neuen Tick neu zu berechnen und zu aktualisieren. Zum Schluss, wenn wir das Programm schließen, wollen wir unsere Dashboard-Komponenten löschen, um das Chart zu bereinigen und die Chart-Ereignisse zu deaktivieren.
//+------------------------------------------------------------------+ //| Deinitialize expert | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { delete_all_objects(); //--- Delete objects ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false); //--- Disable mouse events ChartRedraw(0); //--- Redraw chart }
In der Ereignisbehandlung von OnDeinit fügen wir eine Logik zum Aufräumen der Ressourcen hinzu, wenn das Programm entfernt oder deinitialisiert wird. Sie ruft „delete_all_objects" auf, um alle grafischen Elemente aus dem Chart zu entfernen, deaktiviert Mausbewegungsereignisse im Chart mit ChartSetInteger unter Verwendung von CHART_EVENT_MOUSE_MOVE, das auf false gesetzt ist, und zeichnet das Chart über ChartRedraw neu, um die Änderungen wiederzugeben. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

Anhand der Visualisierung können wir sehen, dass wir das Dashboard der Korrelationsmatrix mit allen durchgeführten Interaktionen verbessert und somit unsere Ziele erreicht haben. Nun bleibt nur noch, die Funktionsfähigkeit des Systems zu testen, was im vorangegangenen Abschnitt behandelt wurde.
Backtests
Wir haben die Tests durchgeführt, und unten sehen Sie die kompilierte Visualisierung in einem einzigen Bild im Bitmap-Format Graphics Interchange Format (GIF).

Schlussfolgerung
Abschließend haben wir das Dashboard der Korrelationsmatrix in MQL5 um interaktive Funktionen erweitert, darunter das Ziehen und Minimieren des Panels über Mausereignisse, Hover-Effekte für visuelles Feedback, die Sortierung von Symbolen nach Korrelationsstärke in auf- und absteigenden Modi, das Umschalten zwischen den Darstellungen der Korrelationen und der p-Werte, das Umschalten zwischen hellem und dunklem Farbschema mit dynamischen Farbaktualisierungen und Tooltips über den Zellen für detaillierte Einblicke. Das System unterstützt jetzt ereignisgesteuerte Reaktionen zur Verbesserung der Nutzerfreundlichkeit, mit Erkennung von Schwebezuständen, Begrenzung während des Ziehens, um innerhalb der Chartgrenzen zu bleiben, und effizienten Aktualisierungen zur Aufrechterhaltung der Leistung. Mit diesem interaktiven Korrelationsmatrix-Dashboard sind Sie in der Lage, die Interdependenzen von Vermögenswerten dynamischer zu analysieren und Ihre Handelsaktivitäten weiter zu optimieren. Viel Spaß beim Handeln!
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20962
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.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Klassische Strategien neu interpretieren (Teil 21): Entdeckung einer Ensemble-Strategie aus Bollinger-Bändern und RSI
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Entwicklung eines Toolkits zur Preisaktionsanalyse (Teil 55): Entwurf eines Overlays der CPI-Minikerzen für Intra-Bar-Druck
- 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.
Haben Sie eine Idee, warum die Symbole der Schaltflächen nicht angezeigt werden, es werden nur kleine Rechtecke angezeigt.
Wissen Sie, warum die Schaltflächensymbole nicht angezeigt werden, sondern nur kleine Rechtecke?