Ein manuelles Chart- und Handelswerkzeug (Teil II). Werkzeuge zum Zeichnen von Chart-Grafiken

26 Januar 2021, 10:53
Oleh Fedorov
0
342

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:

Grafikprimitive: vertikale und horizontale Linien

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

Grafische Primitive: Gerade Linie auf M30

Grafische Primitive: Demonstration von Zeitrahmen

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

Fibonacci-Fächer (VFun)

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


Fibonacci-Fächer (Zukunft)

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.

"Reguläre" Pitchfork von Andrews

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.

Schiff-Pitchfork

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.

"Umgekehrte" Pitchfork

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.

Ein Satz von Andrews' Pitchfork

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.

Freitag, zwei automatisch gezeichnete Linien

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:

Begrenzte Trendlinien

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 Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/7908

Beigefügte Dateien |
Websockets für MetaTrader 5 Websockets für MetaTrader 5

Vor der Einführung der Netzwerkfunktionen, die mit der aktualisierten MQL5-API zur Verfügung gestellt wurde, waren MetaTrader-Programme in ihrer Fähigkeit beschränkt, sich mit Websocket-basierten Diensten zu verbinden und eine Schnittstelle zu bilden. Aber natürlich hat sich das alles geändert. In diesem Artikel werden wir die Implementierung einer Websocket-Bibliothek in reinem MQL5 untersuchen. Eine kurze Beschreibung des Websocket-Protokolls wird zusammen mit einer Schritt-für-Schritt-Anleitung für die Verwendung der resultierenden Bibliothek gegeben.

Wie kann man $1.000.000 durch algorithmischen Handel verdienen? Nutzen Sie die Dienste von MQL5.com! Wie kann man $1.000.000 durch algorithmischen Handel verdienen? Nutzen Sie die Dienste von MQL5.com!

Alle Händler gehen auf den Markt mit dem Ziel, ihre erste Million Dollar zu verdienen. Wie kann man das ohne übermäßiges Risiko und großem Startkapital erreichen? Die Dienstleistungen von MQL5.com bieten diese Möglichkeit für Entwickler und Händler aus der ganzen Welt.

Brute-Force-Ansatz zur Mustersuche (Teil II): Immersion Brute-Force-Ansatz zur Mustersuche (Teil II): Immersion

In diesem Artikel werden wir die Diskussion über den Brute-Force-Ansatz fortsetzen. Ich werde versuchen, das Muster anhand der neuen, verbesserten Version meiner Anwendung besser zu erklären. Ich werde auch versuchen, den Unterschied in der Stabilität mit verschiedenen Zeitintervallen und Zeitrahmen zu finden.

Zeitreihen in der Bibliothek DoEasy (Teil 58): Zeitreihen der Datenpuffer von Indikatoren Zeitreihen in der Bibliothek DoEasy (Teil 58): Zeitreihen der Datenpuffer von Indikatoren

Zum Abschluss des Themas Arbeit mit Zeitreihen organisieren wir das Speichern, Suchen und Sortieren von Daten, die in Indikatorpuffern gespeichert sind, was die weitere Durchführung der Analyse auf der Grundlage von Werten der Indikatoren ermöglicht, die auf der Basis der Bibliothek in Programmen zu erstellen sind. Das allgemeine Konzept aller Kollektionsklassen der Bibliothek ermöglicht es, die benötigten Daten in der entsprechenden Kollektion leicht zu finden. Dementsprechend wird das Gleiche in der heute erstellten Klasse möglich sein.