
Ein manuelles Chart- und Handelswerkzeug (Teil II). Werkzeuge zum Zeichnen von Chart-Grafiken
Einführung
Das Ziel meines vorherigen Artikels war es, ein bequemes Toolkit zu erstellen, das es erlaubt, schnell gerade Linien auf Charts mit Hilfe von Tastaturkürzeln zu zeichnen. Der erste Artikel enthält ein Video, das zeigt, wie die fertige Lösung funktioniert.
In der aktuellen Implementierung gibt es keine GUI (obwohl sie für die Zukunft geplant ist). Das Programm zeichnet einfach Linien basierend auf dem Tastaturkürzel. Das beschleunigt den Zugriff auf solche Aktionen wie das Ändern des aktuellen Chart-"Levels" (Z-Index), das Umschalten von Zeitrahmen und das Umschalten des Modus zum Zeichnen von geraden Linien (Strahl/Segment).
Die Mausposition bestimmt die Stelle, an der die Objekte gezeichnet werden sollen. Befindet sich der Zeiger oberhalb des Kurses, werden die Hochs der Kerzen als Basispunkte gewählt. Befindet sich der Zeiger unterhalb des Kurses, werden die Tiefstpreise verwendet.
In der aktuellen Version der Bibliothek können folgende Objekte gezeichnet werden:
- Einfache ("endlose") Geraden — horizontale und vertikale Linien.
- Normale Trendlinien (durch zwei Extrempunkte, die der Maus am nächsten sind). Sie können einstellen, dass die Linie als Segment oder als Strahl gezeichnet werden soll. Wenn die Linie ein Segment ist, erlaubt ein spezieller Modus, ihr Ende auf einen Punkt in der Zukunft zu setzen. In diesem Fall ist die Größe der Linie gleich dem Abstand zwischen den Extremen multipliziert mit einem bestimmten Koeffizienten, der in den EA-Parametern angegeben werden kann.
- Horizontale Linien mit einer bestimmten Länge (nicht endlos). Das Toolkit kann kurze und "verlängerte" Linien zeichnen, für die Sie ein Verhältnis relativ zur kurzen Linie angeben.
- Eine vertikale Linie mit Ebenenbeschriftungen.
- Fibonacci-Fächer (Fan). Die Level-Parameter sind konfigurierbar, aber ich verwende eine leicht modifizierte Version, die einst auf "Onyx" von einem Mann mit dem Spitznamen Vadimcha gezeigt wurde. Er nannte diesen Fächer VFan, welchen Namen ich in meinem Code weiter verwende.
- Andrews' Pitchfork besteht aus drei Objekten.
Die Projektstruktur ist recht einfach. Die Bibliothek hat fünf zusammengehörige Dateien: "GlobalVariables.mqh", "Graphics.mqh", "Mouse.mqh", "Shortcuts.mqh", "Utilites.mqh". Alle Dateien befinden sich in einem Ordner Shortcuts im Standardverzeichnis Include.
Die Hauptdatei ist "Shortcuts.mqh", mit der alle anderen Dateien verbunden sind. In dieser Datei wird eine Instanz der Klasse CShortcuts erzeugt, die ein einfaches Einbinden der Bibliothek an Ihren Hauptexpert Advisor ermöglicht.
Im vorherigen Artikel habe ich mich auf die Hilfsdatei "Utilites.mqh" konzentriert. In diesem Artikel werden wir uns hauptsächlich mit der Datei "Graphics.mqh" beschäftigen, die die Zeichenlogik enthält.
Globale Einstellungsdatei
Die zweite Bibliotheksversion bietet deutlich erweiterte Konfigurationsmöglichkeiten, da sie mehr Objekte hat, die beeinflusst werden können. Der vollständige Code der aktuellen Version lautet wie folgt:
//+------------------------------------------------------------------+ //| GlobalVariables.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://www.mql5.com/de/articles/7908 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://www.mql5.com/de/articles/7908" //+------------------------------------------------------------------+ //| File describing parameters available to the user | //+------------------------------------------------------------------+ #define VERSION 2.0 // Not #property but a constant. A bit more convenient. //+------------------------------------------------------------------+ //| Key settings | //+------------------------------------------------------------------+ input string Keys="=== Key settings ==="; input string Up_Key="U"; // Switch timeframe up input string Down_Key="D"; // Switch timeframe down input string Trend_Line_Key="T"; // Trend line input string Switch_Trend_Ray_Key="R"; // Indication of a trend line ray input string Z_Index_Key="Z"; // Indication of the chart on top input string Vertical_With_Short_Levels_Key="V"; // Vertical segment input string Short_Level_Key="S"; // Short level input string Long_Level_Key="L"; // Extended level input string Simple_Horizontal_Line_Key="H"; // Simple horizontal line input string Simple_Vertical_Line_Key="I"; // Simple vertical line input string VFun_Key="F"; // Fibonacci fan input string Pitchfork_Key="P"; // Andrews' pitchfork //+------------------------------------------------------------------+ //| Color setting | //+------------------------------------------------------------------+ input string Colors="=== Color Settings ==="; input color VFan_Color=clrLightGray; // Color of the fan lines // (and an additional // one for special cases) //--- input color Pitchfork_Main_Color = clrBlue; // Andrews' pitchfork color input color Pitchfork_Shiff_Color = clrRed; // Schiff pitchfork color input color Pitchfork_Reverce_Color = clrYellow;// "Reverse" pitchfork color //+------------------------------------------------------------------+ //| Size settings | //+------------------------------------------------------------------+ input string Dimensions="=== Size settings ==="; input int Short_Level_Length=12; // Short level length (bar) input int Short_Level_Width=1; // Line width for the short level input int Long_Level_Width=2; // Line width for the long level input int Vertical_With_Short_Levels_Width=1; // Vertical line width with levels input int Short_Level_7_8_Width=1; // Level 7/8 line width input int Short_Level_14_8_Width=1; // Level 14/8 line width input int Simple_Vertical_Width=1; // Simple vertical line width input int Simple_Horizontal_Width=1; // Simple horizontal line width input int Trend_Line_Width=2; // Trend line width //+------------------------------------------------------------------+ //| Display styles | //+------------------------------------------------------------------+ input string Styles="=== Display styles ==="; input ENUM_LINE_STYLE Vertical_With_Short_Levels_Style=STYLE_SOLID; // Vertical Line style // with levels input ENUM_LINE_STYLE Short_Level_Style=STYLE_SOLID; // Short Level style input ENUM_LINE_STYLE Long_Level_Style=STYLE_SOLID; // Long level style input ENUM_LINE_STYLE Short_Level_7_8_Style=STYLE_SOLID; // Level 7/8 style input ENUM_LINE_STYLE Short_Level_14_8_Style=STYLE_DOT; // Level 14/8 style input ENUM_LINE_STYLE Simple_Vertical_Style=STYLE_DOT; // Simple Vertical style input ENUM_LINE_STYLE Simple_Horizontal_Style=STYLE_DOT; // Simple Horizontal style input ENUM_LINE_STYLE VFun_Levels_Style=STYLE_SOLID; // Fan Style input ENUM_LINE_STYLE Trend_Line_Style=STYLE_SOLID; // Trend line style //--- input ENUM_LINE_STYLE Pitchfork_Main_Style = STYLE_SOLID; // Andrews' Pitchfork style input ENUM_LINE_STYLE Pitchfork_Shiff_Style = STYLE_SOLID; // Schiff Pitchfork style input ENUM_LINE_STYLE Pitchfork_Reverce_Style = STYLE_SOLID; // Reverse Pitchfork style //+------------------------------------------------------------------+ //| Pitchfork extrema parameters | //+------------------------------------------------------------------+ input string Pitchforks="=== Pitchfork Extrema Parameters ==="; //--- input int Pitchfork_First_Point_Left_Bars=6; // Pitchfork - 1st point, bars on the left input int Pitchfork_First_Point_Right_Bars=6; // Pitchfork - 1st point, bars on the left //--- input int Pitchfork_Second_Point_Left_Bars=6; // Pitchfork - 2nd point, bars on the left input int Pitchfork_Second_Point_Right_Bars=6; // Pitchfork - 2nd point, bars on the right //--- input int Pitchfork_Third_Point_Left_Bars=6; // Pitchfork - 3rd point, bars on the left input int Pitchfork_Third_Point_Right_Bars=2; // Pitchfork - 3rd point, bars on the right //+------------------------------------------------------------------+ //| Other parameters | //+------------------------------------------------------------------+ input string Others="=== Other Parameters ==="; input double Vertical_Short_Level_Coefficient=0.825; // Coefficient of vertical levels input double Long_Level_Multiplicator=2; // Multiplier for the long level input int Trend_Length_Coefficient=4; // Coefficient for the trend line length input bool Is_Trend_Ray=false; // Trend line - ray input bool Is_Change_Timeframe_On_Create = true; // Hide objects on higher timeframes? // (true - hide, false - show) input bool Is_Select_On_Create=true; // Select upon creation input bool Is_Different_Colors=true; // Change colors for times // Number of bars on the left and on the right // for trend line and fan extreme points input int Fractal_Size_Left=1; // Size of the left fractal input int Fractal_Size_Right=1; // Size of the right fractal input bool Pitchfork_Show_Main = true; // Display Andrews' pitchfork input bool Pitchfork_Show_Shiff = true; // Display Schiff pitchfork input bool Pitchfork_Show_Reverce = true; // Display "Reverse" pitchfork input bool Print_Warning_Messages=true; // Display error messages input string VFun_Levels="-1.5,-0.618,-0.236,"+ " 0,0.236,0.382,"+ " 0.618,0.786,0.886,0.942"; // Fan levels input string Array_Delimiter=","; // Array elements separator //--- //+------------------------------------------------------------------+ //| Name prefixes of drawn shapes (can be change only in code, | //| not visible in EA parameters) | //+------------------------------------------------------------------+ //string Prefixes="=== Prefixes ==="; //string Vertical_With_Short_Levels_Prefix="Vertical_"; // Prefix for vertical lines with levels //string Short_Level_Prefix="Short_Level_"; // Prefix for short levels //string Long_Level_Prefix="Long_Level_"; // Prefix for long levels //string Simple_Horizontal_Prefix="Simple_H_"; // Prefix for simple horizontal lines //string Simple_Vertical_Prefix="Simple_V_"; // Prefix for simple vertical lines //string VFan_Prefix="VFan_"; // Prefix for fan //string Trend_Line_Prefix="Trend_"; // Prefix for trend lines //string Pitchfork_Prefix="Pitchfork_"; // Prefix for pitchfork string allPrefixes[] = // Prefixes for object names { "Trend_", // 0 - Prefix for trend lines "Simple_H_", // 1 - Prefix for simple horizontal lines "Simple_V_", // 2 - Prefix for simple vertical lines "VFan_", // 3 - Prefix for fan "Pitchfork_", // 4 - Prefix for pitchfork "Vertical_", // 5 - Prefix for vertical lines with levels "Short_Level_", // 6 - Prefix for short levels "Long_Level_" // 7 - Prefix for long levels }; //+------------------------------------------------------------------+ //| Colors for objects of one timeframe (can be changed only in code,| //| not visible in EA parameters) | //+------------------------------------------------------------------+ // string TimeframeColors="=== Standard Colors for Timeframes ==="; color mn1_color=clrCrimson; color w1_color=clrDarkOrange; color d1_color=clrGoldenrod; color h4_color=clrLimeGreen; color h1_color=clrLime; color m30_color=clrDeepSkyBlue; color m15_color=clrBlue; color m5_color=clrViolet; color m1_color=clrDarkViolet; color common_color=clrGray; //--- Auxiliary constant for displaying error messages #define DEBUG_MESSAGE_PREFIX "=== ",__FUNCTION__," === " //--- Constants for describing the main timeframes when drawing //--- For compatibility with version 4, timeframes that do not exist //--- in the toolbar are excluded. #define PERIOD_LOWER_M5 OBJ_PERIOD_M1|OBJ_PERIOD_M5 #define PERIOD_LOWER_M15 PERIOD_LOWER_M5|OBJ_PERIOD_M15 #define PERIOD_LOWER_M30 PERIOD_LOWER_M15|OBJ_PERIOD_M30 #define PERIOD_LOWER_H1 PERIOD_LOWER_M30|OBJ_PERIOD_H1 #define PERIOD_LOWER_H4 PERIOD_LOWER_H1|OBJ_PERIOD_H4 #define PERIOD_LOWER_D1 PERIOD_LOWER_H4|OBJ_PERIOD_D1 #define PERIOD_LOWER_W1 PERIOD_LOWER_D1|OBJ_PERIOD_W1 //+------------------------------------------------------------------+
Alle Neuerungen gegenüber der Vorgängerversion sind in gelb hervorgehoben. Mit diesen Neuerungen können nicht nur gerade Linien, sondern auch andere Objekte, die auf dem Bildschirm erscheinen, konfiguriert werden.
Ich habe die Namen der Objektpräfixe in ein Array gelegt, damit es bequemer ist, sie später zu verwenden. Ich plane zum Beispiel, eine Funktion zum Löschen komplexer Objekte hinzuzufügen (z. B. vertikale Linien mit Levels). Das Array wird für solche Fälle einfacher sein.
Nachdem wir nun die Einstellungen besprochen haben, können wir mit der Grafik fortfahren.
Zeichnen von "Primitiven": vertikale und horizontale Linien
Das erste Objekt, das Sie vielleicht erstellen möchten, sind die Level- und Zeitlinien (unendliche horizontale und vertikale Linien). Eigentlich hat die Bibliothek mit diesen Linien angefangen.
Hier ist der Code:
//+------------------------------------------------------------------+ //| Draws simple straight lines (vertical and horizontal) in the | //| position specified by mouse or parameters | //| Parameters: | //| _object_type - object type. Can be OBJ_VLINE or OBJ_HLINE | //| _time - time. If not specified, mouse time is used | //| _price - price. If not specified, price under index is used. | //+------------------------------------------------------------------+ void CGraphics::DrawSimple( ENUM_OBJECT _object_type, // Object type datetime _time=-1, // Time double _price=-1 // Price ) { //--- string Current_Object_Name; // The name of the future object color Current_Object_Color= // Color (depends on the "standard" color of the timeframe) CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()); datetime Current_Object_Time; // Starting point time double Current_Object_Price; // Starting point price ENUM_LINE_STYLE Current_Object_Style=STYLE_DOT; // Line style int Current_Object_Width=1; // Line width int window=0; // Subwindow number //--- Set up line parameters depending on the type if(_object_type==OBJ_VLINE) // For vertical lines { Current_Object_Name= // Generate the name CUtilites::GetCurrentObjectName( Simple_Vertical_Prefix, _object_type ); // style - according to global parameters Current_Object_Style=Simple_Vertical_Style; // width - according to global parameters Current_Object_Width=Simple_Vertical_Width; } else if(_object_type==OBJ_HLINE)// For horizontal lines { Current_Object_Name= // Generate the name CUtilites::GetCurrentObjectName( Simple_Horizontal_Prefix, _object_type ); // style - according to global parameters Current_Object_Style=Simple_Horizontal_Style; // width - according to global parameters Current_Object_Width=Simple_Horizontal_Width; } else // This function only draws horizontal and vertical lines. { // If something else is passed in parameters... if(Print_Warning_Messages) { // ...report an error... Print(DEBUG_MESSAGE_PREFIX,"Error, wrong object type"); } // ...and exit. return; } //--- If coordinates are not specified in the parameters, use the coordinates of the mouse Current_Object_Price = _price==-1 ? CMouse::Price() : _price; Current_Object_Time = _time==-1 ? CMouse::Time() : _time; //--- Create the object ObjectCreate( 0, Current_Object_Name, _object_type, 0, Current_Object_Time, Current_Object_Price ); //--- Set display parameters for the created object CurrentObjectDecorate( Current_Object_Name, Current_Object_Color, Current_Object_Width, Current_Object_Style ); //--- Redraw the chart and complete ChartRedraw(0); }
Die Operationen sind sehr einfach. Erzeugen Sie einen Namen, nehmen Sie die Einstellungen aus den Eingabe-Variablen, die in der Datei "GlobalVariables.mqh" beschrieben sind, holen Sie die Koordinaten des Startpunkts des Objekts (entweder aus den Funktionsparametern oder einfach über die Koordinaten der Maus) und das Objekt ist fertig.
Das ist alles!
Jetzt müssen wir diese Funktion in den Dateikopf einfügen
//+------------------------------------------------------------------+ //| Graphics.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://www.mql5.com/de/articles/7468 | //+------------------------------------------------------------------+ // ... //+------------------------------------------------------------------+ //| Class for plotting graphic objects | //+------------------------------------------------------------------+ class CGraphics { // ... public: // ... //--- Draws simple straight lines (vertical and horizontal) void CGraphics::DrawSimple( ENUM_OBJECT _object_type, // Object type datetime _time=-1, // Time double _price=-1 // Price ) // ... } ; //+------------------------------------------------------------------+
Hier auch die Handhabung der zugehörigen Tasten:
//+------------------------------------------------------------------+ //| Shortcuts.mqh | //+------------------------------------------------------------------+ // ... //+------------------------------------------------------------------+ //| Event handling function | //+------------------------------------------------------------------+ void CShortcuts::OnChartEvent( const int id, const long &lparam, const double &dparam, const string &sparam ) { //--- int window = 0; //--- switch(id) { // ... //--- Handle keystrokes case CHARTEVENT_KEYDOWN: // ... //--- Draw a simple vertical line if(CUtilites::GetCurrentOperationChar(Simple_Vertical_Line_Key) == lparam) { m_graphics.DrawSimple(OBJ_VLINE); } //--- Draw a simple horizontal line if(CUtilites::GetCurrentOperationChar(Simple_Horizontal_Line_Key) == lparam) { m_graphics.DrawSimple(OBJ_HLINE); } // ... break; //--- } }
In Zukunft werde ich, um den Bildschirmplatz zu sparen und mich auf das Wesentliche zu konzentrieren, beim Hinzufügen von Funktionsbeschreibungen keine Kopfzeileneinträge schreiben, sondern entsprechende Zeilen (gelb hervorgehoben) für neu hinzugefügte Befehle einblenden.
Das Ergebnis aller Ergänzungen und Kompilierungen ist sehr einfach: zwei Befehle, die grafische Primitive an beliebiger Stelle im aktuellen Fenster zeichnen:
Die Standard-Tastenkombinationen für diese Linien sind "I" (i) und "H" (h).
Vergessen Sie nicht, dass die Farbe der erstellten Objekte je nach aktuellem Zeitrahmen unterschiedlich sein wird. Und Objekte aus niedrigeren Zeitrahmen werden in höheren nicht angezeigt (mit Standardeinstellungen).
Aus Gründen der Kompatibilität mit MQL4 verwenden wir nur Zeitrahmen aus der Standardsymbolleiste, die standardmäßig angezeigt werden. Diese Zeitrahmen können ausgewählt werden, wenn Sie mit den Tasten "U" und "D" durch sie blättern (durch Drücken dieser Tasten wird die Chart-Periode um einen Zeitrahmen nach oben oder unten geändert; siehe die Funktion CUtilites::ChangeTimeframes).
VFun, oder Fibonacci-Fächer
Die nächste Form ist der Fibonacci-Fächer. Ich verwende ihn recht häufig. Aber es war sehr lästig, sich jedes Mal alle seine Strahlen zu merken, wenn ich ein anderes Terminal benutzen musste. Deshalb beschloss ich, dieses Werkzeug zu meinem wunderbaren EA hinzuzufügen.
Ich entwickelte die Idee weiter und beschloss, eine universelle Funktion zu implementieren, die Fibonacci-Levels für jedes Objekt (Fächerkanal oder horizontale Fibo-Levels) setzen kann, das mit der Bibliothek gezeichnet wurde. Hier ist die Funktion.
//+------------------------------------------------------------------+ //| Sets level values and form in any Fibonacci object | //| Uses colors and styles from the class fields | //| Parameters: | //| _object_name - the name of the Fibonacci object | //| _levels_values[] - array of level values | //+------------------------------------------------------------------+ void CGraphics::SetFiboLevels( string _object_name, // Object name const double &_levels_values[] // Array of values ) { int i, // Current level counter levels_count=ArraySize(_levels_values); // Total number of levels //--- Check if the number of values in the array exceeds the allowed range if(levels_count>32 || levels_count==0) { Print(DEBUG_MESSAGE_PREFIX,": Levels cannot be set! Data array is incorrectly. "); return; } //--- Proceed with the implementation //--- Set the number of levels for the current object ObjectSetInteger(0,_object_name,OBJPROP_LEVELS,levels_count); //--- Set value, color and style for each level. for(i=0; i<levels_count; i++) { ObjectSetDouble(0,_object_name,OBJPROP_LEVELVALUE,i,_levels_values[i]); ObjectSetInteger(0,_object_name,OBJPROP_LEVELCOLOR,i,m_Fibo_Default_Color); ObjectSetInteger(0,_object_name,OBJPROP_LEVELSTYLE,i,m_Fibo_Default_Style); } //--- Redraw the chart before finishing ChartRedraw(0); }
Die übergebenen Funktionsparameter umfassen den Namen des Objekts, für das die Levels gesetzt werden, und das Array aller Level-Werte.
Zunächst prüft die Funktion die Anzahl der übergebenen Levels. Wenn das Array zu groß ist, geht die Funktion davon aus, dass ein Fehler aufgetreten ist und tut nichts. Sie beendet sich auch, wenn das Array keine Elemente enthält.
Nun, wenn alles in Ordnung ist und die Anzahl der Elemente im Array den erlaubten Bereich nicht überschreitet, dann beginnen wir mit dem Hinzufügen der Levels. Der Name des Objekts wird in den Parametern angegeben, also setzen wir einfach die entsprechende Eigenschaft des Objekts gleich der Anzahl der Array-Elemente und iterieren über das gesamte Array, während wir die entsprechenden Levels setzen.
MQL5 erlaubt es auch, verschiedene Parameter auf verschiedene Level zu setzen. Zum Beispiel können wir verschiedene Farben einstellen. Außerdem können wir verschiedene Stile verwenden (durchgezogen, gestrichelt, usw.). MQL4 bietet solche Optionen nicht. Nichtsdestotrotz habe ich der Schleife die Zeilen hinzugefügt, die die Level-Farben und Stile definieren. Sie haben keinen Einfluss auf die Kompilierung beim Hinzufügen der Universalität in MQL5.
Die Variablen, die die Standardparameter beschreiben, sind als private Mitglieder der Klasse CGraphics beschrieben und werden im Klassenkonstruktor mit den Werten aus den EA-Parametern initialisiert.
//+------------------------------------------------------------------+ //| Graphics.mqh | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Class for plotting graphic objects | //+------------------------------------------------------------------+ class CGraphics { //--- Fields private: // ... color m_Fibo_Default_Color; ENUM_LINE_STYLE m_Fibo_Default_Style; // ... //+------------------------------------------------------------------+ //| Default constructor | //+------------------------------------------------------------------+ CGraphics::CGraphics(void) { //... m_Fibo_Default_Color = Fibo_Default_Color; m_Fibo_Default_Style = VFun_Levels_Style; } // ...
Für diejenigen, die sich nicht um die Kompatibilität kümmern, habe ich einen Override dieser Funktion hinzugefügt. Sie ermöglicht die Einstellung von Parametern für jedes Level mit Hilfe von Arrays, die in den Funktionsparametern übergeben werden. Ich denke, aus dem Code ist alles klar. Wenn Sie weitere Erklärungen benötigen, schreiben Sie bitte einen entsprechenden Kommentar. Das Funktions-Override ist im angehängten Zip aktiviert.
Hier ist eine weitere Funktion, die Level-Beschreibungen für jedes Fibonacci Objekt setzt.
//+------------------------------------------------------------------+ //| Sets descriptions of levels in any Fibonacci object | //| _object_name - the name of the Fibonacci object | //| _levels_descriptions[] - array of level descriptions | //+------------------------------------------------------------------+ void CGraphics::SetFiboDescriptions( string _object_name, // Object name const string &_levels_descriptions[] // Array of descriptions ) { int i, // Current level counter levels_count=(int)ObjectGetInteger(0,_object_name,OBJPROP_LEVELS), // The real number of levels array_size=ArraySize(_levels_descriptions); // The number of received descriptions //--- Loop through all levels for(i=0; i<levels_count; i++) { if(array_size>0 && i<array_size) // Choose a description from the array { //--- and write it to the level ObjectSetString(0,_object_name,OBJPROP_LEVELTEXT,i,_levels_descriptions[i]); } else // If the descriptions are not enough, { ObjectSetString(0,_object_name,OBJPROP_LEVELTEXT,i,""); // leave the description empty } } //--- Redraw the chart before finishing ChartRedraw(0); }
Hier gibt es nichts Kompliziertes. Die einzige Bedingung ist, dass zu dem Zeitpunkt, an dem diese Funktion aufgerufen wird, die Objekt Levels bereits gesetzt worden sein müssen. Und die Funktion wird einfach in einer Schleife diese Levels durchlaufen und der Beschreibung jedes Levels einen entsprechenden Wert aus dem Array zuweisen. Wenn die Daten im Array nicht ausreichen, werden einige der Level ohne Beschreibung bleiben.
Und jetzt, wo das Hinzufügen eines Levels einfach geworden ist, können wir die Funktion schreiben, die einen Fibonacci-Fächer hinzufügt.
//+------------------------------------------------------------------+ //| Draws a Fibonacci fan from the nearest local extremum. | //+------------------------------------------------------------------+ void CGraphics::DrawVFan(void) { //--- double levels_values[]; // Array of level values string levels_descriptions[] = {}; // Array of level descriptions int p1=0, // Bar number for the fan starting point p2=0; // Bar number for the fan ending point double price1=0, // First point price price2=0; // Second point price string fun_name = // Fan name CUtilites::GetCurrentObjectName(allPrefixes[3],OBJ_FIBOFAN), fun_0_name = CUtilites::GetCurrentObjectName(allPrefixes[3]+"0_",OBJ_TREND); //--- Get data for the fan from the parameter string CUtilites::StringToDoubleArray(VFun_Levels,levels_values); //--- Find the extreme points closest to the mouse if(CMouse::Below()) // If the mouse cursor is below the price { CUtilites::SetExtremumsBarsNumbers(false,p1,p2); price1=iLow(Symbol(),PERIOD_CURRENT,p1); price2=iLow(Symbol(),PERIOD_CURRENT,p2); } else if(CMouse::Above()) // If the mouse cursor is above the price { CUtilites::SetExtremumsBarsNumbers(true,p1,p2); price1=iHigh(Symbol(),PERIOD_CURRENT,p1); price2=iHigh(Symbol(),PERIOD_CURRENT,p2); } //--- Create the fan object ObjectCreate( 0,fun_name,OBJ_FIBOFAN,0, iTime(Symbol(),PERIOD_CURRENT,p1), price1, iTime(Symbol(),PERIOD_CURRENT,p2), price2 ); //--- The zero ray of this object is denoted by a colored line (for compatibility with MT4) TrendCreate( 0, fun_0_name, 0, iTime(Symbol(),PERIOD_CURRENT,p1), price1, iTime(Symbol(),PERIOD_CURRENT,p2), price2, CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()), 0,1,false,true,true ); //--- Describe the fan levels SetFiboLevels(fun_name,levels_values); SetFiboDescriptions(fun_name, levels_descriptions); //--- Set standard parameters (such as timeframes and selection after creation) CurrentObjectDecorate(fun_name,m_Fibo_Default_Color); //--- Also make out the "substitute" ray CurrentObjectDecorate( fun_0_name, CUtilites::GetTimeFrameColor( CUtilites::GetAllLowerTimeframes() ) ); //--- ChartRedraw(0); }
Ich denke, es ist praktisch, wenn der Strahl, aus dem der Fächer gebildet wird, eine andere Farbe hat. Um diese Fähigkeit in MQL4 zu implementieren, müssen wir eine normale gerade Linie über den Fächer zeichnen, wie im vorherigen Artikel.
In diesem Fall benötigen wir keine Level-Beschriftungen, daher verwende ich einfach ein leeres Array.
Das Array mit den Werten wird aus den EA-Parametern mit der Utility-Funktion erzeugt
CUtilites::StringToDoubleArray(VFun_Levels,levels_values);
Dieses Hilfsprogramm, das eine Zeichenkette in ein Array von Zahlen umwandelt, wurde im ersten Artikel beschrieben.
Fügen Sie der Liste der Befehlsbeschreibungen einen Befehl zum Zeichnen von Ventilatoren hinzu:
//+------------------------------------------------------------------+ //| Shortcuts.mqhh | //+------------------------------------------------------------------+ //... //--- Draw a Fibonacci fan (VFun) if(CUtilites::GetCurrentOperationChar(VFun_Key) == lparam) { m_graphics.DrawVFan(); } break; //...
Kompilieren und prüfen Sie das Ergebnis. Öffnen Sie das Terminal und den gewünschten Chart.
Bewegen Sie die Maus vom oberen oder unteren Rand des Charts nach links zum Basisextremum und drücken Sie dann "F".
Übrigens, als ich mir die Konfiguration dieses Fächers anschaute, ging ich davon aus, dass der Preis sehr bald nach unten gehen könnte.
Und der Preis ist schließlich gefallen.
Andrews' Pitchfork
Ich verwende 3 Arten von Pitchforks (Heugabeln).
Zuerst bestimme ich die gewünschten Extrema und zeichne eine "normale" Pitchfork. Die Punkte der Pitchfork liegen genau an den extremen Kurswerten.
Der zweite Typ von Andrews' Pitchfork ist das Pitchfork von Schiff. Hier ist Punkt 1 um die halbe Strecke 1-2 in Trendrichtung versetzt. Entsprechend ist die Steigung der Mittellinie kleiner. Passt die Bewegung in dieses Pitchforks, ist die Bewegung höchstwahrscheinlich flach, der Kurs befindet sich also in einer "korrigierenden" Bewegung.
Der dritte Typ ist das "umgekehrte" Pitchfork. Der Punkt 1 wird in einer gegenläufigen Trendrichtung um den gleichen Abstand 1-2 versetzt. Dieser Pitchfork-Typ wird für schnelle Bewegungen verwendet. Sie sind in der Regel zeitlich kürzer, machen aber einen größeren Kursabstand.
In der praktischen Analyse habe ich gerne alle drei Arten von Pitchforks gleichzeitig auf dem Chart. In diesem Fall ist die Preisbewegung, zusammen mit den Schlüsselpunkten der wahrscheinlichen zukünftigen Extrema, viel klarer.
Zwei Funktionen werden verwendet, um eine solche Menge zu zeichnen. Die erste ist die Funktion, die eine Pitchfork beliebigen Typs zeichnet.
//+------------------------------------------------------------------+ //| Creates Andrews' pitchfork using specified coordinates | //| Parameters: | //| _name - the name of created pitchfork | //| _base - the structure containing coordinates of basic points | //| _type - pitchfork type (SIMPLE,SHIFF,REVERCE) | //+------------------------------------------------------------------+ void CGraphics::MakePitchfork( string _name, // The name of the created object PitchforkPoints &_base, // Structure describing pitchfork base points PitchforkType _type // Pitchfork type (SIMPLE,SHIFF,REVERCE) ) { //--- double price_first; // The price of the first point (depends on the type) color pitchfork_color; // Pitchfork color (depends on the type) int pitchfork_width; // Line width (depends on the type) ENUM_LINE_STYLE pitchfork_style; // Line style (depends on the type) double fibo_levels[] = {1}; // Add external levels (only for MQL5) string fibo_descriptions[] = {""}; // Level description (only for MQL5) //--- Set type dependent parameters: if(_type == SHIFF) // Schiff pitchfork { price_first = _base.shiffMainPointPrice; pitchfork_color = Pitchfork_Shiff_Color; pitchfork_width = Pitchfork_Shiff_Width; pitchfork_style = Pitchfork_Shiff_Style; } else if(_type == REVERCE) // "Reverse" pitchfork { price_first = _base.reverceMainPointPrice; pitchfork_color = Pitchfork_Reverce_Color; pitchfork_width = Pitchfork_Reverce_Width; pitchfork_style = Pitchfork_Reverce_Style; } else { // "classic" pitchfork price_first =_base.mainPointPrice; pitchfork_color = Pitchfork_Main_Color; pitchfork_width = Pitchfork_Main_Width; pitchfork_style = Pitchfork_Main_Style; } //--- Draw ObjectCreate(0,_name,OBJ_PITCHFORK,0, _base.time1,price_first, _base.time2,_base.secondPointPrice, _base.time3,_base.thirdPointPrice ); //--- Set the parameters common for all graphical objects CurrentObjectDecorate( _name, pitchfork_color, pitchfork_width, pitchfork_style ); //--- If MQL5 #ifdef __MQL5__ //--- add external levels (early levels for Andrews' pitchfork) SetFiboLevels(_name,fibo_levels); SetFiboDescriptions(_name,fibo_descriptions); #endif //--- Update the chart picture ChartRedraw(0); }
Die zweite Funktion berechnet die Koordinaten der Punkte 1, 2 und 3 (Basis) für die erstellte Mistgabel und startet sequentiell das Zeichnen aller drei Objekte. Basierend auf dieser Funktion wird die Pitchfork mit Hilfe der obigen CGraphics::MakePitchfork Funktion gezeichnet.
//+------------------------------------------------------------------+ //| Draws set of Andrews' pitchforks on one base. The set includes | //| three pitchfork types: regular, Schiff and reverse Schiff | //| (aka "micmed channel") | //+------------------------------------------------------------------+ void CGraphics::DrawPitchforksSet(void) { bool up=true; // direction (mouse below or above the price) double dropped_price = CMouse::Price(); // "Starting point" price int dropped_bar = CMouse::Bar(); // Starting point bar number string name = ""; // The name of the current object PitchforkPoints base; // Structure for the base coordinates //--- if(CMouse::Below()) { up=false; } else { if(!CMouse::Above()) // If the mouse pointer is on the candlestick, do nothing { if(Print_Warning_Messages) { Print(DEBUG_MESSAGE_PREFIX,": Set a point above or below the bar extreme price"); } return; } } //--- Find extremum bar numbers int bar_first = CUtilites::GetNearestExtremumBarNumber( dropped_bar, true, up, Pitchfork_First_Point_Left_Bars, Pitchfork_First_Point_Right_Bars ); int bar_second = CUtilites::GetNearestExtremumBarNumber( bar_first-1, true, !up, Pitchfork_Second_Point_Left_Bars, Pitchfork_Second_Point_Right_Bars ); int bar_third = CUtilites::GetNearestExtremumBarNumber( bar_second-1, true, up, Pitchfork_Third_Point_Left_Bars, Pitchfork_Third_Point_Right_Bars ); //--- If not found, report an error if(bar_first<0||bar_second<0||bar_third<0) { if(Print_Warning_Messages) { Print(DEBUG_MESSAGE_PREFIX,": Could not find points that match all conditions."); } return; } //--- Fill the structure for basic control points base.mainPointPrice = up ? // Price - first basic point iHigh(Symbol(),PERIOD_CURRENT,bar_first) : iLow(Symbol(),PERIOD_CURRENT,bar_first); base.secondPointPrice = up ? // Price - second basic point iLow(Symbol(),PERIOD_CURRENT,bar_second) : iHigh(Symbol(),PERIOD_CURRENT,bar_second); base.thirdPointPrice = up ? // Price - third basic point iHigh(Symbol(),PERIOD_CURRENT,bar_third) : iLow(Symbol(),PERIOD_CURRENT,bar_third); base.shiffMainPointPrice = base.mainPointPrice- // Price - first point of Schiff pitchfork (base.mainPointPrice-base.secondPointPrice)/2; base.reverceMainPointPrice = base.mainPointPrice+ // Price - first point of "reverse" pitchfork (base.mainPointPrice-base.secondPointPrice)/2; base.time1 = iTime(Symbol(),PERIOD_CURRENT,bar_first); // Time of the first point base.time2 = iTime(Symbol(),PERIOD_CURRENT,bar_second); // Time of the second point base.time3 = iTime(Symbol(),PERIOD_CURRENT,bar_third); // Time of the third point //--- Draw "regular" pitchfork if(Pitchfork_Show_Main) { name =CUtilites::GetCurrentObjectName(allPrefixes[4]+"_main",OBJ_PITCHFORK); MakePitchfork(name,base,SIMPLE); } //--- Draw Schiff pitchfork if(Pitchfork_Show_Shiff) { name =CUtilites::GetCurrentObjectName(allPrefixes[4]+"_shiff",OBJ_PITCHFORK); MakePitchfork(name,base,SHIFF); } //--- Draw "reverse" pitchfork if(Pitchfork_Show_Reverce) { name =CUtilites::GetCurrentObjectName(allPrefixes[4]+"_reverce",OBJ_PITCHFORK); MakePitchfork(name,base,REVERCE); } //--- //ChartRedraw(0); not needed here as it is called when drawing each object }
Ich verwende die folgende Enumeration zur Beschreibung der Pitchforktypen:
//+------------------------------------------------------------------+ //| Possible Andrews' pitchfork types | //+------------------------------------------------------------------+ enum PitchforkType { SIMPLE, SHIFF, REVERCE };
Ich habe für die Punkte eine Basis-Struktur (PitchforkPoints) hinzugefügt, um beim Aufruf einer Zeichenfunktion weniger Parameter zu übergeben.
//+------------------------------------------------------------------+ //| Structure describing a "base" for the Andrews' pitchfork | //+------------------------------------------------------------------+ struct PitchforkPoints { double mainPointPrice; // Price - first base point double shiffMainPointPrice; // Price - second base point double reverceMainPointPrice; // Price - third base point double secondPointPrice; // Price - first point of Schiff pitchfork double thirdPointPrice; // Price - first point of "reverse" pitchfork datetime time1; // Time of the first point datetime time2; // Time of the second point datetime time3; // Time of the third point };
Fügen Sie schließlich in der Datei "Shortcuts.mqh" eine Beschreibung der Reaktion auf die Steuerungstaste hinzu:
//+------------------------------------------------------------------+ //| Shortcuts.mqhh | //+------------------------------------------------------------------+ //... //--- Draw Andrews' Pitchfork if(CUtilites::GetCurrentOperationChar(Pitchfork_Key) == lparam) { m_graphics.DrawPitchforksSet(); } break; //...
Kompilieren und prüfen.
Um ein Pitchfork im Chart anzuzeigen, drücken Sie die Taste "P" (Pitchfork).
Funktionen zum Zeichnen von Trendlinien im MetaTrader
Generell können die oben beschriebenen Objekte für beliebige Grafiken verwendet werden. Die Funktionalität umfasst gerade Linien, Andres Pitchfork, Fibonacci-Fächer, horizontale und vertikale Levels.
In ähnlicher Weise können wir, indem wir die Extrempunkte rechts oder links von der Maus finden, Kanäle, horizontale Fibonacci Levels usw. zeichnen. Wenn Sie diese Formen häufig verwenden, können Sie die gewünschte Funktionalität leicht implementieren.
Der für mich schwierigste Teil dieser Bibliothek betraf die geraden Linien mit einem Endpunkt rechts und einem zweiten Punkt in der Zukunft.
Solche Linien sind sehr praktisch, um signifikante Levels zu markieren, sowohl nach Preis als auch nach Zeit. In der Regel bemerkt der Preis diese Levels und bildet zumindest ein lokales Extremum irgendwo in der Nähe; sehr oft kehrt sich der Preis um.
Es hat sich jedoch herausgestellt, dass die Funktion zum Zeichnen von Linien im MetaTrader den Preis und die Zeit verwendet.
Das erste Problem tritt auf, wenn Linien am Freitag gezeichnet werden und ihr rechter Rand am Montag liegt.
Am Freitag denkt MetaTrader, dass es einen Sonntag geben muss, aber dann versteht er am Montag, dass an diesem Tag nicht gehandelt werden konnte und deshalb zwei Tage verworfen werden müssen. Dadurch wird eine Linie, die durch Zeit Koordinaten gezeichnet wird, kürzer. Dies ist in der obigen Abbildung deutlich zu sehen.
Wenn ich eine bestimmte Anzahl von Balken im Chart messen muss, ist dieses Verhalten ungünstig.
Die Lösung ist ganz einfach: Der Zeitpunkt muss nicht über den Kalender, sondern über die Punkte berechnet werden. Mauskoordinaten zeigen einen Punkt auf dem Chart; der Abstand zwischen den Kerzen kann immer berechnet werden (z. B. wie im ersten Teil, im Abschnitt "Abstand zwischen benachbarten Balken (in Pixel)" beschrieben), und dann brauchen wir nur noch die gewünschte Anzahl von Kerzen nach rechts zu zählen und die Bildschirmkoordinaten mit der Standardfunktion ChartXYToTimePrice in Zeit und Preis umzurechnen.Eine solche Gerade sollte aber am Montag und nicht am Freitag gezeichnet werden, um den "Sonntagskollaps" zu vermeiden. Eine solche Gerade sollte aber am Montag und nicht am Freitag gezogen werden, um den "Sonntagseinbruch" zu vermeiden.
Die Methode scheint gut zu sein, aber es gibt ein Aber. Die Größe der Fläche, auf der MetaTrader eine Linie zeichnen kann, ist begrenzt. Wenn Sie versuchen, eine Linie zu zeichnen, die größer ist als der vom Programm erlaubte Raum (z.B. sehr nah am Rand, wie in der linken Abbildung), dann können die Auswirkungen sehr unerwartet sein.
Die rechte Abbildung zeigt die gleiche Linie, die automatisch gezeichnet wurde, aber jetzt ist das Chart nach rechts verschoben, um den rechten Rand zu zeigen. Darunter befindet sich eine normale Linie, die auf dieser Skala hätte liegen sollen. Nach den Eigenschaften der oberen Linie zu urteilen, ist ihr rechter Endpunkt um fast sechs Monate nach vorne gerückt!
Manchmal sah ich bei einer schrägen Linie, wie sich die Linie in die entgegengesetzte Richtung umkehrte. MetaTrader konnte die Koordinaten des Punktes nicht in den richtigen Zeitpunkt umrechnen und setzte ihn einfach auf 0 (dementsprechend war das dann der 1. Januar 1970). Dieser Effekt tritt nie auf, wenn die Linie mit Zeitangaben gezeichnet wird.
Fazit: Wir brauchen eine Funktion, die Datumsangaben in einer noch nicht definierten Zukunft berechnet und das einfache Zeichnen von Geraden ermöglicht.
Lassen Sie uns also eine solche Funktion erstellen.
Funktion zum Ermitteln eines zukünftigen Zeitpunktes
Normalerweise gibt es einen Punkt in der Gegenwart oder in der Vergangenheit, von dem aus wir etwas messen wollen (z. B. eine Art Extremwert). Außerdem kennen wir in der Regel entweder den Verschiebungsabstand in Balken oder wir können ihn leicht berechnen. Die häufigste Aufgabe für diese Funktion wird also sein, die Zeit relativ zu einem Punkt zu berechnen, basierend auf einer Verschiebung in Balken. Ich mag aber auch den Effekt der Level-Verlängerung/-Verkürzung in Abhängigkeit von der Skala. Daher möchte ich manchmal, dass die Funktion die Zeit anhand von Punkten und nicht anhand von Balken berechnet.
Sowohl die Anzahl der Punkte als auch die Anzahl der Balken sind Ganzzahlen, daher braucht die Funktion eine Art von Merkmal, um zu verstehen, was genau zu tun ist. Beginnen wir mit dieser Funktion.
//+------------------------------------------------------------------+ //| GlobalVariables.mqh | //+------------------------------------------------------------------+ //... //+------------------------------------------------------------------+ //| The enumeration describes possible options for calculating the | //| time of the next bar | //| COUNT_IN_BARS - calculate date by the number of bars | //| COUNT_IN_PIXELS - calculate date by the number of pixels | //+------------------------------------------------------------------+ enum ENUM_FUTURE_COUNT { COUNT_IN_BARS, // By bars COUNT_IN_PIXELS // By pixel }; //...
Alle Beschreibungen der Enumeration und der globalen Variablen befinden sich in der Datei GlobalVariables.mqh. Die Enumeration der möglichen Optionen für die Auswahl der Intervalle für unsere zukünftige Funktion sollte ebenfalls in diese Datei aufgenommen werden.
Die Funktion selbst zeichnet nichts und hat nichts mit der Maus zu tun. Sie muss also ein Dienstprogramm sein.
//+------------------------------------------------------------------+ //| Utilites.mqh | //+------------------------------------------------------------------+ //... class CUtilites { public: //... //--- Calculates a date in the future relative to the start date with the _length interval, specified in pixels or bars static datetime GetTimeInFuture( const datetime _start_time, // Reference time based on which the future bar is calculated const int _length, // Interval length (in bars or pixels) const ENUM_FUTURE_COUNT _count_type=COUNT_IN_BARS // Interval type (pixels or bars). ); //... //+------------------------------------------------------------------+ //| The function tries to calculate date in the future using the | //| screen x and y coordinates | //| If calculation is unsuccessful (time exceeds limits), calculates | //| time with an error: as a sum of dates excluding Sundays. | //| Parameters: | //| _current_time, Source time, | //| _length, Interval length | //| (in bars or in pixels) | //| _count_type=COUNT_IN_BARS How interval length is measured. | //| COUNT_IN_BARS - the interval is set in bars; | //| COUNT_IN_PIXELS - the interval is set in pixels. | //| Returns: | //| Time in the future distant from the _current_time | //| by an interval in pixels or bars (_length) | //+------------------------------------------------------------------+ datetime CUtilites::GetTimeInFuture( const datetime _start_time, // Reference time based on which the future bar is calculated const int _length, // Interval length (in bars or pixels) const ENUM_FUTURE_COUNT _count_type=COUNT_IN_BARS // Interval type (pixels or bars). ) { //--- datetime future_time; // Variable for result int bar_distance = GetBarsPixelDistance(), // Distance in pixels between two adjacent bars current_x, // The x coordinate of the starting point future_x, // The x coordinate of the result current_y, // The y coordinate, does not affect the result; needed for the conversion function subwindow = 0; // Subwindow index double current_price; // Any initial price, does not affect the result //--- Convert the time passed in parameters into the screen coordinate x ChartTimePriceToXY(0,subwindow,_start_time,CMouse::Price(),current_x,current_y); //--- Calculate a point in the future in screen coordinates if(COUNT_IN_BARS == _count_type) // If the length is specified in bars, { // then the interval size should be converted to pixels. future_x = current_x + _length*bar_distance; } else // ... If the length is in pixels, { // use it as is future_x = current_x + _length; } //--- Convert screen coordinates into time if(ChartGetInteger(0,CHART_WIDTH_IN_PIXELS)>=future_x) // If successful, { ChartXYToTimePrice(0,future_x,current_y,subwindow,future_time,current_price); // convert the resulting value } else // Otherwise, if time cannot be calculated because it exceeds limits { future_time = // Calculate time as usual, in seconds _start_time // To the starting time +( // add ((COUNT_IN_BARS == _count_type) ? _length : _length/bar_distance) // interval size in bars *PeriodSeconds() // multiplied by the number of seconds in the current period ); } //--- Return the resulting value return future_time; }
Es stellte sich jedoch heraus, dass die in der vorherigen Version beschriebene Funktion nicht immer ein korrektes Ergebnis lieferte. Deshalb musste ich sie neu schreiben. Es stellte sich heraus, dass alles viel einfacher ist.
//+------------------------------------------------------------------+ //| Utilites.mqh | //+------------------------------------------------------------------+ //... //+------------------------------------------------------------------+ //| Calculates a distance in pixels between two adjacent bars | //+------------------------------------------------------------------+ int CUtilites::GetBarsPixelDistance(void) { //--- Calculate the distance return ((int)MathPow(2,ChartGetInteger(0,CHART_SCALE))); } //...
Begrenzte horizontale Levels
Diese Levels habe ich im vorherigen Abschnitt gezeigt. Es handelt sich um eine Linie mit einer bestimmten Länge, die im Idealfall nicht davon abhängt, wohin Sie mit der Maus zeigen. Sie wird von dem Punkt aus gezeichnet, auf den der Mauszeiger gerichtet ist. Daher sollte der Punkt etwas sorgfältiger gewählt werden als, sagen wir, bei einem Fächer.
Ich möchte, dass diese Levels eine streng definierte (empirische) Länge in Pixeln haben. Dann wird die Linie eine unterschiedliche Anzahl von Balken auf verschiedenen Skalen abdecken.
Außerdem möchte ich in der Lage sein, einen normalen und einen erweiterten Level der Linie zu schreiben - alle auf der gleichen Skala.
Hier ist das Ergebnis:
//+------------------------------------------------------------------+ //| Graphics.mqh | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Draws a horizontal level | //| Parameters: | //| _multiplicator - multiplier for determining the length | //| of the larger level (how many times higher) | //+------------------------------------------------------------------+ //--- void CGraphics::DrawHorizontalLevel( double _multiplicator // Multiplier for the level length ) { //--- Description of variables datetime p2_time; // Time of point 2 string Level_Name =""; // Level name //Color of the current line (equal to the general color of the current time interval) color Level_Color=CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()); int window = 0; // The index of the subwindow in which the line is drawn ENUM_LINE_STYLE Current_Style = STYLE_SOLID; // Line style int Current_Width=1; // Line width int level_length = 0; // Line length //--- Get the length (in pixels) if(Short_Level_Length_In_Pixels) { // If EA parameters instruct to measure in pixels, level_length = Short_Level_Length_Pix; // ...Use the length from parameters } else { // Otherwise the number of candlesticks is specified in parameters level_length = Short_Level_Length * CUtilites::GetBarsPixelDistance(); } //--- Set level parameters if(_multiplicator>1) // If the level is extended { Level_Name = CUtilites::GetCurrentObjectName(allPrefixes[7]); Current_Style = Long_Level_Style; Current_Width = Long_Level_Width; } else // An if the level is short { Level_Name = CUtilites::GetCurrentObjectName(allPrefixes[6]); Current_Style = Short_Level_Style; Current_Width = Short_Level_Width; } //--- Calculate real coordinates (price and time) for the second point p2_time = CUtilites::GetTimeInFuture(CMouse::Time(),level_length*_multiplicator,COUNT_IN_PIXELS); //--- Draw a line using the known coordinates TrendCreate(0, Level_Name, 0, CMouse::Time(), CMouse::Price(), p2_time, CMouse::Price(), Level_Color, Current_Style, Current_Width ); //--- ChartRedraw(0); }
Der erste Punkt wird durch den Mauszeiger bestimmt. Bei der Berechnung des zweiten Punktes wählt das Programm als Erstes aus, ob sich die Liniengröße mit der Änderung des Chart-Maßstabes ändern soll, und berechnet dann die Koordinaten des zweiten Punktes in Pixeln und rechnet sie auf Preis und Zeit um. (Wenn Sie eine fertige Funktion haben, sind die Berechnungen nicht sehr schwierig).
Nun müssen wir der Datei Shortcuts.mqh Steuerbefehle hinzufügen:
//+------------------------------------------------------------------+ //| Shortcuts.mqh | //+------------------------------------------------------------------+ // ... //--- Draw a short limited level if(CUtilites::GetCurrentOperationChar(Short_Level_Key) == lparam) { m_graphics.DrawHorizontalLevel(1); } //--- Draw an extended limited level if(CUtilites::GetCurrentOperationChar(Long_Level_Key) == lparam) { m_graphics.DrawHorizontalLevel(Long_Level_Multiplicator); } // ...
Wenn also der Parameter Short_Level_Length_In_Pixels wahr ist, dann zeichnet das Programm beim Drücken der Taste S (Short) ein horizontales Segment mit der im Parameter Short_Level_Length_Pix angegebenen Länge in Pixel.
Wenn Short_Level_Length_In_Pixels == false, wird die Level-Länge in Balken gemessen und dafür der Parameter Short_Level_Length herangezogen.
Wenn Sie "L" (Llong) drücken, verdoppelt sich die Länge der Linie (wird mit der im Parameter Long_Level_Multiplicator angegebenen Zahl multipliziert).
Begrenzte Trendlinie
Ich glaube, dass eine Trendlinie eine zwei Informationen tragen kann.
Einerseits zeigt sie eine Begrenzung der Preisänderungsrate an ("nicht schneller", wenn der Preis unterhalb der Linie liegt, oder "nicht langsamer", wenn der Preis oberhalb der Linie liegt).
Andererseits, wenn die Gerade in Preis und Zeit begrenzt ist (kein Strahl ist), dann kann sie Levels anzeigen (sowohl Preise als auch Zeit). Natürlich könnten wir für diese Zwecke auch ein Rechteck oder etwas anderes verwenden, aber eine diagonale Linie ist meiner Meinung nach klarer.
Also habe ich die Funktion CGraphics::DrawTrendLine modifiziert.Daher habe ich für diese Version des Indikators in MQL5 den Algorithmus verändert. Erstens setzt sich die Linie jetzt für eine begrenzte Zeit in die Zukunft fort und zeigt so den geschätzten Preis an. Zweitens habe ich der Übersichtlichkeit halber die üblichen Levels hinzugefügt - horizontal und vertikal.
Sie sieht wie folgt aus:
Natürlich werden die Länge der Linie (wie oft die Gesamtlänge größer ist als der Abstand zwischen den Anfangspunkten), die Anzahl der Balken für Extremwerte und andere Eigenschaften der Geraden in den EA-Parametern konfiguriert.
//+------------------------------------------------------------------+ //| Graphics.mqh | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Draws a trend line using two nearest extreme points. | //| Extremum length (number of bars on left and right) is set | //| by parameters Fractal_Size_Left and Fractal_Size_Right | //| | //| There is a "Trend_Points" variable in the global parameters. | //| | //| If the variable value is equal to "TREND_DOTS_EXTREMUMS", | //| end points of the straight line will lie strictly at extrema. | //| If the values is "TREND_DOTS_HALF", the line will be | //| extended into the future by a distance of | //| (p2-p1)*Trend_Length_Coefficient | //+------------------------------------------------------------------+ void CGraphics::DrawTrendLine(void) { int dropped_bar_number=CMouse::Bar(); // candlestick number under the mouse int p1=0,p2=0; // numbers of the first and seconds points string trend_name = // trend line name CUtilites::GetCurrentObjectName(allPrefixes[0],OBJ_TREND); double price1=0, // price of the first point price2=0, // price of the second point tmp_price; // variable for temporary storing of the price datetime time1=0, // time of the first point time2=0, // time of the second point tmp_time; // a variable to store time //--- Setting initial parameters if(CMouse::Below()) // If a mouse cursor is below the candlestick Low { //--- Find two extreme points below CUtilites::SetExtremumsBarsNumbers(false,p1,p2); //--- Determine point prices by Low price1=iLow(Symbol(),PERIOD_CURRENT,p1); price2=iLow(Symbol(),PERIOD_CURRENT,p2); } else // otherwise if(CMouse::Above()) // If a mouse cursor is below the candlestick High { //--- Find two extreme points above CUtilites::SetExtremumsBarsNumbers(true,p1,p2); //--- Determine point prices by High price1=iHigh(Symbol(),PERIOD_CURRENT,p1); price2=iHigh(Symbol(),PERIOD_CURRENT,p2); } else { return; } //--- The time of the first and second points does not depend on the direction time1=iTime(Symbol(),PERIOD_CURRENT,p1); time2=iTime(Symbol(),PERIOD_CURRENT,p2); //--- If the line should be extended to the right if(Trend_Points == TREND_POINTS_HALF) { //--- Temporarily save the coordinates of point 2 tmp_price = price2; tmp_time = time2; //--- Calculate the time of the second point time2 = CUtilites::GetTimeInFuture(time1,(p1-p2)*Trend_Length_Coefficient); //--- Calculate the price of the second point price2 = NormalizeDouble(price1 + (tmp_price - price1)*Trend_Length_Coefficient,Digits()); //--- Draw boundary levels by price and time DrawSimple(OBJ_HLINE,time2,price2); DrawSimple(OBJ_VLINE,time2,price2); } //--- Draw the line TrendCreate(0,trend_name,0, time1,price1,time2,price2, CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()), 0,Trend_Line_Width,false,true,m_Is_Trend_Ray ); //--- Redrawing the chart ChartRedraw(0); }
Andere Änderungen im Code sind gelb hervorgehoben.
Der Rest ist einfach. Die Anzahl der Balken zwischen den Punkten ist gleich (р1-р2) (vergessen Sie nicht, dass der Balkenindex nach rechts zunimmt). Mit einem Koeffizienten lässt sich berechnen, um wie viel ein Intervall erweitert werden soll. Rufen Sie dann einfach die Utility-Funktion auf, auch ohne Angabe der dritten Parameter, da sie standardmäßig die Berechnung in Balken erlaubt.
Berechnen Sie dann den Preis, zeichnen Sie die Levels mit der zuvor beschriebenen Funktion DrawSimple, die sich in der gleichen Klasse befindet, und zeichnen Sie die Hauptlinie.
Ein Anfänger mag sich fragen: "Woher 'weiß' die Funktion, wo der Preis hinzugefügt werden soll: oben oder unten? Wenn die Linie von oben nach unten geht, dann sollte der Preis subtrahiert werden, und wenn sie von unten nach oben geht, dann sollte der Preis addiert werden."
Da es für uns nicht wichtig ist, ob wir uns an Tiefst- oder Höchstpreisen orientieren (das haben wir bereits zu Beginn der Funktion überprüft), wird die Richtung eindeutig durch den Ausdruck Preis1 + (tmp_Preis - Preis1) bestimmt.
Wenn die Linie nach unten verläuft, dann ist Preis1 größer als der Preis des zweiten Punktes, und daher wird der Ausdruck (tmp_price - price1) negativ sein. Somit wird der gewünschte Abstand vom Preis abgezogen.
Wenn die Linie nach oben verläuft, dann ist der Preis, der den zweiten Punkt definiert, größer als der erste, und der Ausdruck in den Klammern wird positiv sein, so dass der Abstand zum Anfangspreis addiert wird.
Ich möchte noch eine weitere Eigenschaft dieser Funktion erwähnen; dies ist eine Erklärung für Anfänger. Wenn eine Funktion Preise berechnet, dann müssen die Daten normalisiert werden. Das heißt, wir müssen sicherstellen, dass die empfangene Zahl die gleiche Anzahl von Dezimalstellen hat wie die Kurse im Chart. Andernfalls wird ein Fehler auftreten. Die Funktion NormalizeDouble wird zum Normalisieren der Kurse verwendet.
Es sind keine Änderungen in der Datei Shortcuts.mqh erforderlich. Die Linie wird durch Drücken der Taste "T" gezeichnet (Trend). Es sollte also die obige Funktion aufgerufen werden, um die Linie zu zeichnen.
//+------------------------------------------------------------------+ //| Shortcuts.mqh | //+------------------------------------------------------------------+ //... //--- Draw a trend line if(CUtilites::GetCurrentOperationChar(Trend_Line_Key) == lparam) { m_graphics.DrawTrendLine(); } //...
Zeichnen vertikaler Level
Da die Märkte einen Trendcharakter haben und die Preisbewegung nicht völlig zufällig ist, kann beim Handel meist folgende Regel angewendet werden: Der Preis tendiert immer dazu, sich um die gleiche Strecke zu bewegen, die er bereits zurückgelegt hat. Die Richtung der Bewegung ist eine andere Frage. Oft bewegt sich der Preis nach dem Durchbrechen, sagen wir, der Kante eines Pin Bar oder einer großen Kerze, um die gleiche Distanz, die von diesem Balken gemessen wurde, und kehrt dann um.
Dennoch ziehen es viele große Händler (die letztendlich die Richtung bestimmen könnten) vor, eine Position etwas früher zu verlassen, bevor 100% des Levels erreicht wird. Daher erreicht der Preis oft nicht die verwendeten Level.
Deshalb verwende ich auch fraktionierte Levels für den Handel. Das am häufigsten verwendete ist das Level von 7/8. Das letzte Tool, das wir in diesem Artikel betrachten werden, dient dazu, diese Levels auf dem Bildschirm darzustellen.
Nun sollte die Funktion, die die Levels zeichnet, einfach zu verstehen sein.
//+------------------------------------------------------------------+ //| Graphics.mqh | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Draws a vertical line at levels 7/8 and 14/8 of the | //| current candlestick size | //+------------------------------------------------------------------+ void CGraphics::DrawVerticalLevels(void) { //--- Description of variables string Current_Vertical_Name = // The name of the basic vertical line CUtilites::GetCurrentObjectName(allPrefixes[5]), Current_Level_Name = // The name of the current level CUtilites::GetCurrentObjectName(allPrefixes[5]+"7_8_"); double Current_Line_Lenth, // The length of the current line (level or vertical) Current_Extremum, // Working extremum (High or Low, depending on the mouse position Level_Price, // Level price High = iHigh(Symbol(),PERIOD_CURRENT,CMouse::Bar()), // The High price of the current candlestick Low = iLow(Symbol(),PERIOD_CURRENT,CMouse::Bar()); // The Low price of the current candlestick int direction=0; // Price increment sign long timeframes; // List of working timeframes datetime Current_Date = // Time of the current bar iTime(Symbol(),PERIOD_CURRENT,CMouse::Bar()), Right_End_Time = // Time of the right border of the level CUtilites::GetTimeInFuture(Current_Date,Short_Level_Length); //--- Calculating candlestick length Current_Line_Lenth = (High-Low)*2; //--- Initialization of the main variables depending on the desired drawing direction if(CMouse::Above()) // If the mouse is above { Current_Extremum = High; // The main price is High direction = -1; // Drawing direction - downward } else // Otherwise { if(CMouse::Below()) // If the mouse is below { Current_Extremum = Low; // The main price is Low direction = 1; // Drawing direction is upward } else // If the mouse is in the middle of the candlestick, exit { return; } } //--- Vertical line TrendCreate(0, Current_Vertical_Name, 0, Current_Date, Current_Extremum, Current_Date, Current_Extremum+(Current_Line_Lenth*2)*direction, CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()), Vertical_With_Short_Levels_Style, Vertical_With_Short_Levels_Width ); //--- First level (7/8) Level_Price = Current_Extremum+(Current_Line_Lenth*Vertical_Short_Level_Coefficient)*direction; TrendCreate(0, Current_Level_Name, 0, Current_Date, Level_Price, Right_End_Time, Level_Price, CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()), Short_Level_7_8_Style, Short_Level_7_8_Width ); //--- Second level (14/8) Current_Level_Name = CUtilites::GetCurrentObjectName(allPrefixes[5]+"14_8_"); Level_Price = Current_Extremum+(Current_Line_Lenth*2*Vertical_Short_Level_Coefficient)*direction; TrendCreate(0, Current_Level_Name, 0, Current_Date, Level_Price, Right_End_Time, Level_Price, CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()), Short_Level_14_8_Style, Short_Level_14_8_Width ); }
Achten Sie bitte auf zwei Punkte. Erstens wird hier die Zeit der diesen Levels immer in Balken berechnet. Die benötigte Anzahl der Balken richtet sich nach dem Wert der Variablen Short_Level_Length, daher weiß ich immer, wie viele Balken gezählt werden.
Zweitens muss hier der Preis auf der Basis von nur einem Punkt berechnet werden. Daher ist es notwendig, richtungsabhängige Parameter zu setzen, damit Sie nicht jedes Mal doppelt prüfen und doppelten Code schreiben müssen. Insbesondere setzen wir den Parameter direction, mit dem jeder Term multipliziert wird, bis auf den ersten Punkt. So habe ich wieder nur einen Ausdruck, der das Verhalten der Linie beschreibt, aber das Vorzeichen der Terme in diesem Ausdruck hängt davon ab, wo sich die Maus befindet: über oder unter der Kerze.
Die endgültige Form ist in der obigen Abbildung zu sehen.
Fügen Sie der Datei "Shortcuts.mqh" eine Kontrollstruktur hinzu:
//+------------------------------------------------------------------+ //| Shortcuts.mqh | //+------------------------------------------------------------------+ //... //--- Draw a vertical line with 7/8 levels if(CUtilites::GetCurrentOperationChar(Vertical_With_Short_Levels_Key) == lparam) { m_graphics.DrawVerticalLevels(); } break;Die Taste ist V (Vertical).
Tasten, die in der aktuellen Bibliothek implementiert sind
Aktion | Schlüssel | Bedeutung |
---|---|---|
Wechsel des Zeitrahmen nach oben in den Haupt-Zeitrahmen (aus dem Panel der Zeitrahmen) | U | Up (aufwärts) |
Wechsel des Zeitrahmens nach unten | D | Down (abwärts) |
Wechsel der Chart Z-Ebenen (Chart ganz nach oben über allen Objekten oder nicht) | Z | Z order |
Zeichnen einer schrägen Trendlinie basierend auf zwei unidirektionalen Extrempunkten, die der Maus am nächsten liegen | T | Trendlinie |
Wechsel des Modus für Strahl (ray) für neue Linien | R | Ray (Strahl) |
Zeichnen einer einfachen senkrechten Linie | I(i) | [Nur visuelle Vertikale] |
Zeichnen einer einfachen horizontalen Linie | H | Horizontal |
Zeichnen von Andrews' Pitchfork | P | Pitchfork |
Zeichnen eines Fibonacci-Fächers (VFun) | F | Fun |
Zeichnen einer kurzen horizontalen Linie | S | Skurz |
Zeichnen einer längeren horizontalen Linie | L | Llong |
Zeichnen einer vertikalen Linie mit Level-Markierungen | V | Vertical |
Schlussfolgerung
Ich hoffe, dass der Artikel nützlich sein wird, aber ich garantiere überhaupt nichts. Das resultierende Toolkit ist sehr flexibel und für die Arbeit in allen Märkten geeignet. Wenn die Leser des Artikels jedoch anfangen werden, es mit den Standardeinstellungen zu verwenden, werden sich die Märkte wahrscheinlich ändern. Wahrscheinlich nicht allzu sehr, denn Veränderung ist das Wesen des Marktes.
Sie sind herzlich eingeladen, Ihre Kommentare und Ideen zu teilen.
Ich wünsche Ihnen stabile Gewinne!
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7908





- 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.