Ein manuelles Chart- und Handelswerkzeug (Teil I). Vorbereitung: Strukturbeschreibung und die Hilfsklasse
Einführung
Ich handle manuell. Ich ziehe es daher vor, Charts nicht mit komplexen Formeln und Indikatoren zu analysieren, sondern manuell, d.h. grafisch. Das hilft mir, beim Handel flexibler zu sein, einige Dinge zu bemerken, die schwer zu formalisieren sind, leicht zwischen Zeitrahmen zu wechseln, wenn ich sehe, dass die Bewegung zunimmt oder sich verlangsamt, das wahrscheinliche Kursverhalten zu kennen, lange bevor ich in einen Handel einsteige.
Grundsätzlich verwende ich verschiedene Kombinationen von Trendlinien (Pitchfork (Gabel), Fans (Fächer), Ebenen und so weiter). Daher habe ich ein praktisches Werkzeug geschaffen, das das schnelle Zeichnen von Trendlinien ermöglicht, buchstäblich mit einem Tastendruck. Nun möchte ich dieses Werkzeug mit der Gemeinschaft teilen.
Video-Präsentation. Wie es funktioniert.
Allgemeines Konzept. Erklärung des Problems
Wir schaffen also ein Werkzeug, das hilft, die häufigsten Operationen mit Hilfe von Tastaturkürzeln auszuführen.
Welche Aufgaben können implementiert werden? Zum Beispiel:
- Das Zeichnen einer einfachen, horizontalen Linie durch das Drücken der Taste "H" ("Horizontal") und eine vertikale Linie durch Drücken der Taste "i" (einfach weil i wie eine vertikale Linie aussieht).
- Das Zeichnen von horizontalen Linien einer bestimmten Länge (NICHT unendlich), die an einem jedem Punkt auf dem Chart beginnen können.
- Das Zeichen von vertikalen Ebenen in einem bestimmten (willkürlichen) Abstand vom Startpunkt.
- Das Umschalten der Zeitrahmen und die Schichten im Chart mit Hilfe von Tasten neu anordnen.
- Das Zeichnen von Fibonacci-Fächer mit den voreingestellten Stufen an den nächstgelegenen Extrempunkten.
- Das Zeichnen von Trendlinien an den nächstgelegenen Extrempunkten; ihre Länge sollte ein Vielfaches des Abstands zwischen den Extrempunkten sein; in einigen Fällen kann die Linie auch ein Strahl sein.
- Das Zeichnen von Andrews Pitchfork (Gabel) verschiedener Typen (Standard, Schiff, umgekehrte Schiffsgabel - für schnelle Trends) (siehe das Video).
- Für die Extrempunkte der Gabeln, Linien und Fächer sollte es die Möglichkeit geben, die Reihenfolge der Extrempunkte anzupassen (die Anzahl der Balken, links und rechts getrennt).
- Eine grafische Schnittstelle, mit der die Parameter der erforderlichen Linien und Extrempunkte angepasst werden können, ohne dass das Einstellungsfenster geöffnet werden muss.
- Eine Reihe von Funktionen zur Verwaltung von Aufträgen: Eröffnung eines Auftrags auf der Grundlage eines prozentualen Anteils der Einlage, automatische Festlegung von Stopp-Levels bei der Eröffnung eines Marktauftrags oder wenn ein Pending-Auftrag ohne Stopp-Level ausgelöst wird, Ermöglichung einer teilweisen Positionsschließung nach Levels, Trailing-Stopp und so weiter.
Natürlich können die meisten Aufgaben mit Hilfe von Skripten automatisiert werden. Ich habe ein paar davon. Sie finden sie unten im Anhang des Artikels. Ich bevorzuge jedoch einen anderen Ansatz.
In jedem Expert Advisor oder Indikator können wir die Methode OnChartEvent erstellen, die eine Beschreibung der Reaktionen auf beliebige Ereignisse enthält: Tastenanschläge, Mausbewegungen, Erstellen oder Löschen von grafischen Objekten.
Deshalb habe ich mich entschieden, ein Programm als Include-Dateien zu erstellen. Alle Funktionen und Variablen sind auf mehrere Klassen verteilt, um den Zugriff auf sie zu erleichtern. An dieser Stelle benötige ich nur Klassen zur bequemen Gruppierung von Funktionen. Deshalb werden wir in dieser ersten Implementierung keine komplexen Dinge wie Vererbung oder Fabriken verwenden. Es ist nur eine Kollektion.
Außerdem möchte ich eine plattformübergreifende Klasse erstellen, die sowohl in MQL4 als auch in MQL5 laufen kann.
Struktur des Programms
Die Bibliothek enthält fünf miteinander in Beziehung stehende Dateien. Alle Dateien befinden sich im Ordner "Shortcuts" im Verzeichnis "Include". Ihre Namen sind in der Abbildung dargestellt: GlobalVariables.mqh, Graphics.mqh, Mouse.mqh, Shortcuts.mqh, Utilites.mqh.
Die Hauptbibliotheksdatei (Shortcuts.mqh)
Die Hauptdatei des Programms ist "Shortcuts.mqh". Die Logik der Reaktion auf Tastendrücke ist in dieser Datei. Dies ist die Datei, die mit einem Expert Advisor verbunden werden sollte. Alle Hilfsdateien befinden sich ebenfalls in dieser Datei.
//+------------------------------------------------------------------+ //| Shortcuts.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://www.mql5.com/ru/articles/7468 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://www.mql5.com/en/articles/7468" #include "GlobalVariables.mqh" #include "Mouse.mqh" #include "Utilites.mqh" #include "Graphics.mqh" //+------------------------------------------------------------------+ //| The main control class of the program. It should be connected | //| to the Expert Advisor | //+------------------------------------------------------------------+ class CShortcuts { private: CGraphics m_graphics; // Object for drawing m_graphics public: CShortcuts(); void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); }; //+------------------------------------------------------------------+ //| Default constructor | //+------------------------------------------------------------------+ CShortcuts::CShortcuts(void) { ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true); } //+------------------------------------------------------------------+ //| Event handling function | //+------------------------------------------------------------------+ void CShortcuts::OnChartEvent( const int id, const long &lparam, const double &dparam, const string &sparam ) { //--- // This will contain the description of the events related to keystrokes // and mouse movements // ... } } CShortcuts shortcuts;
Die Datei enthält die Klassen CShortcuts.
Alle Hilfsklassen sind am Anfang der Datei eingebunden.
Die Klasse hat nur zwei Methoden. Die erste ist die Ereignisbehandlung OnChartEvent, in der alle Tastendruck- und Mausbewegungsereignisse abgearbeitet werden. Die zweite ist der Standardkonstruktor, in der Mausbewegungen behandelt werden können.
Nach der Klassenbeschreibung wird eine Variable für die Schnelltastenerstellt, die in der Methode OnChartEvent des Expert Advisors verwendet werden sollte, wenn die Bibliothek verwendet und eingebunden wird.
Die Einbindung erfordert zwei Zeilen:
//+------------------------------------------------------------------+ //| The main Expert (file "Shortcuts-Main-Expert.mq5") | //+------------------------------------------------------------------+ #include <Shortcuts\Shortcuts.mqh> // ... //+------------------------------------------------------------------+ //| The ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- shortcuts.OnChartEvent(id,lparam,dparam,sparam); }
Die erste Zeile verbindet die Klassendatei. Die zweite Zeile überträgt die Ereignisbehandlungskontrolle an die Klasse.
Danach ist der Expert Advisor einsatzbereit — er kann kompiliert und zum Zeichnen von Linien verwendet werden.
Klasse zur Behandlung von Mausbewegungen
Die Klasse speichert alle Grundparameter der aktuellen Cursorposition: Koordinaten X, Y in Pixeln und in Preis/Zeit, Taktnummer, auf der der Zeiger positioniert ist, und so eins - all das wird in der Datei "Mouse.mqh" gespeichert.
//+------------------------------------------------------------------+ //| Mouse.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://www.mql5.com/ru/articles/7468 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://www.mql5.com/en/articles/7468" //+------------------------------------------------------------------+ //| Mouse movement handling class | //+------------------------------------------------------------------+ class CMouse { //--- Members private: static int m_x; // X static int m_y; // Y static int m_barNumber; // Bar number static bool m_below; // Indication of a cursor above the price static bool m_above; // Indication of a cursor below the price static datetime m_currentTime; // Current time static double m_currentPrice;// Current price //--- Methods public: //--- Remembers the main parameters of the mouse cursor static void SetCurrentParameters( const int id, const long &lparam, const double &dparam, const string &sparam ); //--- Returns the X coordinate (in pixels) of the current cursor position static int X(void) {return m_x;} //--- Returns the Y coordinate (in pixels) of the current cursor position static int Y(void) {return m_y;} //--- Returns the price of the current cursor position static double Price(void) {return m_currentPrice;} //--- Returns the time of the current cursor position static datetime Time(void) {return m_currentTime;} //--- Returns the bar number of the current cursor position static int Bar(void) {return m_barNumber;} //--- Returns a flag showing that the price is below the Low of the current bar static bool Below(void) {return m_below;} //--- Returns a flag showing that the price is above the High of the current bar static bool Above(void) {return m_above;} }; //--- int CMouse::m_x=0; int CMouse::m_y=0; int CMouse::m_barNumber=0; bool CMouse::m_below=false; bool CMouse::m_above=false; datetime CMouse::m_currentTime=0; double CMouse::m_currentPrice=0; //+------------------------------------------------------------------+ //| Remembers the main parameters for a mouse movement: coordinates | //| of cursor in pixels and price/time, whether the cursor is | //| above or below the price. | //|+-----------------------------------------------------------------+ static void CMouse::SetCurrentParameters( const int id, const long &lparam, const double &dparam, const string &sparam ) { //--- int window = 0; //--- ChartXYToTimePrice( 0, (int)lparam, (int)dparam, window, m_currentTime, m_currentPrice ); m_x=(int)lparam; m_y=(int)dparam; m_barNumber=iBarShift( Symbol(), PERIOD_CURRENT, m_currentTime ); m_below=m_currentPrice<iLow(Symbol(),PERIOD_CURRENT,m_barNumber); m_above=m_currentPrice>iHigh(Symbol(),PERIOD_CURRENT,m_barNumber); } //+------------------------------------------------------------------+
Diese Klasse kann von jeder Stelle innerhalb eines Programms verwendet werden, da ihre Methoden als statisch deklariert sind. Es ist nicht notwendig, eine Instanz dieser Klasse zu erzeugen, um sie zu verwenden.
Beschreibung des Einstellungsblocks Expert Advisor. Die Datei GlobalVariables.mqh
Alle Einstellungen, die dem Nutzer zur Verfügung stehen werden in der Datei GlobalVariables.mqh gespeichert.
Der nächste Artikel wird Beschreibungen von viel mehr Einstellungen enthalten, da wir weitere Objekte zum Zeichnen hinzufügen werden.
Unten finden Sie den Code der Einstellungen, die in der aktuellen Version verwendet werden:
//+------------------------------------------------------------------+ //| GlobalVariables.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://www.mql5.com/ru/articles/7468 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://www.mql5.com/en/articles/7468" //+------------------------------------------------------------------+ //| File describing parameters available to the user | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| 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 //+------------------------------------------------------------------+ //| Size settings | //+------------------------------------------------------------------+ input string Dimensions="=== Size settings ==="; input int Trend_Line_Width=2; // Trend line width //+------------------------------------------------------------------+ //| Display styles | //+------------------------------------------------------------------+ input string Styles="=== Display styles ==="; input ENUM_LINE_STYLE Trend_Line_Style=STYLE_SOLID; // Trend line style //+------------------------------------------------------------------+ //| Other parameters | //+------------------------------------------------------------------+ input string Others="=== Other parameters ==="; 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 //+------------------------------------------------------------------+ //| Name prefixes of drawn shapes (can be change only in code, | //| not visible in EA parameters) | //+------------------------------------------------------------------+ // string Prefixes="=== Prefixes ==="; string Trend_Line_Prefix="Trend_"; // Trend line prefix //+------------------------------------------------------------------+ //| Colors for objects of one timeframe (can be changed only in code,| //| not visible in EA parameters) | //+------------------------------------------------------------------+ // string TimeframeColors="=== Time frame colors ==="; 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 #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 //+------------------------------------------------------------------+
Zusatzfunktionen
Das Programm hat viele Funktionen, die nicht direkt mit dem Chartbild zusammenhängen, aber sie helfen dabei, Extrempunkte zu finden, Zeitrahmen zu wechseln und so weiter. All diese Funktionen sind in der Datei "Utilites.mqh" implementiert.
Der Kopf der Datei "Utilites.mqh
//+------------------------------------------------------------------+ //| Utilites.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://www.mql5.com/ru/articles/7468 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://www.mql5.com/en/articles/7468" //+------------------------------------------------------------------+ //| Class describing auxiliary functions | //+------------------------------------------------------------------+ class CUtilites { public: //--- Changes the timeframe to the next one on the toolbar static void ChangeTimeframes(bool isUp); //--- Converts string command constants to keycodes static int GetCurrentOperationChar(string keyString); //--- Switches layers in charts (the chart is on top of all objects) static void ChangeChartZIndex(void); //--- Returns the number of the nearest extreme bar static int GetNearestExtremumBarNumber( int starting_number=0, bool is_search_right=false, bool is_up=false, int left_side_bars=1, int right_side_bars=1, string symbol=NULL, ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT ); //--- Returns the color for the current timeframe static color GetTimeFrameColor(long allDownPeriodsValue); //--- Returns a list of all timeframes lower than the current one (including the current one) static long GetAllLowerTimeframes(int NeededTimeframe=PERIOD_CURRENT); //--- Coordinates of the straight line. Writes numbers of extreme bars to points p1 and p2 static void SetExtremumsBarsNumbers(bool _is_up,int &p1, int &p2); //--- Converts a string to an array of floating point numbers static void StringToDoubleArray( string _haystack, double &_result[], const string _delimiter="," ); //--- Determines the name of the current object static string GetCurrentObjectName( const string _prefix, const ENUM_OBJECT _type=OBJ_TREND, int _number = -1 ); //--- Obtains the number of the next object static int GetNextObjectNumber( const string _prefix, const ENUM_OBJECT _object_type, bool true ); //--- Returns the distance, in screen pixels, between adjacent bars static int GetBarsPixelDistance(void); //--- Converts a numeric value of the timeframe to its string name static string GetTimeframeSymbolName( ENUM_TIMEFRAMES _timeframe=PERIOD_CURRENT // Desired timeframe ); };
Die Funktion ändert sequenziell die Zeitrahmen des Charts
Die ersten Funktionen in dieser Datei sind einfach. Die Funktion, die sequenziell die Zeitrahmen des aktuellen Diagramms ändert, sieht beispielsweise wie folgt aus:
//+------------------------------------------------------------------+ //| Sequentially changes the current chart period | //| | //| In this implementation, only the timeframes shown in the | //| standard panel are used. | //| | //| Parameters: | //| _isUp - timeframe switch direction: up (true) | //| or down (false) | //+------------------------------------------------------------------+ static void CUtilites::ChangeTimeframes(bool _isUp) { ENUM_TIMEFRAMES timeframes[] = { PERIOD_CURRENT, PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1, PERIOD_MN1 }; int period = Period(); int shift = ArrayBsearch(timeframes,period); if(_isUp && shift < ArraySize(timeframes)-1) { ChartSetSymbolPeriod(0,NULL,timeframes[++shift]); } else if(!_isUp && shift > 1) { ChartSetSymbolPeriod(0,NULL,timeframes[--shift]); } }
Zuerst wird in der Funktion ein Array aller in der Standard-Symbolleiste angegebenen Zeitrahmen beschrieben. Wenn Sie zwischen alle im MetaTrader 5 verfügbaren Zeitrahmen wechseln möchten, sollten entsprechende Konstanten in das Array geschrieben werden. In diesem Fall kann jedoch die Kompatibilität verloren gehen und die Bibliothek kann die Arbeit mit MQL4 nicht fortsetzen.
Als Nächstes verwenden wir Standardfunktionen, um den aktuellen Zeitrahmen zu erhalten und um auf der Liste zu finden.
Dann werden Zeitrahmen des Charts mit der Standardfunktion ChartSetSymbolPeriode umgeschaltet, indem die nach dem aktuellen Zeitrahmen auf den folgenden übergegangen wird.
Andere im Code verwendete Funktionen sollten ohne Erklärung klar sein. Dies ist ein einfacher Code.
Einige einfache Funktionen
//+------------------------------------------------------------------+ //| Converts string command constants to keycodes | //+------------------------------------------------------------------+ static int CUtilites::GetCurrentOperationChar(string keyString) { string keyValue = keyString; StringToUpper(keyValue); return(StringGetCharacter(keyValue,0)); } //+------------------------------------------------------------------+ //| Switch chart position to display on top of other objects | //+------------------------------------------------------------------+ static void CUtilites::ChangeChartZIndex(void) { ChartSetInteger( 0, CHART_FOREGROUND, !(bool)ChartGetInteger(0,CHART_FOREGROUND) ); ChartRedraw(0); } //+------------------------------------------------------------------+ //| Returns a string name for any standard timeframe | //| Parameters: | //| _timeframe - ENUM_TIMEFRAMES numeric value for which we need | //| to find a string name | //| Return value: | //| string name of the required timeframe | //+------------------------------------------------------------------+ static string CUtilites::GetTimeframeSymbolName( ENUM_TIMEFRAMES _timeframe=PERIOD_CURRENT // Desired timeframe ) { ENUM_TIMEFRAMES current_timeframe; // current timeframe string result = ""; //--- if(_timeframe == PERIOD_CURRENT) { current_timeframe = Period(); } else { current_timeframe = _timeframe; } //--- switch(current_timeframe) { case PERIOD_M1: return "M1"; case PERIOD_M2: return "M2"; case PERIOD_M3: return "M3"; case PERIOD_M4: return "M4"; case PERIOD_M5: return "M5"; case PERIOD_M6: return "M6"; case PERIOD_M10: return "M10"; case PERIOD_M12: return "M12"; case PERIOD_M15: return "M15"; case PERIOD_M20: return "M20"; case PERIOD_M30: return "M30"; case PERIOD_H1: return "H1"; case PERIOD_H2: return "M1"; case PERIOD_H3: return "H3"; case PERIOD_H4: return "H4"; case PERIOD_H6: return "H6"; case PERIOD_H8: return "H8"; case PERIOD_D1: return "D1"; case PERIOD_W1: return "W1"; case PERIOD_MN1: return "MN1"; default: return "Unknown"; } } //+------------------------------------------------------------------+ //| Returns the standard color for the current timeframe | //+------------------------------------------------------------------+ static color CUtilites::GetTimeFrameColor(long _all_down_periods_value) { if(Is_Different_Colors) { switch((int)_all_down_periods_value) { case OBJ_PERIOD_M1: return (m1_color); case PERIOD_LOWER_M5: return (m5_color); case PERIOD_LOWER_M15: return (m15_color); case PERIOD_LOWER_M30: return (m30_color); case PERIOD_LOWER_H1: return (h1_color); case PERIOD_LOWER_H4: return (h4_color); case PERIOD_LOWER_D1: return (d1_color); case PERIOD_LOWER_W1: return (w1_color); case OBJ_ALL_PERIODS: return (mn1_color); default: return (common_color); } } else { return (common_color); } }
Eine Suchfunktion für Extrema und ihre Anwendung
Die nächste Funktion hilft, Extrema zu finden. Abhängig von den Parametern können die Extrema entweder rechts oder links vom Mauszeiger liegen. Außerdem können Sie die gewünschte Anzahl von Balken links und rechts vom Extremum angeben.
//+------------------------------------------------------------------+ //| Returns the number of the nearest fractal in the selected | //| direction | //| Parameters: | //| starting_number - bar number at which the search starts | //| is_search_right - search to the right (true) or left (false) | //| is_up - if "true", search by High levels, otherwise - Low | //| left_side_bars - number of bars on the left | //| right_side_bars - number of bars on the right | //| symbol - symbol name for search | //| timeframe - period for search | //| Return value: | //| the number of the extremum closest to starting_number, | //| matching the specified parameters | | //+------------------------------------------------------------------+ static int CUtilites::GetNearestExtremumBarNumber( int starting_number=0, // Initial bar number const bool is_search_right=false, // Direction to the right const bool is_up=false, // Search by Highs const int left_side_bars=1, // Number of bars to the left const int right_side_bars=1, // Number of bars to the right const string symbol=NULL, // The required symbol const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT // The required timeframe ) { //--- int i, nextExtremum, sign = is_search_right ? -1 : 1; //--- If the starting bar is specified incorrectly //--- (is beyond the current chart borders) //--- and search - towards the border... if((starting_number-right_side_bars<0 && is_search_right) || (starting_number+left_side_bars>iBars(symbol,timeframe) && !is_search_right) ) { //--- ...it is necessary to display an error message if(Print_Warning_Messages) { Print(DEBUG_MESSAGE_PREFIX, "Can't find extremum: ", "wrong direction"); Print("left_side_bars = ",left_side_bars,"; ", "right_side_bars = ",right_side_bars); } return (-2); } else { //--- otherwise - the direction allows you to select the correct bar. //--- check all bars in the required direction, //--- as long as we are beyond the known chart borders while((starting_number-right_side_bars<0 && !is_search_right) || (starting_number+left_side_bars>iBars(symbol,timeframe) && is_search_right) ) { starting_number +=sign; } } //--- i=starting_number; //--- Preparation is complete. Proceed to search while(i-right_side_bars>=0 && i+left_side_bars<iBars(symbol,timeframe) ) { //--- Depending on the level, check the required extremum if(is_up) { //--- either the upper one nextExtremum = iHighest( Symbol(), Period(), MODE_HIGH, left_side_bars+right_side_bars+1, i-right_side_bars ); } else { //--- or the lower one nextExtremum = iLowest( Symbol(), Period(), MODE_LOW, left_side_bars+right_side_bars+1, i-right_side_bars ); } if(nextExtremum == i) // If the current bar is an extremum, { return nextExtremum; // the problem is solved } else // Otherwise - continue to shift the counter of the checked candlestick to the desired direction if(is_search_right) { if(nextExtremum<i) { i=nextExtremum; } else { i--; } } else { if(nextExtremum>i) { i=nextExtremum; } else { i++; } } } //--- If the edge is reached but no extremum has been found, if(Print_Warning_Messages) { //--- show an error message. Print(DEBUG_MESSAGE_PREFIX, "Can't find extremum: ", "an incorrect starting point or wrong border conditions."); Print("left_side_bars = ",left_side_bars,"; ", "right_side_bars = ",right_side_bars); } return (-1); }
Um Trendlinien zu zeichnen, benötigen wir eine Funktion, die die beiden nächstgelegenen Extrema auf der rechten Seite der Maus findet. Diese Funktion kann die vorherige verwenden:
//+------------------------------------------------------------------+ //| Finds 2 nearest extreme points to the right of the current | //| mouse pointer position | //| Parameters: | //| _is_up - search by High (true) or Low (false) | //| int &_p1 - bar number of the first point | //| int &_p2 - bar number of the second point | //+------------------------------------------------------------------+ static void CUtilites::SetExtremumsBarsNumbers( bool _is_up, // search by High (true) or by Low (false) int &_p1, // bar number of the first point int &_p2 // bar number of second point ) { int dropped_bar_number=CMouse::Bar(); //--- _p1=CUtilites::GetNearestExtremumBarNumber( dropped_bar_number, true, _is_up, Fractal_Size_Left, Fractal_Size_Right ); _p2=CUtilites::GetNearestExtremumBarNumber( _p1-1, // Bar to the left of the previous found extremum true, _is_up, Fractal_Size_Left, Fractal_Size_Right ); if(_p2<0) { _p2=0; } }
Erzeugen von Objektnamen
Um Serien der gleichen Objekte zeichnen zu können, müssen die Objekte eindeutige Namen haben. Am effizientesten ist es, ein Präfix zu verwenden, das diesem Objekttyp entspricht, und ihm eine eindeutige Nummer hinzuzufügen. Für verschiedene Objekttypen sind in GlobalVariables.mqh Präfixe aufgeführt.
Die Nummern werden von der entsprechenden Funktion generiert.
//+------------------------------------------------------------------+ //| Returns the number of the next object in the series | //| Parameters: | //| prefix - name prefix for this group of objects. | //| object_type - the type of objects to search in. | //| only_prefixed - if "false", search in all objects | //| of this type, "true" - only the objects | //| with the specified prefix | //| Return value: | //| number of the next object in series. If search by prefix, | //| and the existing numbering has a gap, the next number | //| will be at gap beginning. | //+------------------------------------------------------------------+ int CUtilites::GetNextObjectNumber( const string prefix, const ENUM_OBJECT object_type, bool true ) { int count = ObjectsTotal(0,0,object_type), i, current_element_number, total_elements = 0; string current_element_name = "", comment_text = ""; //--- if(only_prefixed) { for(i=0; i<count; i++) { current_element_name=ObjectName(0,i,0,object_type); if(StringSubstr(current_element_name,0,StringLen(prefix))==prefix) { current_element_number= (int)StringToInteger( StringSubstr(current_element_name, StringLen(prefix), -1) ); if(current_element_number!=total_elements) { break; } total_elements++; } } } else { total_elements = ObjectsTotal(0,-1,object_type); do { current_element_name = GetCurrentObjectName( prefix, object_type, total_elements ); if(ObjectFind(0,current_element_name)>=0) { total_elements++; } } while(ObjectFind(0,current_element_name)>=0); } //--- return(total_elements); }
Im Code sind zwei Suchalgorithmen implementiert. Der erste Algorithmus (der Hauptalgorithmus für diese Bibliothek) prüft alle Objekte, die dem Typ entsprechen und das angegebene Präfix haben. Sobald er eine freie Nummer findet, gibt der Algorithmus diese an den Nutzer zurück. Dies erlaubt es, "Lücken" in der Nummerierung zu füllen.
Dieser Algorithmus eignet sich jedoch nicht für den Fall, dass es mehrere Objekte mit der gleichen Nummer, aber mit unterschiedlichen Suffixen geben kann. In früheren Versionen, als ich Skripte zum Zeichnen von Objekten verwendete, habe ich solche Benennungen für Einstellungen von Pitchforks (den Gabeln) verwendet.
Daher hat die Bibliothek die zweite Suchmethode. Der Algorithmus nimmt die Gesamtzahl der Objekte dieses Typs und prüft, ob es einen Namen gibt, der mit dem gleichen Präfix beginnt und den gleichen Index hat. Wenn ja, wird die Anzahl um 1 erhöht, bis ein freier Wert gefunden wird.
Und wenn es eine Zahl gibt (oder wenn sie leicht mit einer Funktion ermittelt werden kann), kann ein Name leicht erstellt werden.
//+------------------------------------------------------------------+ //| Generates the current element name | //| Parameters: | //| _prefix - name prefix for this group of objects. | //| _type - the type of objects to search in. | //| _number - the number of the current object if it is ready. | //| Return value: | //| current object name string. | //+------------------------------------------------------------------+ string CUtilites::GetCurrentObjectName( string _prefix, ENUM_OBJECT _type=OBJ_TREND, int _number = -1 ) { int Current_Line_Number; //--- Addition to the prefix - current timeframe string Current_Line_Name=IntegerToString(PeriodSeconds()/60)+"_"+_prefix; //--- Get the element number if(_number<0) { Current_Line_Number = GetNextObjectNumber(Current_Line_Name,_type); } else { Current_Line_Number = _number; } //--- Generate the name Current_Line_Name +=IntegerToString(Current_Line_Number,4,StringGetCharacter("0",0)); //--- return (Current_Line_Name); }
Der Abstand zwischen benachbarten Balken (in Pixeln)
Manchmal ist es notwendig, die Entfernung zu einem bestimmten Punkt in der Zukunft zu berechnen. Eine der zuverlässigsten Methoden ist es, den Abstand in Pixeln zwischen zwei benachbarten Balken zu berechnen und ihn dann mit dem erforderlichen Koeffizienten (die gewünschte Anzahl von Balken für den Einzug) zu multiplizieren.
Der Abstand zwischen benachbarten Balken kann mit der folgenden Funktion berechnet werden:
//+------------------------------------------------------------------+ //| Calculates a distance in pixels between two adjacent bars | //+------------------------------------------------------------------+ int CUtilites::GetBarsPixelDistance(void) { double price; // arbitrary price on the chart (used for // standard functions calculating coordinates datetime time1,time2; // the time of the current and adjacent candlesticks int x1,x2,y1,y2; // screen coordinates of two points // on adjacent candlesticks int deltha; // result - the desired distance //--- Initial settings price = iHigh(Symbol(),PERIOD_CURRENT,CMouse::Bar()); time1 = CMouse::Time(); //--- Get the time of the current candlestick if(CMouse::Bar()<Bars(Symbol(),PERIOD_CURRENT)){ // if at the middle of the chart, time2 = time1+PeriodSeconds(); // take the candlestick on the left } else { // otherwise time2 = time1; time1 = time1-PeriodSeconds(); // take the candlestick on the right } //--- Convert coordinates form price/time values to screen pixels ChartTimePriceToXY(0,0,time1,price,x1,y1); ChartTimePriceToXY(0,0,time2,price,x2,y2); //--- Calculate the distance deltha = MathAbs(x2-x1); //--- return (deltha); }
Eine Funktion zur Umwandlung einer Zeichenkette in ein Array von reellen Zahlen
Der bequemste Weg, Fibonacci-Ebenen mit EA-Parametern einzurichten, ist die Verwendung von Zeichenketten, die aus durch Kommata getrennten Werten bestehen. Bei MQL muss jedoch die reelle Zahl für die Einstellung der Ebene verwendet werden.
Die folgende Funktion kann beim Extrahieren von Zahlen aus einer Zeichenfolge helfen.
//+------------------------------------------------------------------+ //| Converts a string with separators to an array of double | //| numbers | //| Parameters: | //| _haystack - source string for conversion | //| _result[] - resulting array | //| _delimiter - separator character | //+------------------------------------------------------------------+ static void CUtilites::StringToDoubleArray( string _haystack, // source string double &_result[], // array of results const string _delimiter="," // separator ) { //--- string haystack_pieces[]; // Array of string fragments int pieces_count, // Number of fragments i; // Counter string current_number=""; // The current string fragment (estimated number) //--- Split the string into fragments pieces_count=StringSplit(_haystack,StringGetCharacter(_delimiter,0),haystack_pieces); //--- Convert if(pieces_count>0) { ArrayResize(_result,pieces_count); for(i=0; i<pieces_count; i++) { StringTrimLeft(haystack_pieces[i]); StringTrimRight(haystack_pieces[i]); _result[i]=StringToDouble(haystack_pieces[i]); } } else { ArrayResize(_result,1); _result[0]=0; } }
Zeichenklasse: Beispiel für die Verwendung von Hilfsfunktionen
Der Artikel ist schon etwas lang geworden, deshalb werden die meisten Zeichenfunktionen im nächsten Artikel beschrieben. Um jedoch einige der erstellten Funktionen zu testen, werde ich hier den Code hinzufügen, der einfache gerade Linien zeichnet (basierend auf zwei nächstliegenden Extrema).
Der Kopf der Datei Graphics.mqh
//+------------------------------------------------------------------+ //| Graphics.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://www.mql5.com/ru/articles/7468 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://www.mql5.com/en/articles/7468" //+------------------------------------------------------------------+ //| Class for plotting graphic objects | //+------------------------------------------------------------------+ class CGraphics { //--- Fields private: bool m_Is_Trend_Ray; bool m_Is_Change_Timeframe_On_Create; //--- Methods private: //--- Sets general parameters for any newly created object void CurrentObjectDecorate( const string _name, const color _color=clrNONE, const int _width = 1, const ENUM_LINE_STYLE _style = STYLE_SOLID ); public: //--- Default constructor CGraphics(); //--- Universal function for creating trend lines with specified parameters bool TrendCreate( const long chart_ID=0, // chart ID const string name="TrendLine", // line name const int sub_window=0, // subwindow number datetime time1=0, // time of the first point double price1=0, // price of the first point datetime time2=0, // time of the second point double price2=0, // price of the second point const color clr=clrRed, // line color const ENUM_LINE_STYLE style=STYLE_SOLID, // line style const int width=1, // line width const bool back=false, // in the background const bool selection=true, // if the line is selected const bool ray_right=false, // ray to the right const bool hidden=true, // hide in the list of objects const long z_order=0 // Z-Index ); //--- Plots a trend line based on the two nearest (to the right of the mouse) extreme points void DrawTrendLine(void); //--- Checks if the straight line is a ray bool IsRay() {return m_Is_Trend_Ray;} //--- Sets the ray indication (whether the line should be extended to the right) void IsRay(bool _is_ray) {m_Is_Trend_Ray = _is_ray;} //--- Checks if the display of objects created by the program should be changed on higher timeframes bool IsChangeTimeframe(void) {return m_Is_Change_Timeframe_On_Create;} //--- Sets flags for the display of objects created by the program on higher timeframes void IsChangeTimeframe(bool _is_tf_change) {m_Is_Change_Timeframe_On_Create = _is_tf_change;} };
Funktion zum Einstellen allgemeiner Parameter für jedes neu erstellte Objekt
//+------------------------------------------------------------------+ //| Sets general parameters for all new objects | //| Parameters: | //| _name - the name of the object being modified | //| _color - the color of the object being modified. If not set, | //| standard color of the current period is used | //| _width - object line width | //| _style - object line type | //+------------------------------------------------------------------+ void CGraphics::CurrentObjectDecorate( const string _name, // the name of the object being modified const color _color=clrNONE, // the color of the object being modified const int _width = 1, // object line width const ENUM_LINE_STYLE _style = STYLE_SOLID // object line style ) { long timeframes; // timeframes on which the object will be displayed color currentColor; // object color //--- Specify timeframes on which the object will be displayed if(Is_Change_Timeframe_On_Create) { timeframes = CUtilites::GetAllLowerTimeframes(); } else { timeframes = OBJ_ALL_PERIODS; } //--- Specify the object color if(_color != clrNONE) { currentColor = _color; } else { currentColor = CUtilites::GetTimeFrameColor(timeframes); } //--- Set attributes ObjectSetInteger(0,_name,OBJPROP_COLOR,currentColor); // Color ObjectSetInteger(0,_name,OBJPROP_TIMEFRAMES,timeframes); // Periods ObjectSetInteger(0,_name,OBJPROP_HIDDEN,true); // Hide in the list of objects ObjectSetInteger(0,_name,OBJPROP_SELECTABLE,true); // Ability to select ObjectSetInteger(0,_name,OBJPROP_SELECTED,Is_Select_On_Create); // Stay selected after creation? ObjectSetInteger(0,_name,OBJPROP_WIDTH,_width); // Line width ObjectSetInteger(0,_name,OBJPROP_STYLE,_style); // Line style }
Die Zeichenfunktion für eine gerade Linie
//+------------------------------------------------------------------+ //| Universal function for creating trend lines with specified | //| parameters | //| Parameters: | //| chart_ID - chart ID | //| name - line name | //| sub_window - subwindow number | //| time1 - time of the first point | //| price1 - price of the first point | //| time2 - time of the second point | //| price2 - price of the second point | //| clr - line color | //| style - line style | //| width - line width | //| back - in the background | //| selection - if the line is selected | //| ray_right - ray to the right | //| hidden - hide in the list of objects | //| z_order - priority of mouse clicks (Z-Index) | //| Return value: | //| indication of success. If line drawing failed, | //| false is returned, otherwise - true | //+------------------------------------------------------------------+ bool CGraphics::TrendCreate( const long chart_ID=0, // chart ID const string name="TrendLine", // line name const int sub_window=0, // subwindow number datetime time1=0, // time of the first point double price1=0, // price of the first point datetime time2=0, // time of the second point double price2=0, // price of the second point const color clr=clrRed, // line color const ENUM_LINE_STYLE style=STYLE_SOLID, // line style const int width=1, // line width const bool back=false, // in the background const bool selection=true, // if the line is selected const bool ray_right=false, // ray to the right const bool hidden=true, // hide in the list of objects const long z_order=0 // Z-Index ) { //--- Reset the last error message ResetLastError(); //--- Create a line if(!ObjectCreate(chart_ID,name,OBJ_TREND,sub_window,time1,price1,time2,price2)) { if(Print_Warning_Messages) // if failed, show an error message { Print(__FUNCTION__, ": Can't create trend line! Error code = ",GetLastError()); } return(false); } //--- Set additional attributes CurrentObjectDecorate(name,clr,width,style); //--- line in the foreground (false) or in the background (true) ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); //--- ray to the right (true) or exact borders (false) ObjectSetInteger(chart_ID,name,OBJPROP_RAY_RIGHT,ray_right); //--- mouse click priority (Z-index) ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); //--- Update the chart image ChartRedraw(0); //--- Everything is good. The line has been drawn successfully. return(true); }
Nehmen wir diese allgemeine Funktion als Grundlage, um eine weitere Funktion erstellen, die eine gerade Linie durch zwei benachbarte Extrempunkte zieht.
//+------------------------------------------------------------------+ //| Draws a trend line by nearest extreme points in the right | //+------------------------------------------------------------------+ 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(Trend_Line_Prefix,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; int x1,x2,y1,y2; // Point coordinates int window=0; // Subwindow number //--- 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 coordinates time1=iTime(Symbol(),PERIOD_CURRENT,p1); price1=iLow(Symbol(),PERIOD_CURRENT,p1); time2=iTime(Symbol(),PERIOD_CURRENT,p2); 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 coordinates time1=iTime(Symbol(),PERIOD_CURRENT,p1); price1=iHigh(Symbol(),PERIOD_CURRENT,p1); time2=iTime(Symbol(),PERIOD_CURRENT,p2); price2=iHigh(Symbol(),PERIOD_CURRENT,p2); } //--- 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 ); //--- Redraw the chart ChartRedraw(0); }
Bitte beachten Sie den Funktionsaufruf CUtilites::SetExtremumsBarsNumbers, der für die Punkte 1 und 2 die Balkennummern erhält. Ihr Code wurde bereits beschrieben. Der Rest scheint klar zu sein, so dass es nicht notwendig ist, eine lange Erklärung hinzuzufügen.
Die letzte Funktion zeichnet eine einfache Linie, die auf zwei Punkten basiert. Abhängig vom globalen Parameter Is_Trend_Ray (beschrieben in der Datei GlobalVariables.mqh) ist die Linie entweder ein nach rechts verlängerter Strahl oder ein kurzes Segment zwischen zwei Extrempunkten.
Fügen wir die Möglichkeit hinzu, die Zeile mit Hilfe einer Tastatur zu erweitern.
Erstellen eines Steuerblocks: Einstellung der Methode OnChartEvent
Nun, da die Grundfunktionen fertig sind, können wir die Tastaturkürzel anpassen.
Schreiben wir in Shortcuts.mqh die Methode CShortcuts::OnChartEvent.
//+------------------------------------------------------------------+ //| Event handling function | //+------------------------------------------------------------------+ void CShortcuts::OnChartEvent( const int id, const long &lparam, const double &dparam, const string &sparam ) { //--- switch(id) { //--- Save the coordinates of the mouse cursor case CHARTEVENT_MOUSE_MOVE: CMouse::SetCurrentParameters(id,lparam,dparam,sparam); break; //--- Handle keystrokes case CHARTEVENT_KEYDOWN: //--- Change the timeframe 1 level up if(CUtilites::GetCurrentOperationChar(Up_Key) == lparam) { CUtilites::ChangeTimeframes(true); }; //--- Change the timeframe 1 level down if(CUtilites::GetCurrentOperationChar(Down_Key) == lparam) { CUtilites::ChangeTimeframes(false); }; //--- Change the Z Index of the chart (chart on top of all objects) if(CUtilites::GetCurrentOperationChar(Z_Index_Key) == lparam) { CUtilites::ChangeChartZIndex(); } //--- Draw a trend line if(CUtilites::GetCurrentOperationChar(Trend_Line_Key) == lparam) { m_graphics.DrawTrendLine(); } //--- Switch the Is_Trend_Ray parameter if(CUtilites::GetCurrentOperationChar(Switch_Trend_Ray_Key) == lparam) { m_graphics.IsRay(!m_graphics.IsRay()); } break; //--- } }
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) |
Schlussfolgerung
Die angehängte Datei enthält die aktuelle Version der Bibliothek. Außerdem enthält der Anhang drei Skripte.
- Das erste ist Del-All-Graphics. Es löscht alle grafischen Objekte aus dem aktuellen Fenster. In meinem Terminal setze ich für dieses Skript das Tastaturkürzel Strg+A (Alle).
- Das zweite Skript ist Del-All-Prefixed. Es löscht alle Objekte mit einem Präfix (z.B. alle Trendlinien oder Objekte, die mit H1 beginnen). Ich rufe es mit Alt+R (Remove bzw. Entfernen) auf.
- Und schließlich erlaubt das dritte Skript (DeselectAllObjects) die Abwahl aller Objekte im aktuellen Fenster. Mein Tastaturkürzel lautet Strg+D (Deselect, wie in Photoshop).
Es ist besser, die Bibliothek mit einem Expert Advisor und nicht mit einem Indikator zu verbinden, denn wenn Sie eine Verbindung zu einem Indikator herstellen und versuchen, diesen Indikator zusammen mit einem anderen Expert Advisor zu verwenden, kann dies zu starken Verlangsamungen führen. Zumindest war dies so bei mir. Natürlich könnte es auch andere Fehler gegeben haben.
Was wird weiter realisiert werden.
Die zweite Version der Bibliothek wird die Implementierung der im Video gezeigten nützlichen Objekte beschreiben. Einige Objekte sind primitiv (wie vertikale oder horizontale Linien), andere Objekte, wie z.B. Linien einer bestimmten Länge, erforderten mehr Aufwand. Einige von ihnen funktionieren wegen des "Ausgabefehlers" oder aus einem anderen Grund noch immer nicht immer richtig. Ich werde meine Entscheidungen beschreiben, und natürlich ist Ihr Feedback willkommen.
Die dritte Version wird eine grafische Schnittstelle zur Konfiguration der Parameter enthalten.
Die vierte Version wird, falls sie jemals erscheinen sollte, einen vollwertigen Hilfs-EA darstellen, der den manuellen Handel erleichtern wird. Ich brauche Ratschläge von der Gemeinschaft. Ich bin mir nicht sicher, ob ich neue Ideen im Vergleich zu bestehenden Lösungen anwenden werde. Natürlich werde ich die Schnittstelle selbst zeichnen. All dies gilt jedoch für den manuellen Handel. Glauben Sie also, dass diese Entwicklung nützlich sein wird?
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7468
- 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.